This changes the user API for ALSA to not rely on time_t as the type
any more, so it can survive the update to new C libraries that redefine
time_t to 64 bit.

This is a much simpler approach than earlier patches, simply defining
the API to use '__kernel_ulong_t' for tv_sec and tv_nsec, keeping the
existing binary interface but changing the way we express it.

The downside of this approach is that it requires significant user
space changes in alsa-lib and simialr projects in order to convert back
the 32-bit timestamps into 'timespec' for consumption by applications,
and that it requires using monotonic timestamps to avoid overflowing a
timespec in y2038.

To try to counter the incompatibility with existing user sources, the
new type is only used when __USE_TIME_BITS64 is set. This gets defined
by glibc when an application is compiled with 64-bit time_t.

Unfortunately, existing alsa-lib releases come with their own copy of
sound/asound.h and don't use the version provided by the linux/libc
headers, so alsa-lib is still silently broken when rebuilt with a new
libc until it gets updated to use the new header.

I also try to be extra conservative with the naming of the structure,
giving it the rather long name 'snd_monotonic_timestamp' to clarify that
this can only be used for monotonic timestamps after we have decided
not to extend the current interface to 64 bit stamps. A separate patch
is used to allow enforcing the use of monotonic timestamps in the kernel.

Signed-off-by: Arnd Bergmann <a...@arndb.de>
---
 include/sound/asound.h      |  8 ++++++++
 include/uapi/sound/asound.h | 46 +++++++++++++++++++++++++++++++++++----------
 sound/core/compat.h         | 11 +++++++++++
 sound/core/pcm_compat.c     | 38 +++++++++++++++++++------------------
 sound/core/pcm_lib.c        |  6 +++---
 sound/core/pcm_native.c     |  9 ++++-----
 sound/core/rawmidi_compat.c | 12 ++++++------
 sound/core/timer.c          | 10 +++++-----
 sound/core/timer_compat.c   |  4 +++-
 9 files changed, 96 insertions(+), 48 deletions(-)
 create mode 100644 sound/core/compat.h

diff --git a/include/sound/asound.h b/include/sound/asound.h
index c2dff5369d33..8c919eb9bb45 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -37,4 +37,12 @@
 #endif
 
 #include <uapi/sound/asound.h>
+
+
+static inline struct snd_monotonic_timestamp
+timespec64_to_snd_monotonic_timestamp(struct timespec64 ts)
+{
+       return (struct snd_monotonic_timestamp) { (u32)ts.tv_sec, ts.tv_nsec };
+};
+
 #endif /* __SOUND_ASOUND_H */
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 1231f0a943f1..9c61cf77beb8 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -455,10 +455,36 @@ enum {
        SNDRV_PCM_AUDIO_TSTAMP_TYPE_LAST = 
SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED
 };
 
