Skip to contentSkip to conversation

time: Fix truncation in jiffies_to_usecs()

RFC PATCH In review Tony Luck 6 participants 16 messages Apr 8, 2014 – Jun 28, 2014 Tracing

lore
Share & tools
lore

Tony Luck

author
#0 lore raw

RFC PATCH 1/1 time: Fix truncation in jiffies_to_usecs()

jiffies_to_usecs() returns an "int" so it can only handle times up to 2^32 microseconds (1 hour 11 minutes 34 seconds) before truncation occurs. This is a problem for the "uptime" trace clock added in

commit 8aacf017b065a805d27467843490c976835eb4a5

tracing: Add "uptime" trace clock that uses jiffies

since it converts jiffies since boot into microseconds (and then on to nanoseconds).

Change return type from "int" to "u64" to avoid trucncation.

Also have the trace clock code use jiffies_to_nsecs() directly.

---

This is a RFC because:
a) Not sure it is fair to all the existing users of jiffies_to_usecs() to start handing them a u64 when their usage needs are met by "int" (mostly they only pass in small arguments, like 10).
b) If this is OK - it needs to touch a few more files where people do

	printk("yada yada yada %d blah blah blah\n", jiffies_to_nsecs(something));

to change that format to %lld
c) If not this ... then what? Separate routine to convert large numbers of jiffies to usec/nsecs? Should we make the existing one barf when handed a number that overflows?

 include/linux/jiffies.h    | 4 ++--
 kernel/time.c              | 2 +-
 kernel/trace/trace_clock.c | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)
include/linux/jiffies.h +2 -2
index 1f44466c1e9d..4b2621af705a 100644
@@ -293,11 +293,11 @@ extern unsigned long preset_lpj;
  * Convert various time units to each other:
  */
 extern unsigned int jiffies_to_msecs(const unsigned long j);
-extern unsigned int jiffies_to_usecs(const unsigned long j);
+extern u64 jiffies_to_usecs(const unsigned long j);
 
 static inline u64 jiffies_to_nsecs(const unsigned long j)
 {
-	return (u64)jiffies_to_usecs(j) * NSEC_PER_USEC;
+	return jiffies_to_usecs(j) * NSEC_PER_USEC;
 }
 
 extern unsigned long msecs_to_jiffies(const unsigned int m);
kernel/time.c +1 -1
index 7c7964c33ae7..e0dfc77f60ae 100644
@@ -257,7 +257,7 @@ unsigned int jiffies_to_msecs(const unsigned long j)
 }
 EXPORT_SYMBOL(jiffies_to_msecs);
 
