'struct timeval' uses 32-bit representation for seconds which will overflow in year 2038 and beyond. mISDN/clock.c needs to compute and store elapsed time in intervals of 125 microseconds. This patch replaces the usage of 'struct timeval' with 64-bit ktime_t which is y2038 safe. The patch also replaces use of wall-clock time (do_gettimeofday) with monotonic time (ktime_get) since we only care about elapsed time here.
Signed-off-by: Tina Ruchandani ruchandani.tina@gmail.com --- drivers/isdn/mISDN/clock.c | 53 +++++++++++++++++----------------------------- include/linux/mISDNif.h | 2 +- 2 files changed, 21 insertions(+), 34 deletions(-)
diff --git a/drivers/isdn/mISDN/clock.c b/drivers/isdn/mISDN/clock.c index 693fb7c..7550f2e 100644 --- a/drivers/isdn/mISDN/clock.c +++ b/drivers/isdn/mISDN/clock.c @@ -37,6 +37,7 @@ #include <linux/types.h> #include <linux/stddef.h> #include <linux/spinlock.h> +#include <linux/ktime.h> #include <linux/mISDNif.h> #include <linux/export.h> #include "core.h" @@ -45,7 +46,7 @@ static u_int *debug; static LIST_HEAD(iclock_list); static DEFINE_RWLOCK(iclock_lock); static u16 iclock_count; /* counter of last clock */ -static struct timeval iclock_tv; /* time stamp of last clock */ +static ktime_t iclock_tv; /* time stamp of last clock */ static int iclock_tv_valid; /* already received one timestamp */ static struct mISDNclock *iclock_current;
@@ -53,7 +54,7 @@ void mISDN_init_clock(u_int *dp) { debug = dp; - do_gettimeofday(&iclock_tv); + iclock_tv = ktime_get(); }
static void @@ -139,12 +140,11 @@ mISDN_unregister_clock(struct mISDNclock *iclock) EXPORT_SYMBOL(mISDN_unregister_clock);
void -mISDN_clock_update(struct mISDNclock *iclock, int samples, struct timeval *tv) +mISDN_clock_update(struct mISDNclock *iclock, int samples, ktime_t *tv) { u_long flags; - struct timeval tv_now; - time_t elapsed_sec; - int elapsed_8000th; + ktime_t tv_now; + u16 delta;
write_lock_irqsave(&iclock_lock, flags); if (iclock_current != iclock) { @@ -160,28 +160,20 @@ mISDN_clock_update(struct mISDNclock *iclock, int samples, struct timeval *tv) /* increment sample counter by given samples */ iclock_count += samples; if (tv) { /* tv must be set, if function call is delayed */ - iclock_tv.tv_sec = tv->tv_sec; - iclock_tv.tv_usec = tv->tv_usec; + iclock_tv = *tv; } else - do_gettimeofday(&iclock_tv); + iclock_tv = ktime_get(); } else { /* calc elapsed time by system clock */ if (tv) { /* tv must be set, if function call is delayed */ - tv_now.tv_sec = tv->tv_sec; - tv_now.tv_usec = tv->tv_usec; + tv_now = *tv; } else - do_gettimeofday(&tv_now); - elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec; - elapsed_8000th = (tv_now.tv_usec / 125) - - (iclock_tv.tv_usec / 125); - if (elapsed_8000th < 0) { - elapsed_sec -= 1; - elapsed_8000th += 8000; - } + tv_now = ktime_get(); + delta = ktime_to_us(ktime_sub(tv_now, iclock_tv)); + delta = delta / 125; /* add elapsed time to counter and set new timestamp */ - iclock_count += elapsed_sec * 8000 + elapsed_8000th; - iclock_tv.tv_sec = tv_now.tv_sec; - iclock_tv.tv_usec = tv_now.tv_usec; + iclock_count += delta; + iclock_tv = tv_now; iclock_tv_valid = 1; if (*debug & DEBUG_CLOCK) printk("Received first clock from source '%s'.\n", @@ -195,22 +187,17 @@ unsigned short mISDN_clock_get(void) { u_long flags; - struct timeval tv_now; - time_t elapsed_sec; - int elapsed_8000th; + ktime_t tv_now; + u16 delta; u16 count;
read_lock_irqsave(&iclock_lock, flags); /* calc elapsed time by system clock */ - do_gettimeofday(&tv_now); - elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec; - elapsed_8000th = (tv_now.tv_usec / 125) - (iclock_tv.tv_usec / 125); - if (elapsed_8000th < 0) { - elapsed_sec -= 1; - elapsed_8000th += 8000; - } + tv_now = ktime_get(); + delta = ktime_to_us(ktime_sub(tv_now, iclock_tv)); + delta = delta / 125; /* add elapsed time to counter */ - count = iclock_count + elapsed_sec * 8000 + elapsed_8000th; + count = iclock_count + delta; read_unlock_irqrestore(&iclock_lock, flags); return count; } diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index 246a352..ac02c54 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -596,7 +596,7 @@ static inline struct mISDNdevice *dev_to_mISDN(struct device *dev) }
extern void set_channel_address(struct mISDNchannel *, u_int, u_int); -extern void mISDN_clock_update(struct mISDNclock *, int, struct timeval *); +extern void mISDN_clock_update(struct mISDNclock *, int, ktime_t *); extern unsigned short mISDN_clock_get(void); extern const char *mISDNDevName4ch(struct mISDNchannel *);
On Monday 11 May 2015 08:02:57 Tina Ruchandani wrote:
do_gettimeofday(&tv_now);
elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec;
elapsed_8000th = (tv_now.tv_usec / 125)
- (iclock_tv.tv_usec / 125);
if (elapsed_8000th < 0) {
elapsed_sec -= 1;
elapsed_8000th += 8000;
}
tv_now = ktime_get();
A minor coding style comment here: If you have a multi-line block after if() using curly braces, then you should also use that for the else path, even if that is just one line.
However, if both sides do not need curly braces, leave them out entirely.
delta = ktime_to_us(ktime_sub(tv_now, iclock_tv));
We have a ktime_us_delta() function now that would simplify this slightly.
delta = delta / 125;
This looks like a possible bug: delta is a 16-bit number, so it will be truncated to 65536 microseconds when assigning the result of ktime_us_delta() or ktime_to_us(), and then you divide by 125, so it will now be a number that is smaller than 524, and if the elapsed time is more than 65536 microseconds, you can an incorrect result.
You can avoid that by using a 32-bit temporary, or doing it implicitly as
delta = ktime_us_delta(tv_now, iclock_tv) / 125;
Another option would be to calculate the right number from the nanoseconds:
delta = ktime_divns(ktime_sub(tv_now, iclock_tv), (NS_PER_SEC / 8000));
Arnd