How does linux handle overflow in jiffies?

Suppose we have the following code:

if (timeout > jiffies)
{
    /* we did not time out, good ... */
}
else
{
    /* we timed out, error ...*
}

This code works fine when the jiffies value does not overflow. However, when jiffies overflow and wrap to zero, this code does not work correctly.

Linux seems to provide macros to solve this overflow problem

#define time_before(unknown, known) ((long)(unkown) - (long)(known) < 0)

and above, the code should be safe against overflow when replaced with this macro:

// SAFE AGAINST OVERFLOW
if (time_before(jiffies, timeout)
{
    /* we did not time out, good ... */
}
else
{
    /* we timed out, error ...*
}    

But what is the reasonable value of time_before (and other time_ macros

time_before (jiffies, timeout) will be expanded to

((long)(jiffies) - (long)(timeout) < 0)

How does this code prevent overflow problems?

+5
source share
3 answers

Let's try a try:

#define time_before(unknown, known) ((long)(unkown) - (long)(known) < 0)

, , long - , [0, 0xFFFF].

, [0, 0xFFFF] [0, 0x7FFF], [0x8000, 0xFFFF]. [0, 32767], [-32768, -1]. :

[0x0      -              -                  -               0xFFFF]
[0x0                       0x7FFF][0x8000                   0xFFFF]
[0                         32,767][-32,768                      -1]

timeout - 32 000. , -, , jiffies -31,000. , jiffies < timeout, True. , :

   time_before(jiffies, offset)
== ((long)(jiffies) - (long)(offset) < 0)
== (-31000 - 32000 < 0)             // WTF is this. Clearly NOT -63000
== (-31000 - 1768 - 1 - 30231 < 0)  // simply expanded 32000
== (-32768 - 1 - 30232 < 0)         // this -1 causes an underflow
== (32767 - 30232 < 0)
== (2535 < 0)
== False

jiffies - 4 , 2, . ?

+5

. : http://fixunix.com/kernel/266713-%5Bpatch-1-4%5D-fs-autofs-use-time_before-time_before_eq-etc.html

time_before. ?

, time_after .. :

/linux/jiffies.h: 93

 93 /*
 94  *      These inlines deal with timer wrapping correctly. You are 
 95  *      strongly encouraged to use them
 96  *      1. Because people otherwise forget
 97  *      2. Because if the timer wrap changes in future you won't have to
 98  *         alter your driver code.
 99  *
100  * time_after(a,b) returns true if the time a is after time b.
101  *

, time_before time_after - .

, timeout < jiffles ( ), timeout > jiffles ( ):

unsigned long jiffies = 2147483658;
unsigned long timeout = 10;

unsigned long timeout = -2146483000;

?

printf("%d",time_before(jiffies,timeout));

printf("%d",time_before(jiffies,old_jiffles+timeout));

old_jiffles - jiffles .

, , time_before :

old_jiffles=jiffles;
timeout=10;  // or even 10*HZ for ten-seconds

do_a_long_work_or_wait();

//check is the timeout reached or not
if(time_before(jiffies,old_jiffles+timeout) ) {
  do_another_long_work_or_wait();
} else {
  printk("ERRROR: the timeout is reached; here is a problem");
  panic();
}
+3

, jiffies - , ( ), ( , ). , .

, timeout jiffies + some_offset - - , . , , , .

, jiffies 16- ( ):

timeout > jiffies

, true, . :

  • timeout == 0x0300, jiffies == 0x0100: , .
  • timeout == 0x8100, jiffies == 0x7F00: , .
  • timeout == 0x0100, jiffies == 0xFF00: oops, result is false, , .
  • timeout == 0x0100, jiffies == 0x0300: , .
  • timeout == 0x7F00, jiffies == 0x8100: , .
  • timeout == 0xFF00, jiffies == 0x0100: oops, , .

time_before (jiffies, timeout)

, , , , true, . :

  • timeout == 0x0300, jiffies == 0x0100: , .
  • timeout == 0x8100, jiffies == 0x7F00: , .
  • timeout == 0x0100, jiffies == 0xFF00: , .
  • timeout == 0x0100, jiffies == 0x0300: , .
  • timeout == 0x7F00, jiffies == 0x8100: , .
  • timeout == 0xFF00, jiffies == 0x0100: , .

, timeout, timeout, . . timeout , , time_before , false , , true , 0x8000 ( , ). , -, ( -).

The real kernel is jiffieslonger than 16 bits, so it will take longer to transfer it, but this is still possible if the machine is running long enough. (And, as a rule, it starts shortly after loading to catch these errors faster.)

0
source

All Articles