-unsigned int jiffies_to_usecs(const unsigned long j)
+u64 jiffies_to_usecs(const unsigned long j)
 {
 #if HZ <= USEC_PER_SEC && !(USEC_PER_SEC % HZ)
 	return (USEC_PER_SEC / HZ) * j;
kernel/trace/trace_clock.c +1 -1
index 26dc348332b7..52470fba1d26 100644
@@ -65,7 +65,7 @@ u64 notrace trace_clock_jiffies(void)
 	u64 jiffy = jiffies - INITIAL_JIFFIES;
 
 	/* Return nsecs */
-	return (u64)jiffies_to_usecs(jiffy) * 1000ULL;
+	return jiffies_to_nsecs(jiffy);
 }
 
 /*
Signature · 1 line
1.8.4.1
#1 lore raw
Quoted 3 lines · Tony Luck

c) If not this ... then what? Separate routine to convert large numbers of jiffies to usec/nsecs? Should we make the existing one barf when handed a number that overflows?

Having thought about this a bit more - I'm leaning towards leaving jiffies_to_usecs() alone, but using it as a model for a from-scratch implementation of:
u64 jiffies_to_nsecs(const unsigned long j)
{
}

This is what the uptime tracer actually needs - and there is only one user of jiffies_to_nsecs() to worry about.

-Tony

maintainer
#2 lore raw
Quoted 13 lines · On Mon, 7 Apr 2014 22:34:51 -0700 Tony Luck <[email protected]> wrote:
Quoted 3 lines · Tony Luck

c) If not this ... then what? Separate routine to convert large numbers of jiffies to usec/nsecs? Should we make the existing one barf when handed a number that overflows?

Having thought about this a bit more - I'm leaning towards leaving jiffies_to_usecs() alone, but using it as a model for a from-scratch implementation of:
u64 jiffies_to_nsecs(const unsigned long j)
{
}

Quoted 2 lines

This is what the uptime tracer actually needs - and there is only one user of jiffies_to_nsecs() to worry about.

Sounds good to me.

-- Steve

#3 lore raw
In reply to Tony Luck
Quoted 13 lines · Tony Luck
Quoted 3 lines · Tony Luck

c) If not this ... then what? Separate routine to convert large numbers of jiffies to usec/nsecs? Should we make the existing one barf when handed a number that overflows?

Having thought about this a bit more - I'm leaning towards leaving jiffies_to_usecs() alone, but using it as a model for a from-scratch implementation of:
u64 jiffies_to_nsecs(const unsigned long j)
{
}

Quoted 2 lines

This is what the uptime tracer actually needs - and there is only one user of jiffies_to_nsecs() to worry about.

I'm not sure I get what you're trying to do. We already have jiffies_to_nsecs(). Anyway I'll just wait and check out the next patch :)

Thanks.

maintainer
#4 lore raw
Quoted 14 lines · On Tue, 8 Apr 2014 19:49:51 +0200 Frederic Weisbecker <[email protected]>…
Quoted 13 lines · Tony Luck
Quoted 3 lines · Tony Luck

c) If not this ... then what? Separate routine to convert large numbers of jiffies to usec/nsecs? Should we make the existing one barf when handed a number that overflows?

Having thought about this a bit more - I'm leaning towards leaving jiffies_to_usecs() alone, but using it as a model for a from-scratch implementation of:
u64 jiffies_to_nsecs(const unsigned long j)
{
}

Quoted 5 lines
Quoted 2 lines

This is what the uptime tracer actually needs - and there is only one user of jiffies_to_nsecs() to worry about.

I'm not sure I get what you're trying to do. We already have jiffies_to_nsecs(). Anyway I'll just wait and check out the next patch :)

I believe the issue is the way it's implemented:

static inline u64 jiffies_to_nsecs(const unsigned long j)

{
	return (u64)jiffies_to_usecs(j) * NSEC_PER_USEC;
}

The problem is with jiffies_to_usecs(). Which we probably should change.

With HZ = 100,
1 second jiffies_to_usecs(100) = 1000,000.
1 minute jiffies_to_usec(6000) = 60,000,000.
1 hour jiffies_to_usecs(360000) = 3,600,000,000
1 hour 11 minutes 35 seconds -

    jiffies_to_usecs(429500) = 4,295,000,000

2^32 = 4294967296 < 4,295,000,000

Overflow!

That means after 1 hour, 11 minutes and 35 seconds, jiffies_to_usecs() will return a reset number. Time will go backwards. It doesn't matter what you typecast the return value of jiffies_to_usecs() to, the result is wrong.

Actually, I like Tony's first patch. I really think jiffies_to_usecs() should return a u64 number.

-- Steve

#5 lore raw
Quoted 40 lines · Steven Rostedt
Quoted 14 lines · On Tue, 8 Apr 2014 19:49:51 +0200 Frederic Weisbecker <[email protected]>…
Quoted 13 lines · Tony Luck
Quoted 3 lines · Tony Luck

c) If not this ... then what? Separate routine to convert large numbers of jiffies to usec/nsecs? Should we make the existing one barf when handed a number that overflows?

Having thought about this a bit more - I'm leaning towards leaving jiffies_to_usecs() alone, but using it as a model for a from-scratch implementation of:
u64 jiffies_to_nsecs(const unsigned long j)
{
}

Quoted 5 lines
Quoted 2 lines

This is what the uptime tracer actually needs - and there is only one user of jiffies_to_nsecs() to worry about.

I'm not sure I get what you're trying to do. We already have jiffies_to_nsecs(). Anyway I'll just wait and check out the next patch :)

I believe the issue is the way it's implemented:

static inline u64 jiffies_to_nsecs(const unsigned long j)

{
	return (u64)jiffies_to_usecs(j) * NSEC_PER_USEC;
}

The problem is with jiffies_to_usecs(). Which we probably should change.

With HZ = 100,
1 second jiffies_to_usecs(100) = 1000,000.
1 minute jiffies_to_usec(6000) = 60,000,000.
1 hour jiffies_to_usecs(360000) = 3,600,000,000
1 hour 11 minutes 35 seconds -

    jiffies_to_usecs(429500) = 4,295,000,000
Quoted 8 lines

2^32 = 4294967296 < 4,295,000,000

Overflow!

That means after 1 hour, 11 minutes and 35 seconds, jiffies_to_usecs() will return a reset number. Time will go backwards. It doesn't matter what you typecast the return value of jiffies_to_usecs() to, the result is wrong.

Ah! Ok got it now.

Quoted 2 lines

Actually, I like Tony's first patch. I really think jiffies_to_usecs() should return a u64 number.

Agreed it's way too error-prone. OTOH there are too many users to allow such a blind broad conversion of its return type:

    $ git grep jiffies_to_usecs | cut -f1 | wc -l
    52

So it may indeed be a better idea to first create a standalone jiffies_to_nsecs(). It can then be used to deprecate and replace most (if not all) calls to jiffies_to_usecs() altogether. Just the conversion must be made one by one to make sure that users can handle that.

Of course a big fat comment on jiffies_to_usecs() to describe that it's unsafe and deprecated would help a bit.

maintainer
#6 lore raw
Quoted 7 lines · On Tue, 8 Apr 2014 20:56:13 +0200 Frederic Weisbecker <[email protected]>…

So it may indeed be a better idea to first create a standalone jiffies_to_nsecs(). It can then be used to deprecate and replace most (if not all) calls to jiffies_to_usecs() altogether. Just the conversion must be made one by one to make sure that users can handle that.

Of course a big fat comment on jiffies_to_usecs() to describe that it's unsafe and deprecated would help a bit.

Sure, sounds like a plan.

-- Steve

author
#7 lore raw

PATCH 1/1 time: Provide full featured jiffies_to_nsecs() function

The "uptime" tracer added in:
commit 8aacf017b065a805d27467843490c976835eb4a5
tracing: Add "uptime" trace clock that uses jiffies
has wraparound problems when the system has been up more than 1 hour 11 minutes and 34 seconds. It converts jiffies to nanoseconds using:

	(u64)jiffies_to_usecs(jiffy) * 1000ULL

but since jiffies_to_usecs() only returns a 32-bit value, it truncates at 2^32 microseconds. An additional problem on 32-bit systems is that the argument is "unsigned long", so fixing the return value only helps until 2^32 jiffies (49.7 days on a HZ=1000 system).

So we provide a full featured jiffies_to_nsec() function that takes a "u64" argument and provides a "u64" result. To avoid cries of rage from the other user of this: scheduler_tick_max_deferment() we check whether the argument is small enough that we can do the calculations in 32-bit operations.

---
 include/linux/jiffies.h    |  6 +-----
 kernel/time.c              | 24 ++++++++++++++++++++++++
 kernel/timeconst.bc        | 12 ++++++++++++
 kernel/trace/trace_clock.c |  2 +-
 4 files changed, 38 insertions(+), 6 deletions(-)
include/linux/jiffies.h +1 -5
index 1f44466c1e9d..3cf44401055f 100644
@@ -294,11 +294,7 @@ extern unsigned long preset_lpj;
  */
 extern unsigned int jiffies_to_msecs(const unsigned long j);
 extern unsigned int jiffies_to_usecs(const unsigned long j);
