On Wed, Oct 11, 2017 at 05:20:12PM +0200, Arnd Bergmann wrote:
> 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 <a...@arndb.de>

Hi Arnd,
Keith posted something very similar with:
<20171011004514.9500-2-kei...@keithp.com> "drm: Widen vblank UAPI to 64 bits.
Change vblank time to ktime_t"

It looks like perhaps Keith missed one of the comment tweaks that you have
below.

Keith, perhaps you can rebase your widening patch on this one?

Sean


> ---
>  drivers/gpu/drm/drm_vblank.c | 123 
> +++++++++++++++++++++++--------------------
>  include/drm/drm_drv.h        |   2 +-
>  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 @@
>  
>  static bool
>  drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
> -                       struct timeval *tvblank, bool in_vblank_irq);
> +                       ktime_t *tvblank, bool in_vblank_irq);
>  
>  static unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
>  
> @@ -99,7 +99,7 @@ MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic 
> timestamps");
>  
>  static void store_vblank(struct drm_device *dev, unsigned int pipe,
>                        u32 vblank_count_inc,
> -                      struct timeval *t_vblank, u32 last)
> +                      ktime_t t_vblank, u32 last)
>  {
>       struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
>  
> @@ -108,7 +108,7 @@ static void store_vblank(struct drm_device *dev, unsigned 
> int pipe,
>       vblank->last = last;
>  
>       write_seqlock(&vblank->seqlock);
> -     vblank->time = *t_vblank;
> +     vblank->time = t_vblank;
>       vblank->count += vblank_count_inc;
>       write_sequnlock(&vblank->seqlock);
>  }
> @@ -151,7 +151,7 @@ static void drm_reset_vblank_timestamp(struct drm_device 
> *dev, unsigned int pipe
>  {
>       u32 cur_vblank;
>       bool rc;
> -     struct timeval t_vblank;
> +     ktime_t t_vblank;
>       int count = DRM_TIMESTAMP_MAXRETRIES;
>  
>       spin_lock(&dev->vblank_time_lock);
> @@ -171,13 +171,13 @@ static void drm_reset_vblank_timestamp(struct 
> drm_device *dev, unsigned int pipe
>        * interrupt and assign 0 for now, to mark the vblanktimestamp as 
> invalid.
>        */
>       if (!rc)
> -             t_vblank = (struct timeval) {0, 0};
> +             t_vblank = 0;
>  
>       /*
>        * +1 to make sure user will never see the same
>        * vblank counter value before and after a modeset
>        */
> -     store_vblank(dev, pipe, 1, &t_vblank, cur_vblank);
> +     store_vblank(dev, pipe, 1, t_vblank, cur_vblank);
>  
>       spin_unlock(&dev->vblank_time_lock);
>  }
> @@ -200,7 +200,7 @@ static void drm_update_vblank_count(struct drm_device 
> *dev, unsigned int pipe,
>       struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
>       u32 cur_vblank, diff;
>       bool rc;
> -     struct timeval t_vblank;
> +     ktime_t t_vblank;
>       int count = DRM_TIMESTAMP_MAXRETRIES;
>       int framedur_ns = vblank->framedur_ns;
>  
> @@ -225,11 +225,7 @@ static void drm_update_vblank_count(struct drm_device 
> *dev, unsigned int pipe,
>               /* trust the hw counter when it's around */
>               diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
>       } else if (rc && framedur_ns) {
> -             const struct timeval *t_old;
> -             u64 diff_ns;
> -
> -             t_old = &vblank->time;
> -             diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old);
> +             u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));
>  
>               /*
>                * 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,
>        * for now, to mark the vblanktimestamp as invalid.
>        */
>       if (!rc && !in_vblank_irq)
> -             t_vblank = (struct timeval) {0, 0};
> +             t_vblank = 0;
>  
> -     store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
> +     store_vblank(dev, pipe, diff, t_vblank, cur_vblank);
>  }
>  
>  static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
> @@ -556,7 +552,7 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
>   * @pipe: index of CRTC whose vblank timestamp to retrieve
>   * @max_error: Desired maximum allowable error in timestamps (nanosecs)
>   *             On return contains true maximum error of timestamp
> - * @vblank_time: Pointer to struct timeval which should receive the timestamp
> + * @vblank_time: Pointer to time which should receive the timestamp
>   * @in_vblank_irq:
>   *     True when called from drm_crtc_handle_vblank().  Some drivers
>   *     need to apply some workarounds for gpu-specific vblank irq quirks
> @@ -584,10 +580,10 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
>  bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
>                                          unsigned int pipe,
>                                          int *max_error,
> -                                        struct timeval *vblank_time,
> +                                        ktime_t *vblank_time,
>                                          bool in_vblank_irq)
>  {
> -     struct timeval tv_etime;
> +     struct timespec64 ts_etime, ts_vblank_time;
>       ktime_t stime, etime;
>       bool vbl_status;
>       struct drm_crtc *crtc;
> @@ -680,29 +676,27 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct 
> drm_device *dev,
>               etime = ktime_mono_to_real(etime);
>  
>       /* save this only for debugging purposes */
> -     tv_etime = ktime_to_timeval(etime);
> +     ts_etime = ktime_to_timespec64(etime);
> +     ts_vblank_time = ktime_to_timespec64(*vblank_time);
>       /* Subtract time delta from raw timestamp to get final
>        * vblank_time timestamp for end of vblank.
>        */
>       etime = ktime_sub_ns(etime, delta_ns);
> -     *vblank_time = ktime_to_timeval(etime);
> +     *vblank_time = etime;
>  
> -     DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d 
> rep]\n",
> +     DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %lld.%06ld -> %lld.%06ld [e %d us, 
> %d rep]\n",
>                     pipe, hpos, vpos,
> -                   (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
> -                   (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
> -                   duration_ns/1000, i);
> +                   (u64)ts_etime.tv_sec, ts_etime.tv_nsec / 1000,
> +                   (u64)ts_vblank_time.tv_sec, ts_vblank_time.tv_nsec / 1000,
> +                   duration_ns / 1000, i);
>  
>       return true;
>  }
>  EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
>  
> -static struct timeval get_drm_timestamp(void)
> +static ktime_t get_drm_timestamp(void)
>  {
> -     ktime_t now;
> -
> -     now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
> -     return ktime_to_timeval(now);
> +     return drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
>  }
>  
>  /**
> @@ -710,7 +704,7 @@ static struct timeval get_drm_timestamp(void)
>   *                             vblank interval
>   * @dev: DRM device
>   * @pipe: index of CRTC whose vblank timestamp to retrieve
> - * @tvblank: Pointer to target struct timeval which should receive the 
> timestamp
> + * @tvblank: Pointer to target time which should receive the timestamp
>   * @in_vblank_irq:
>   *     True when called from drm_crtc_handle_vblank().  Some drivers
>   *     need to apply some workarounds for gpu-specific vblank irq quirks
> @@ -728,7 +722,7 @@ static struct timeval get_drm_timestamp(void)
>   */
>  static bool
>  drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
> -                       struct timeval *tvblank, bool in_vblank_irq)
> +                       ktime_t *tvblank, bool in_vblank_irq)
>  {
>       bool ret = false;
>  
> @@ -769,14 +763,14 @@ u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
>  EXPORT_SYMBOL(drm_crtc_vblank_count);
>  
>  static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int 
> pipe,
> -                                  struct timeval *vblanktime)
> +                                  ktime_t *vblanktime)
>  {
>       struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
>       u32 vblank_count;
>       unsigned int seq;
>  
>       if (WARN_ON(pipe >= dev->num_crtcs)) {
> -             *vblanktime = (struct timeval) { 0 };
> +             *vblanktime = 0;
>               return 0;
>       }
>  
> @@ -793,7 +787,7 @@ static u32 drm_vblank_count_and_time(struct drm_device 
> *dev, unsigned int pipe,
>   * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
>   *     and the system timestamp corresponding to that vblank counter value
>   * @crtc: which counter to retrieve
> - * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
> + * @vblanktime: Pointer to time to receive the vblank timestamp.
>   *
>   * Fetches the "cooked" vblank count value that represents the number of
>   * vblank events since the system was booted, including lost events due to
> @@ -801,7 +795,7 @@ static u32 drm_vblank_count_and_time(struct drm_device 
> *dev, unsigned int pipe,
>   * of the vblank interval that corresponds to the current vblank counter 
> value.
>   */
>  u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
> -                                struct timeval *vblanktime)
> +                                ktime_t *vblanktime)
>  {
>       return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
>                                        vblanktime);
> @@ -810,11 +804,18 @@ EXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
>  
>  static void send_vblank_event(struct drm_device *dev,
>               struct drm_pending_vblank_event *e,
> -             unsigned long seq, struct timeval *now)
> +             unsigned long seq, ktime_t now)
>  {
> +     struct timespec64 tv = ktime_to_timespec64(now);
> +
>       e->event.sequence = seq;
> -     e->event.tv_sec = now->tv_sec;
> -     e->event.tv_usec = now->tv_usec;
> +     /*
> +      * e->event is a user space structure, with hardcoded unsigned
> +      * 32-bit seconds/microseconds. This will overflow in 2106 for
> +      * drm_timestamp_monotonic==0, but not with drm_timestamp_monotonic==1
> +      */
> +     e->event.tv_sec = tv.tv_sec;
> +     e->event.tv_usec = tv.tv_nsec / 1000;
>  
>       trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe,
>                                        e->event.sequence);
> @@ -891,7 +892,7 @@ void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
>  {
>       struct drm_device *dev = crtc->dev;
>       unsigned int seq, pipe = drm_crtc_index(crtc);
> -     struct timeval now;
> +     ktime_t now;
>  
>       if (dev->num_crtcs > 0) {
>               seq = drm_vblank_count_and_time(dev, pipe, &now);
> @@ -902,7 +903,7 @@ void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
>       }
>       e->pipe = pipe;
>       e->event.crtc_id = crtc->base.id;
> -     send_vblank_event(dev, e, seq, &now);
> +     send_vblank_event(dev, e, seq, now);
>  }
>  EXPORT_SYMBOL(drm_crtc_send_vblank_event);
>  
> @@ -1100,7 +1101,8 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc)
>       unsigned int pipe = drm_crtc_index(crtc);
>       struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
>       struct drm_pending_vblank_event *e, *t;
> -     struct timeval now;
> +
> +     ktime_t now;
>       unsigned long irqflags;
>       unsigned int seq;
>  
> @@ -1141,7 +1143,7 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc)
>                         e->event.sequence, seq);
>               list_del(&e->base.link);
>               drm_vblank_put(dev, pipe);
> -             send_vblank_event(dev, e, seq, &now);
> +             send_vblank_event(dev, e, seq, now);
>       }
>       spin_unlock_irqrestore(&dev->event_lock, irqflags);
>  
> @@ -1321,7 +1323,7 @@ static int drm_queue_vblank_event(struct drm_device 
> *dev, unsigned int pipe,
>  {
>       struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
>       struct drm_pending_vblank_event *e;
> -     struct timeval now;
> +     ktime_t now;
>       unsigned long flags;
>       unsigned int seq;
>       int ret;
> @@ -1367,7 +1369,7 @@ static int drm_queue_vblank_event(struct drm_device 
> *dev, unsigned int pipe,
>       e->event.sequence = vblwait->request.sequence;
>       if (vblank_passed(seq, vblwait->request.sequence)) {
>               drm_vblank_put(dev, pipe);
> -             send_vblank_event(dev, e, seq, &now);
> +             send_vblank_event(dev, e, seq, now);
>               vblwait->reply.sequence = seq;
>       } else {
>               /* drm_handle_vblank_events will call drm_vblank_put */
> @@ -1398,6 +1400,24 @@ static bool drm_wait_vblank_is_query(union 
> drm_wait_vblank *vblwait)
>                                         _DRM_VBLANK_NEXTONMISS));
>  }
>  
> +static void drm_wait_vblank_reply(struct drm_device *dev, unsigned int pipe,
> +                               struct drm_wait_vblank_reply *reply)
> +{
> +     ktime_t now;
> +     struct timespec64 ts;
> +
> +     /*
> +      * drm_wait_vblank_reply is a UAPI structure that uses 'long'
> +      * to store the seconds. This will overflow in y2038 on 32-bit
> +      * architectures with drm_timestamp_monotonic==0, but not with
> +      * drm_timestamp_monotonic==1 (the default).
> +      */
> +     reply->sequence = drm_vblank_count_and_time(dev, pipe, &now);
> +     ts = ktime_to_timespec64(now);
> +     reply->tval_sec = (u32)ts.tv_sec;
> +     reply->tval_usec = ts.tv_nsec / 1000;
> +}
> +
>  int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
>                         struct drm_file *file_priv)
>  {
> @@ -1439,12 +1459,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void 
> *data,
>       if (dev->vblank_disable_immediate &&
>           drm_wait_vblank_is_query(vblwait) &&
>           READ_ONCE(vblank->enabled)) {
> -             struct timeval now;
> -
> -             vblwait->reply.sequence =
> -                     drm_vblank_count_and_time(dev, pipe, &now);
> -             vblwait->reply.tval_sec = now.tv_sec;
> -             vblwait->reply.tval_usec = now.tv_usec;
> +             drm_wait_vblank_reply(dev, pipe, &vblwait->reply);
>               return 0;
>       }
>  
> @@ -1487,11 +1502,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void 
> *data,
>       }
>  
>       if (ret != -EINTR) {
> -             struct timeval now;
> -
> -             vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, 
> &now);
> -             vblwait->reply.tval_sec = now.tv_sec;
> -             vblwait->reply.tval_usec = now.tv_usec;
> +             drm_wait_vblank_reply(dev, pipe, &vblwait->reply);
>  
>               DRM_DEBUG("crtc %d returning %u to client\n",
>                         pipe, vblwait->reply.sequence);
> @@ -1507,7 +1518,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void 
> *data,
>  static void drm_handle_vblank_events(struct drm_device *dev, unsigned int 
> pipe)
>  {
>       struct drm_pending_vblank_event *e, *t;
> -     struct timeval now;
> +     ktime_t now;
>       unsigned int seq;
>  
>       assert_spin_locked(&dev->event_lock);
> @@ -1525,7 +1536,7 @@ static void drm_handle_vblank_events(struct drm_device 
> *dev, unsigned int pipe)
>  
>               list_del(&e->base.link);
>               drm_vblank_put(dev, pipe);
> -             send_vblank_event(dev, e, seq, &now);
> +             send_vblank_event(dev, e, seq, now);
>       }
>  
>       trace_drm_vblank_event(pipe, seq);
> diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
> index ee06ecd6c01f..412e83a4d3db 100644
> --- a/include/drm/drm_drv.h
> +++ b/include/drm/drm_drv.h
> @@ -324,7 +324,7 @@ struct drm_driver {
>        */
>       bool (*get_vblank_timestamp) (struct drm_device *dev, unsigned int pipe,
>                                    int *max_error,
> -                                  struct timeval *vblank_time,
> +                                  ktime_t *vblank_time,
>                                    bool in_vblank_irq);
>  
>       /**
> diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h
> index 7fba9efe4951..6a58e2e91a0f 100644
> --- a/include/drm/drm_vblank.h
> +++ b/include/drm/drm_vblank.h
> @@ -92,7 +92,7 @@ struct drm_vblank_crtc {
>       /**
>        * @time: Vblank timestamp corresponding to @count.
>        */
> -     struct timeval time;
> +     ktime_t time;
>  
>       /**
>        * @refcount: Number of users/waiters of the vblank interrupt. Only when
> @@ -154,7 +154,7 @@ struct drm_vblank_crtc {
>  int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs);
>  u32 drm_crtc_vblank_count(struct drm_crtc *crtc);
>  u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
> -                                struct timeval *vblanktime);
> +                                ktime_t *vblanktime);
>  void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
>                              struct drm_pending_vblank_event *e);
>  void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
> @@ -172,7 +172,7 @@ u32 drm_crtc_accurate_vblank_count(struct drm_crtc *crtc);
>  
>  bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
>                                          unsigned int pipe, int *max_error,
> -                                        struct timeval *vblank_time,
> +                                        ktime_t *vblank_time,
>                                          bool in_vblank_irq);
>  void drm_calc_timestamping_constants(struct drm_crtc *crtc,
>                                    const struct drm_display_mode *mode);
> -- 
> 2.9.0
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to