+#if (__BITS_PER_LONG == 32 && defined(__USE_TIME_BITS64)) || defined __KERNEL__
+/*
+ * We used to use 'struct timespec' here, but that no longer works on
+ * 32 bit architectures since the migration to 64-bit time_t in user
+ * space. Rather than updating all ioctls, we change the internal type
+ * to a new one with the same binary layout as before.
+ *
+ * We use a 'unsigned long' as the base type here to give us a little
+ * extra range over the traditional signed type, but this really
+ * should only be used for CLOCK_MONOTONIC timestamps, not CLOCK_REALTIME,
+ * to avoid all issues with y2038 overflow, hence the name.
+ *
+ * alsa-lib 1.1.6 and earlier are incompatible with this definition and
+ * will break either at compile time or at runtime if built against
+ * an older header while using a 64-bit time_t on a 32-bit architecture,
+ * so if you run into a build problem here, please upgrade to the latest
+ * alsa-lib.
+ */
+struct snd_monotonic_timestamp {
+       __kernel_ulong_t tv_sec;
+       __kernel_ulong_t tv_nsec;
+};
+#else
+#define snd_monotonic_timestamp timespec
+#endif
+
 struct snd_pcm_status {
        snd_pcm_state_t state;          /* stream state */
-       struct timespec trigger_tstamp; /* time when stream was 
started/stopped/paused */
-       struct timespec tstamp;         /* reference timestamp */
+       struct snd_monotonic_timestamp trigger_tstamp;/* time when stream was 
started/stopped/paused */
+       struct snd_monotonic_timestamp tstamp;  /* reference timestamp */
        snd_pcm_uframes_t appl_ptr;     /* appl ptr */
        snd_pcm_uframes_t hw_ptr;       /* hw ptr */
        snd_pcm_sframes_t delay;        /* current delay in frames */
@@ -467,19 +493,19 @@ struct snd_pcm_status {
        snd_pcm_uframes_t overrange;    /* count of ADC (capture) overrange 
detections from last status */
        snd_pcm_state_t suspended_state; /* suspended stream state */
        __u32 audio_tstamp_data;         /* needed for 64-bit alignment, used 
for configs/report to/from userspace */
-       struct timespec audio_tstamp;   /* sample counter, wall clock, PHC or 
on-demand sync'ed */
-       struct timespec driver_tstamp;  /* useful in case reference system 
tstamp is reported with delay */
+       struct snd_monotonic_timestamp audio_tstamp;    /* sample counter, wall 
clock, PHC or on-demand sync'ed */
+       struct snd_monotonic_timestamp driver_tstamp;   /* useful in case 
reference system tstamp is reported with delay */
        __u32 audio_tstamp_accuracy;    /* in ns units, only valid if indicated 
in audio_tstamp_data */
-       unsigned char reserved[52-2*sizeof(struct timespec)]; /* must be filled 
with zero */
+       unsigned char reserved[52-2*sizeof(struct snd_monotonic_timestamp)]; /* 
must be filled with zero */
 };
 
 struct snd_pcm_mmap_status {
        snd_pcm_state_t state;          /* RO: state - SNDRV_PCM_STATE_XXXX */
        int pad1;                       /* Needed for 64 bit alignment */
        snd_pcm_uframes_t hw_ptr;       /* RO: hw ptr (0...boundary-1) */
-       struct timespec tstamp;         /* Timestamp */
+       struct snd_monotonic_timestamp tstamp; /* Timestamp */
        snd_pcm_state_t suspended_state; /* RO: suspended stream state */
-       struct timespec audio_tstamp;   /* from sample counter or wall clock */
+       struct snd_monotonic_timestamp audio_tstamp;    /* from sample counter 
or wall clock */
 };
 
 struct snd_pcm_mmap_control {
@@ -649,7 +675,7 @@ struct snd_rawmidi_params {
 
 struct snd_rawmidi_status {
        int stream;
-       struct timespec tstamp;         /* Timestamp */
+       struct snd_monotonic_timestamp tstamp; /* Timestamp */
        size_t avail;                   /* available bytes */
        size_t xruns;                   /* count of overruns since last status 
(in bytes) */
        unsigned char reserved[16];     /* reserved for future use */
@@ -761,7 +787,7 @@ struct snd_timer_params {
 };
 
 struct snd_timer_status {
-       struct timespec tstamp;         /* Timestamp - last update */
+       struct snd_monotonic_timestamp tstamp;          /* Timestamp - last 
update */
        unsigned int resolution;        /* current period resolution in ns */
        unsigned int lost;              /* counter of master tick lost */
        unsigned int overrun;           /* count of read queue overruns */
@@ -811,7 +837,7 @@ enum {
 
 struct snd_timer_tread {
        int event;
-       struct timespec tstamp;
+       struct snd_monotonic_timestamp tstamp;
        unsigned int val;
 };
 
diff --git a/sound/core/compat.h b/sound/core/compat.h
new file mode 100644
index 000000000000..7a08d6e34955
--- /dev/null
+++ b/sound/core/compat.h
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifndef SND_CORE_COMPAT_H
+#define SND_CORE_COMPAT_H
+
+struct compat_snd_monotonic_timestamp {
+       __u32   tv_sec;
+       __u32   tv_nsec;
+};
+
+#endif
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index e21d3f35a724..40e9be542322 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -23,6 +23,8 @@
 #include <linux/compat.h>
 #include <linux/slab.h>
 
+#include "compat.h"
+
 static int snd_pcm_ioctl_delay_compat(struct snd_pcm_substream *substream,
                                      s32 __user *src)
 {
@@ -189,8 +191,8 @@ static int snd_pcm_channel_info_user(struct 
snd_pcm_substream *substream,
 
 struct snd_pcm_status32 {
        s32 state;
-       struct compat_timespec trigger_tstamp;
-       struct compat_timespec tstamp;
+       struct compat_snd_monotonic_timestamp trigger_tstamp;
+       struct compat_snd_monotonic_timestamp tstamp;
        u32 appl_ptr;
        u32 hw_ptr;
        s32 delay;
@@ -199,10 +201,10 @@ struct snd_pcm_status32 {
        u32 overrange;
        s32 suspended_state;
        u32 audio_tstamp_data;
-       struct compat_timespec audio_tstamp;
-       struct compat_timespec driver_tstamp;
+       struct compat_snd_monotonic_timestamp audio_tstamp;
+       struct compat_snd_monotonic_timestamp driver_tstamp;
        u32 audio_tstamp_accuracy;
-       unsigned char reserved[52-2*sizeof(struct compat_timespec)];
+       unsigned char reserved[52-2*sizeof(struct 
compat_snd_monotonic_timestamp)];
 } __attribute__((packed));
 
 
@@ -269,11 +271,9 @@ struct snd_pcm_status_x32 {
        struct __kernel_timespec audio_tstamp;
        struct __kernel_timespec driver_tstamp;
        u32 audio_tstamp_accuracy;
-       unsigned char reserved[52-2*sizeof(struct timespec)];
+       unsigned char reserved[52-2*sizeof(struct __kernel_timespec)];
 } __packed;
 
-#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))
-
 static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream,
                                   struct snd_pcm_status_x32 __user *src,
                                   bool ext)
@@ -461,14 +461,13 @@ static int snd_pcm_ioctl_xfern_compat(struct 
snd_pcm_substream *substream,
        return err;
 }
 
-
 struct snd_pcm_mmap_status32 {
        s32 state;
        s32 pad1;
        u32 hw_ptr;
-       struct compat_timespec tstamp;
+       struct compat_snd_monotonic_timestamp tstamp;
        s32 suspended_state;
-       struct compat_timespec audio_tstamp;
+       struct compat_snd_monotonic_timestamp audio_tstamp;
 } __attribute__((packed));
 
 struct snd_pcm_mmap_control32 {
@@ -535,10 +534,11 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct 
snd_pcm_substream *substream,
        snd_pcm_stream_unlock_irq(substream);
        if (put_user(sstatus.state, &src->s.status.state) ||
            put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
-           compat_put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
+           put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) ||
+           put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) ||
            put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
-           compat_put_timespec(&sstatus.audio_tstamp,
-                   &src->s.status.audio_tstamp) ||
+           put_user(sstatus.audio_tstamp.tv_sec, 
&src->s.status.audio_tstamp.tv_sec) ||
+           put_user(sstatus.audio_tstamp.tv_nsec, 
&src->s.status.audio_tstamp.tv_nsec) ||
            put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
            put_user(scontrol.avail_min, &src->c.control.avail_min))
                return -EFAULT;
@@ -553,10 +553,10 @@ struct snd_pcm_mmap_status_x32 {
        s32 pad1;
        u32 hw_ptr;
        u32 pad2; /* alignment */
-       struct timespec tstamp;
+       struct __kernel_timespec tstamp;
        s32 suspended_state;
        s32 pad3;
-       struct timespec audio_tstamp;
+       struct __kernel_timespec audio_tstamp;
 } __packed;
 
 struct snd_pcm_mmap_control_x32 {
@@ -624,9 +624,11 @@ static int snd_pcm_ioctl_sync_ptr_x32(struct 
snd_pcm_substream *substream,
        snd_pcm_stream_unlock_irq(substream);
        if (put_user(sstatus.state, &src->s.status.state) ||
            put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) ||
-           put_timespec(&sstatus.tstamp, &src->s.status.tstamp) ||
+           put_user(sstatus.tstamp.tv_sec, &src->s.status.tstamp.tv_sec) ||
+           put_user(sstatus.tstamp.tv_nsec, &src->s.status.tstamp.tv_nsec) ||
            put_user(sstatus.suspended_state, &src->s.status.suspended_state) ||
-           put_timespec(&sstatus.audio_tstamp, &src->s.status.audio_tstamp) ||
+           put_user(sstatus.audio_tstamp.tv_sec, 
&src->s.status.audio_tstamp.tv_sec) ||
+           put_user(sstatus.audio_tstamp.tv_nsec, 
&src->s.status.audio_tstamp.tv_nsec) ||
            put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) ||
            put_user(scontrol.avail_min, &src->c.control.avail_min))
                return -EFAULT;
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 9278b5ded9a2..e676356bd3be 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -162,7 +162,7 @@ static void xrun(struct snd_pcm_substream *substream)
                struct timespec64 tstamp;
 
                snd_pcm_gettime(runtime, &tstamp);
-               runtime->status->tstamp = timespec64_to_timespec(tstamp);
+               runtime->status->tstamp = 
timespec64_to_snd_monotonic_timestamp(tstamp);
        }
        snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
        if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
@@ -256,8 +256,8 @@ static void update_audio_tstamp(struct snd_pcm_substream 
*substream,
        if (runtime->status->audio_tstamp.tv_sec != audio_tstamp->tv_sec ||
            runtime->status->audio_tstamp.tv_nsec != audio_tstamp->tv_nsec) {
                runtime->status->audio_tstamp =
-                       timespec64_to_timespec(*audio_tstamp);
-               runtime->status->tstamp = timespec64_to_timespec(*curr_tstamp);
+                       timespec64_to_snd_monotonic_timestamp(*audio_tstamp);
+               runtime->status->tstamp = 
timespec64_to_snd_monotonic_timestamp(*curr_tstamp);
        }
 
 
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index c57dd4b30198..d27c6252e14c 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -884,14 +884,13 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
        status->suspended_state = runtime->status->suspended_state;
        if (status->state == SNDRV_PCM_STATE_OPEN)
                goto _end;
-       status->trigger_tstamp = 
timespec64_to_timespec(runtime->trigger_tstamp);
+       status->trigger_tstamp = 
timespec64_to_snd_monotonic_timestamp(runtime->trigger_tstamp);
        if (snd_pcm_running(substream)) {
                snd_pcm_update_hw_ptr(substream);
                if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) {
                        status->tstamp = runtime->status->tstamp;
-                       status->driver_tstamp = 
timespec64_to_timespec(runtime->driver_tstamp);
-                       status->audio_tstamp =
-                               runtime->status->audio_tstamp;
+                       status->driver_tstamp = 
timespec64_to_snd_monotonic_timestamp(runtime->driver_tstamp);
+                       status->audio_tstamp = runtime->status->audio_tstamp;
                        if (runtime->audio_tstamp_report.valid == 1)
                                /* backwards compatibility, no report provided 
in COMPAT mode */
                                
snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data,
@@ -906,7 +905,7 @@ int snd_pcm_status(struct snd_pcm_substream *substream,
                        struct timespec64 tstamp;
 
                        snd_pcm_gettime(runtime, &tstamp);
-                       status->tstamp = timespec64_to_timespec(tstamp);
+                       status->tstamp = 
timespec64_to_snd_monotonic_timestamp(tstamp);
                }
        }
  _tstamp_end:
diff --git a/sound/core/rawmidi_compat.c b/sound/core/rawmidi_compat.c
index f69764d7cdd7..bf8b0c9da690 100644
--- a/sound/core/rawmidi_compat.c
+++ b/sound/core/rawmidi_compat.c
@@ -55,7 +55,7 @@ static int snd_rawmidi_ioctl_params_compat(struct 
snd_rawmidi_file *rfile,
 
 struct snd_rawmidi_status32 {
        s32 stream;
-       struct compat_timespec tstamp;
+       struct snd_monotonic_timestamp tstamp;
        u32 avail;
        u32 xruns;
        unsigned char reserved[16];
@@ -85,7 +85,8 @@ static int snd_rawmidi_ioctl_status_compat(struct 
snd_rawmidi_file *rfile,
        if (err < 0)
                return err;
 
-       if (compat_put_timespec(&status.tstamp, &src->tstamp) ||
+       if (put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
+           put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
            put_user(status.avail, &src->avail) ||
            put_user(status.xruns, &src->xruns))
                return -EFAULT;
@@ -98,14 +99,12 @@ static int snd_rawmidi_ioctl_status_compat(struct 
snd_rawmidi_file *rfile,
 struct snd_rawmidi_status_x32 {
        s32 stream;
        u32 rsvd; /* alignment */
-       struct timespec tstamp;
+       struct __kernel_timespec tstamp;
        u32 avail;
        u32 xruns;
        unsigned char reserved[16];
 } __attribute__((packed));
 
-#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))
-
 static int snd_rawmidi_ioctl_status_x32(struct snd_rawmidi_file *rfile,
                                        struct snd_rawmidi_status_x32 __user 
*src)
 {
@@ -130,7 +129,8 @@ static int snd_rawmidi_ioctl_status_x32(struct 
snd_rawmidi_file *rfile,
        if (err < 0)
                return err;
 
-       if (put_timespec(&status.tstamp, &src->tstamp) ||
+       if (put_user(status.tstamp.tv_sec, &src->tstamp.tv_sec) ||
+           put_user(status.tstamp.tv_nsec, &src->tstamp.tv_nsec) ||
            put_user(status.avail, &src->avail) ||
            put_user(status.xruns, &src->xruns))
                return -EFAULT;
diff --git a/sound/core/timer.c b/sound/core/timer.c
index a77b4619f1b7..b7059a9e9edc 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -1309,7 +1309,7 @@ static void snd_timer_user_ccallback(struct 
snd_timer_instance *timeri,
                return;
        memset(&r1, 0, sizeof(r1));
        r1.event = event;
-       r1.tstamp = timespec64_to_timespec(*tstamp);
+       r1.tstamp = timespec64_to_snd_monotonic_timestamp(*tstamp);
        r1.val = resolution;
        spin_lock_irqsave(&tu->qlock, flags);
        snd_timer_user_append_to_tqueue(tu, &r1);
@@ -1352,7 +1352,7 @@ static void snd_timer_user_tinterrupt(struct 
snd_timer_instance *timeri,
        if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
            tu->last_resolution != resolution) {
                r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
-               r1.tstamp = timespec64_to_timespec(tstamp);
+               r1.tstamp = timespec64_to_snd_monotonic_timestamp(tstamp);
                r1.val = resolution;
                snd_timer_user_append_to_tqueue(tu, &r1);
                tu->last_resolution = resolution;
@@ -1366,14 +1366,14 @@ static void snd_timer_user_tinterrupt(struct 
snd_timer_instance *timeri,
                prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
                r = &tu->tqueue[prev];
                if (r->event == SNDRV_TIMER_EVENT_TICK) {
-                       r->tstamp = timespec64_to_timespec(tstamp);
+                       r->tstamp = 
timespec64_to_snd_monotonic_timestamp(tstamp);
                        r->val += ticks;
                        append++;
                        goto __wake;
                }
        }
        r1.event = SNDRV_TIMER_EVENT_TICK;
-       r1.tstamp = timespec64_to_timespec(tstamp);
+       r1.tstamp = timespec64_to_snd_monotonic_timestamp(tstamp);
        r1.val = ticks;
        snd_timer_user_append_to_tqueue(tu, &r1);
        append++;
@@ -1852,7 +1852,7 @@ static int snd_timer_user_status(struct file *file,
        if (!tu->timeri)
                return -EBADFD;
        memset(&status, 0, sizeof(status));
-       status.tstamp = timespec64_to_timespec(tu->tstamp);
+       status.tstamp = timespec64_to_snd_monotonic_timestamp(tu->tstamp);
        status.resolution = snd_timer_resolution(tu->timeri);
        status.lost = tu->timeri->lost;
        status.overrun = tu->overrun;
diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c
index e00f7e399e46..960d1075071f 100644
--- a/sound/core/timer_compat.c
+++ b/sound/core/timer_compat.c
@@ -22,6 +22,8 @@
 
 #include <linux/compat.h>
 
+#include "compat.h"
+
 /*
  * ILP32/LP64 has different size for 'long' type. Additionally, the size
  * of storage alignment differs depending on architectures. Here, '__packed'
@@ -84,7 +86,7 @@ static int snd_timer_user_info_compat(struct file *file,
 }
 
 struct snd_timer_status32 {
-       struct compat_timespec tstamp;
+       struct compat_snd_monotonic_timestamp tstamp;
        u32 resolution;
        u32 lost;
        u32 overrun;
-- 
2.9.0

Reply via email to