-
-static inline u64 jiffies_to_nsecs(const unsigned long j)
-{
-	return (u64)jiffies_to_usecs(j) * NSEC_PER_USEC;
-}
+extern u64 jiffies_to_nsecs(const u64 j);
 
 extern unsigned long msecs_to_jiffies(const unsigned int m);
 extern unsigned long usecs_to_jiffies(const unsigned int u);
kernel/time.c +24 -0
index 7c7964c33ae7..bbd18b0e5698 100644
@@ -273,6 +273,30 @@ unsigned int jiffies_to_usecs(const unsigned long j)
 }
 EXPORT_SYMBOL(jiffies_to_usecs);
 
+u64 jiffies_to_nsecs(const u64 jl)
+{
+	/* can we do this with 32-bit math? */
+	if (jl < 4*HZ) {
+		unsigned long j = jl;
+#if !(NSEC_PER_SEC % HZ)
+		return (NSEC_PER_SEC / HZ) * j;
+#else
+# if BITS_PER_LONG == 32
+		return (HZ_TO_NSEC_MUL32 * j) >> HZ_TO_NSEC_SHR32;
+# else
+		return (j * HZ_TO_NSEC_NUM) / HZ_TO_NSEC_DEN;
+# endif
+#endif
+	} else {
+#if !(NSEC_PER_SEC % HZ)
+		return (NSEC_PER_SEC / HZ) * jl;
+#else
+		return (jl * HZ_TO_NSEC_NUM) / HZ_TO_NSEC_DEN;
+#endif
+	}
+}
+EXPORT_SYMBOL(jiffies_to_nsecs);
+
 /**
  * timespec_trunc - Truncate timespec to a granularity
  * @t: Timespec
kernel/timeconst.bc +12 -0
index 511bdf2cafda..6f6e8b285c2b 100644
@@ -100,6 +100,18 @@ define timeconst(hz) {
 		print "#define USEC_TO_HZ_DEN\t\t", 1000000/cd, "\n"
 		print "\n"
 
+                s=fmuls(32,1000000000,hz)
+                obase=16
+                print "#define HZ_TO_NSEC_MUL32\tU64_C(0x", fmul(s,1000000000,hz), ")\n"
+                obase=10
+                print "#define HZ_TO_NSEC_SHR32\t", s, "\n"
+
+                obase=10
+                cd=gcd(hz,1000000000)
+                print "#define HZ_TO_NSEC_NUM\t\t", 1000000000/cd, "\n"
+                print "#define HZ_TO_NSEC_DEN\t\t", hz/cd, "\n"
+                print "\n"
+
 		print "#endif /* KERNEL_TIMECONST_H */\n"
 	}
 	halt
