There was a meeting at ELCE about the status and development of Y2038-
safe APIs in Linux and GNU libc. This included several developers from
members of CIP, plus Arnd Bergmann, Mark Brown and Albert Aribaud.
Below are my notes from the meeting.
Ben.
## Kernel
Arnd Bergmann started working on Y2038 about 6 years ago, initially
looking at file-systems and VFS. File-systems are mostly ready but VFS
hasn't been switched over yet.
The largest missing piece is the syscall interface. "We know what we
want to do." There was a complete patch set for an older version, but
it has not been completely rebased onto current mainline. On 32-bit
systems about 35 syscalls use 32-bit time, but half of those are
already obsolete. Something like 22 new syscalls will be needed.
Network, sound, media, key management, input have not been dealt with
yet. Patches are available for some of these but they can be invasive
and hard to review. This is a low priority for some subsystem
maintainers.
About 100 device drivers need changes, ranging from an obvious 1 line
change to a week's work.
About 10% of the changes are needed for Y2038 safety on both 32-bit
and 64-bit architectures, the rest only for 32-bit.
Arnd wants to include a kconfig option to disable the 32-bit time
APIs, so that any remaining users are easy to detect.
## GNU libc
Albert Aribaut talked about the status of glibc. It will need to
support both 32-bit `time_t` and 64-bit `time_t` independently of the
kernel. A draft specification for this exists at
<https://sourceware.org/glibc/wiki/Y2038ProofnessDesign>. About 60
APIs are affected, using `time_t` or derived type.
Ideally source can be rebuilt to use 64-bit `time_t` just by
defining the feature macro to enable it.
The implementation is not complete, partly because syscalls haven't
yet been defined.
## Other C libraries
Arnd says some other C libraries will support 64-bit `time_t` but as a
build-time option. I.e. libc and all applications must be built for
either 32-bit or 64-bit `time_t`.
## Application compatibility issues
If Unix timestamps are used in binary file formats or network
protocols, these will need a new version. In some cases switching to
unsigned 32-bit values is easy and will work for long enough.
If `time_t` is used in library APIs then an ABI change is required.
cppcheck(?) can find instances of this.
Some libraries may use their own time types, so changing `time_t`
won't be an ABI change but they will need to be updated anyway.
Printing a value of type `time_t` with `printf()` and similar
functions requires casting as there's no format specifier for it. It
will be necessary to cast to `long long`, whereas previously `long`
would work.
The sparse static checker is supposed to be able to check for
truncating conversions of `time_t`.
## Ongoing work in kernel and glibc
A few people are working part time on this. Kernel patches are 60%
done after 5 years, GNU libc about 75% (but only some of those changes
have been applied). More people may be needed to speed this up and get
it finished.
The kernel side is coordinated through the y2038 mailing list:
<https://lists.linaro.org/mailman/listinfo/y2038>. Patches are all
sent to this mailing list. There is currently no git tree collecting
them all.
Help is wanted to:
* Update device drivers
* Review sound patches
* Collect patches into single git tree
The glibc side is coordinated through the general development mailing
list: <https://www.gnu.org/software/libc/involved.html>,
<https://sourceware.org/ml/libc-alpha/>.
--
Ben Hutchings
Software Developer, Codethink Ltd.
We want to remove uses of do_gettimeofday() from the kernel since the
resulting timeval structure overflows in 2038. This is not a problem for
this particular use, but do_gettimeofday() is also not an appropriate
method for measuring time intervals, since it requires a conversion into
microseconds and is complicated to work with.
ktime_get() is a better replacement, as it works with the monontonic
kernel timebase and requires a minimum of computation.
I'm slightly changing the output from microseconds to nanoseconds here,
to avoid introducing a new division operation. This should be fine
since the value is only used for debugging.
Signed-off-by: Arnd Bergmann <arnd(a)arndb.de>
---
drivers/gpu/drm/via/via_drv.h | 4 ++--
drivers/gpu/drm/via/via_irq.c | 21 +++++++--------------
2 files changed, 9 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h
index 9873942ca8f4..6d1ae834484c 100644
--- a/drivers/gpu/drm/via/via_drv.h
+++ b/drivers/gpu/drm/via/via_drv.h
@@ -74,9 +74,9 @@ typedef struct drm_via_private {
volatile uint32_t *last_pause_ptr;
volatile uint32_t *hw_addr_ptr;
drm_via_ring_buffer_t ring;
- struct timeval last_vblank;
+ ktime_t last_vblank;
int last_vblank_valid;
- unsigned usec_per_vblank;
+ ktime_t nsec_per_vblank;
atomic_t vbl_received;
drm_via_state_t hc_state;
char pci_buf[VIA_PCI_BUF_SIZE];
diff --git a/drivers/gpu/drm/via/via_irq.c b/drivers/gpu/drm/via/via_irq.c
index ea8172c747a2..24e71578af4d 100644
--- a/drivers/gpu/drm/via/via_irq.c
+++ b/drivers/gpu/drm/via/via_irq.c
@@ -88,13 +88,6 @@ static int via_num_unichrome = ARRAY_SIZE(via_unichrome_irqs);
static int via_irqmap_unichrome[] = {-1, -1, -1, 0, -1, 1};
-static unsigned time_diff(struct timeval *now, struct timeval *then)
-{
- return (now->tv_usec >= then->tv_usec) ?
- now->tv_usec - then->tv_usec :
- 1000000 - (then->tv_usec - now->tv_usec);
-}
-
u32 via_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
{
drm_via_private_t *dev_priv = dev->dev_private;
@@ -111,7 +104,7 @@ irqreturn_t via_driver_irq_handler(int irq, void *arg)
drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
u32 status;
int handled = 0;
- struct timeval cur_vblank;
+ ktime_t cur_vblank;
drm_via_irq_t *cur_irq = dev_priv->via_irqs;
int i;
@@ -119,18 +112,18 @@ irqreturn_t via_driver_irq_handler(int irq, void *arg)
if (status & VIA_IRQ_VBLANK_PENDING) {
atomic_inc(&dev_priv->vbl_received);
if (!(atomic_read(&dev_priv->vbl_received) & 0x0F)) {
- do_gettimeofday(&cur_vblank);
+ cur_vblank = ktime_get();
if (dev_priv->last_vblank_valid) {
- dev_priv->usec_per_vblank =
- time_diff(&cur_vblank,
- &dev_priv->last_vblank) >> 4;
+ dev_priv->nsec_per_vblank =
+ ktime_sub(cur_vblank,
+ dev_priv->last_vblank) >> 4;
}
dev_priv->last_vblank = cur_vblank;
dev_priv->last_vblank_valid = 1;
}
if (!(atomic_read(&dev_priv->vbl_received) & 0xFF)) {
- DRM_DEBUG("US per vblank is: %u\n",
- dev_priv->usec_per_vblank);
+ DRM_DEBUG("nsec per vblank is: %llu\n",
+ ktime_to_ns(dev_priv->nsec_per_vblank));
}
drm_handle_vblank(dev, 0);
handled = 1;
--
2.9.0