kernel/trace/trace_clock.c +1 -1
index 26dc348332b7..52470fba1d26 100644
@@ -65,7 +65,7 @@ u64 notrace trace_clock_jiffies(void)
 	u64 jiffy = jiffies - INITIAL_JIFFIES;
 
 	/* Return nsecs */
-	return (u64)jiffies_to_usecs(jiffy) * 1000ULL;
+	return jiffies_to_nsecs(jiffy);
 }
 
 /*
Signature · 1 line
1.8.4.1
#8 lore raw

Re: [PATCH] time: Provide full featured jiffies_to_nsecs() function

+ if (jl < 4*HZ) {

That was lazy ... should be something like:

+ if (jl < (u64)UINT_MAX * HZ / NSEC_PER_SEC)

-Tony

author
#9 lore raw

PATCH 1/1 time: Provide full featured jiffies_to_nsecs() function

The "uptime" tracer added in:
commit 8aacf017b065a805d27467843490c976835eb4a5
tracing: Add "uptime" trace clock that uses jiffies
has wraparound problems when the system has been up more than 1 hour 11 minutes and 34 seconds. It converts jiffies to nanoseconds using:

	(u64)jiffies_to_usecs(jiffy) * 1000ULL

but since jiffies_to_usecs() only returns a 32-bit value, it truncates at 2^32 microseconds. An additional problem on 32-bit systems is that the argument is "unsigned long", so fixing the return value only helps until 2^32 jiffies (49.7 days on a HZ=1000 system).

So we provide a full featured jiffies_to_nsec() function that takes a "u64" argument and provides a "u64" result. To avoid cries of rage from the other user of this: scheduler_tick_max_deferment() we check whether the argument is small enough that we can do the calculations in 32-bit operations.

---

Looks like I flubbed on re-sending the updated version of this after our discussion back in early April.

 include/linux/jiffies.h    |  6 +-----
 kernel/time.c              | 25 +++++++++++++++++++++++++
 kernel/timeconst.bc        | 12 ++++++++++++
 kernel/trace/trace_clock.c |  2 +-
 4 files changed, 39 insertions(+), 6 deletions(-)
include/linux/jiffies.h +1 -5
index 1f44466c1e9d..3cf44401055f 100644
@@ -294,11 +294,7 @@ extern unsigned long preset_lpj;
  */
 extern unsigned int jiffies_to_msecs(const unsigned long j);
 extern unsigned int jiffies_to_usecs(const unsigned long j);
-
-static inline u64 jiffies_to_nsecs(const unsigned long j)
-{
-	return (u64)jiffies_to_usecs(j) * NSEC_PER_USEC;
-}
+extern u64 jiffies_to_nsecs(const u64 j);
 
 extern unsigned long msecs_to_jiffies(const unsigned int m);
 extern unsigned long usecs_to_jiffies(const unsigned int u);
kernel/time.c +25 -0
index 7c7964c33ae7..7e69ceed12c0 100644
@@ -259,6 +259,7 @@ EXPORT_SYMBOL(jiffies_to_msecs);
 
 unsigned int jiffies_to_usecs(const unsigned long j)
 {
+	WARN_ON_ONCE(j > UINT_MAX * HZ / USEC_PER_SEC);
 #if HZ <= USEC_PER_SEC && !(USEC_PER_SEC % HZ)
 	return (USEC_PER_SEC / HZ) * j;
 #elif HZ > USEC_PER_SEC && !(HZ % USEC_PER_SEC)
@@ -273,6 +274,30 @@ unsigned int jiffies_to_usecs(const unsigned long j)
 }
 EXPORT_SYMBOL(jiffies_to_usecs);
 
+u64 jiffies_to_nsecs(const u64 jl)
+{
+	/* can we do this with 32-bit math? */
+	if (jl < (u64)UINT_MAX * HZ / NSEC_PER_SEC) {
+		unsigned long j = jl;
+#if !(NSEC_PER_SEC % HZ)
+		return (NSEC_PER_SEC / HZ) * j;
+#else
+# if BITS_PER_LONG == 32
+		return (HZ_TO_NSEC_MUL32 * j) >> HZ_TO_NSEC_SHR32;
+# else
+		return (j * HZ_TO_NSEC_NUM) / HZ_TO_NSEC_DEN;
+# endif
+#endif
+	} else {
+#if !(NSEC_PER_SEC % HZ)
+		return (NSEC_PER_SEC / HZ) * jl;
+#else
+		return (jl * HZ_TO_NSEC_NUM) / HZ_TO_NSEC_DEN;
+#endif
+	}
+}
+EXPORT_SYMBOL(jiffies_to_nsecs);
+
 /**
  * timespec_trunc - Truncate timespec to a granularity
  * @t: Timespec
kernel/timeconst.bc +12 -0
index 511bdf2cafda..6f6e8b285c2b 100644
@@ -100,6 +100,18 @@ define timeconst(hz) {
 		print "#define USEC_TO_HZ_DEN\t\t", 1000000/cd, "\n"
 		print "\n"
 
+                s=fmuls(32,1000000000,hz)
+                obase=16
+                print "#define HZ_TO_NSEC_MUL32\tU64_C(0x", fmul(s,1000000000,hz), ")\n"
+                obase=10
+                print "#define HZ_TO_NSEC_SHR32\t", s, "\n"
+
+                obase=10
+                cd=gcd(hz,1000000000)
+                print "#define HZ_TO_NSEC_NUM\t\t", 1000000000/cd, "\n"
+                print "#define HZ_TO_NSEC_DEN\t\t", hz/cd, "\n"
+                print "\n"
+
 		print "#endif /* KERNEL_TIMECONST_H */\n"
 	}
 	halt
kernel/trace/trace_clock.c +1 -1
index 26dc348332b7..52470fba1d26 100644
@@ -65,7 +65,7 @@ u64 notrace trace_clock_jiffies(void)
 	u64 jiffy = jiffies - INITIAL_JIFFIES;
 
 	/* Return nsecs */
-	return (u64)jiffies_to_usecs(jiffy) * 1000ULL;
+	return jiffies_to_nsecs(jiffy);
 }
 
 /*
Signature · 1 line
1.8.4.1
#10 lore raw

Re: [PATCH] time: Provide full featured jiffies_to_nsecs() function

In reply to Tony Luck

2014-04-09 1:32 GMT+08:00 Tony Luck <[email protected]>:

Quoted 13 lines

The "uptime" tracer added in:
commit 8aacf017b065a805d27467843490c976835eb4a5
tracing: Add "uptime" trace clock that uses jiffies
has wraparound problems when the system has been up more than 1 hour 11 minutes and 34 seconds. It converts jiffies to nanoseconds using:

        (u64)jiffies_to_usecs(jiffy) * 1000ULL

but since jiffies_to_usecs() only returns a 32-bit value, it truncates at 2^32 microseconds. An additional problem on 32-bit systems is that the argument is "unsigned long", so fixing the return value only helps until 2^32 jiffies (49.7 days on a HZ=1000 system).

Quoted 5 lines

So we provide a full featured jiffies_to_nsec() function that takes a "u64" argument and provides a "u64" result. To avoid cries of rage from the other user of this: scheduler_tick_max_deferment() we check whether the argument is small enough that we can do the calculations in 32-bit operations.

Hi Tony,
How about get uptime by get_monotonic_boottime() directly, which's the same as /proc/uptime.

Then trace_clock_jiffies() would be like bellow:

u64 notrace trace_clock_jiffies(void)

        struct timespec uptime;
        get_monotonic_boottime(&uptime);

        return uptime.tv_sec * NSEC_PER_SEC + uptime.tv_nsec;
}

--
XiuQi

Quoted 8 lines
---
 include/linux/jiffies.h    |  6 +-----
 kernel/time.c              | 24 ++++++++++++++++++++++++
 kernel/timeconst.bc        | 12 ++++++++++++
 kernel/trace/trace_clock.c |  2 +-
 4 files changed, 38 insertions(+), 6 deletions(-)

...

author
#11 lore raw

RE: [PATCH] time: Provide full featured jiffies_to_nsecs() function

Quoted 2 lines

How about get uptime by get_monotonic_boottime() directly, which's the same as /proc/uptime.

I don't know. get_monotonic_boottime() had existed for many releases at the point the uptime trace clock was added - so it was an available choice. Maybe not noticed by

Is this function safe to call in every context (including NMI & machine check)? [it uses read_seqcount_begin/read_seqcount_retry ... which I *think* is safe ... but this stuff is tricky, so I'd like some reassurance].

Mauro, Steven: Did we just do math on jiffies because we wanted less overhead in a tracepoint?

Bigger question (mostly for Mauro) ... what was the motivation for the "uptime" tracer to begin with? The rasdaemon code that is using it converts the times from traces into absolute times (by adding an offset it computes by comparing uptime and gettimeofday() when it starts). But this would seem to be fraught with problems:
1) Do we get this right for events that happen in daylight saving time shift windows?
2) Is there a "drift" problem for systems that stay up for months and rely on ntp to keep wall clock time in line with reality?

-Tony ���{.n�+�������+%�����ݶ��w��{.n�+����{��G�����{ay�ʇڙ�,j��f���h��������z_�(�階�ݢj"���m������G������?����&���~��iO��z��v�^�m���� ������?�I�

maintainer
#12 lore raw

Re: [PATCH] time: Provide full featured jiffies_to_nsecs() function

Quoted 3 lines · Tony

Is this function safe to call in every context (including NMI & machine check)? [it uses read_seqcount_begin/read_seqcount_retry ... which I *think* is safe ... but this stuff is tricky, so I'd like some reassurance].

No, read_seqcount_begin() is not safe in NMI context. If it interrupts a write, it goes into an infinite spin (see __read_seqcount_begin()).

Quoted 3 lines

Mauro, Steven: Did we just do math on jiffies because we wanted less overhead in a tracepoint?

As I meantioned. read_seqcount_begin() is not safe for tracing, it had to be reimplemented.

-- Steve

Quoted 11 lines

Bigger question (mostly for Mauro) ... what was the motivation for the "uptime" tracer to begin with? The rasdaemon code that is using it converts the times from traces into absolute times (by adding an offset it computes by comparing uptime and gettimeofday() when it starts). But this would seem to be fraught with problems:
1) Do we get this right for events that happen in daylight saving time shift windows?
2) Is there a "drift" problem for systems that stay up for months and rely on ntp to keep wall clock time in line with reality?

-Tony

#13 lore raw

Re: [PATCH] time: Provide full featured jiffies_to_nsecs() function

In reply to Tony Luck
Quoted 27 lines · Tony Luck

The "uptime" tracer added in:
commit 8aacf017b065a805d27467843490c976835eb4a5
tracing: Add "uptime" trace clock that uses jiffies
has wraparound problems when the system has been up more than 1 hour 11 minutes and 34 seconds. It converts jiffies to nanoseconds using:

	(u64)jiffies_to_usecs(jiffy) * 1000ULL

but since jiffies_to_usecs() only returns a 32-bit value, it truncates at 2^32 microseconds. An additional problem on 32-bit systems is that the argument is "unsigned long", so fixing the return value only helps until 2^32 jiffies (49.7 days on a HZ=1000 system).

So we provide a full featured jiffies_to_nsec() function that takes a "u64" argument and provides a "u64" result. To avoid cries of rage from the other user of this: scheduler_tick_max_deferment() we check whether the argument is small enough that we can do the calculations in 32-bit operations.

---
 include/linux/jiffies.h    |  6 +-----
 kernel/time.c              | 24 ++++++++++++++++++++++++
 kernel/timeconst.bc        | 12 ++++++++++++
 kernel/trace/trace_clock.c |  2 +-
 4 files changed, 38 insertions(+), 6 deletions(-)

[...]

kernel/trace/trace_clock.c u64 notrace trace_clock_jiffies(void) · line 65

Quoted 6 lines
kernel/trace/trace_clock.c +0 -0
index 26dc348332b7..52470fba1d26 100644
@@ -65,7 +65,7 @@ u64 notrace trace_clock_jiffies(void)
 	u64 jiffy = jiffies - INITIAL_JIFFIES;

Hi Tony and Steven,
Another problem, maybe we should use get_jiffies_64() instead of jiffies directly here OR we'll meet wraparound problems on 32bit system. But a same problem is that the jiffies_lock is not safe in NMI context...

Quoted 8 lines
kernel/trace/trace_clock.c +1 -1
 	/* Return nsecs */
-	return (u64)jiffies_to_usecs(jiffy) * 1000ULL;
+	return jiffies_to_nsecs(jiffy);
 }
 
 /*
 
author
#14 lore raw

RE: [PATCH] time: Provide full featured jiffies_to_nsecs() function

Quoted 3 lines

Another problem, maybe we should use get_jiffies_64() instead of jiffies directly here OR we'll meet wraparound problems on 32bit system. But a same problem is that the jiffies_lock is not safe in NMI context...

To quote Winnie the Pooh - "Bother".

Can someone think of a clever way to get a sane 64 bit jiffie value on 32-bit systems (even when we get an NMI in the middle of updating the 64-bit jiffie)?

-Tony

#15 lore raw

Re: [PATCH] time: Provide full featured jiffies_to_nsecs() function

Quoted 9 lines · Tony
Quoted 3 lines

Another problem, maybe we should use get_jiffies_64() instead of jiffies directly here OR we'll meet wraparound problems on 32bit system. But a same problem is that the jiffies_lock is not safe in NMI context...

To quote Winnie the Pooh - "Bother".

Can someone think of a clever way to get a sane 64 bit jiffie value on 32-bit systems (even when we get an NMI in the middle of updating the 64-bit jiffie)?

Hi Tony & Steven,

I've sent a patch to solve this problem, please help to review, thanks.

	http://lkml.org/lkml/2014/6/28/41
	tracing: fix uptime overflow problem

Thanks,
XiuQi