Device drivers often need to delay the execution of a particular piece of code for a period of time—usually to allow the hardware to accomplish some task. In this section we cover a number of different techniques for achieving delays. The circumstances of each situation determine which technique is best to use; we’ll go over them all and point out the advantages and disadvantages of each.
One important thing to consider is whether the length of the needed delay is longer than one clock tick. Longer delays can make use of the system clock; shorter delays typically must be implemented with software loops.
If you want to delay execution by a multiple of the clock tick or you don’t require strict precision (for example, if you want to delay an integer number of seconds), the easiest implementation (and the most braindead) is the following, also known as busy waiting:
unsigned long j = jiffies + jit_delay * HZ; while (jiffies < j) /* nothing */;
This kind of implementation should definitely be avoided. We show it here because on occasion you might want to run this code to understand better the internals of other code.
So let’s look at how this code works. The loop is guaranteed to work
because jiffies
is declared as
volatile
by the kernel headers and therefore is
reread any time some C code accesses it. Though “correct,” this
busy loop completely locks the processor for the duration of the
delay; the scheduler never interrupts a process that is running in
kernel space.
Still worse, if interrupts happen to be disabled when you enter the
loop, jiffies
won’t be updated, and the
while
condition remains true forever. You’ll be
forced to hit the big red button.
This implementation of delaying code is available, like the following
ones, in the jit module. The
/proc/jit*
files created by the module delay a
whole second every time they are read. If you want to test the busy
wait code, you can read /proc/jitbusy
, which
busy-loops for one second whenever its read
method is called; a command such as dd if=/proc/jitbusy
bs=1 delays one second each time it reads a character.
As you may suspect, reading /proc/jitbusy
is
terrible for system performance, because the computer can run other
processes only once a second.
A better solution that allows other processes to run during the time interval is the following, although it can’t be used in hard real-time tasks or other time-critical situations.
while (jiffies < j) schedule();
The variable j
in this example and the following
ones is the value of jiffies
at the expiration of
the delay and is always calculated as just shown for busy waiting.
This loop (which can be tested by reading
/proc/jitsched
) still isn’t optimal. The
system can schedule other tasks; the current process does nothing but
release the CPU, but it remains in the run queue. If it is the only
runnable process, it will actually run (it calls the scheduler, which
selects the same process, which calls the scheduler, which...). In
other words, the load of the machine (the average number of running
processes) will be at least one, and the idle task (process number 0,
also called swapper for historical reasons) will
never run. Though this issue may seem irrelevant, running the idle
task when the computer is idle relieves the processor’s workload,
decreasing its temperature and increasing its lifetime, as well as the
duration of the batteries if the computer happens to be your
laptop. Moreover, since the process is actually executing during the
delay, it will be accounted for all the time it consumes. You can see
this by running time cat /proc/jitsched.
If, instead, the system is very busy, the driver could end up waiting rather longer than expected. Once a process releases the processor with schedule, there are no guarantees that it will get it back anytime soon. If there is an upper bound on the acceptable delay time, calling schedule in this manner is not a safe solution to the driver’s needs.
Despite its drawbacks, the previous loop can provide a quick and dirty way to monitor the workings of a driver. If a bug in your module locks the system solid, adding a small delay after each debugging printk statement ensures that every message you print before the processor hits your nasty bug reaches the system log before the system locks. Without such delays, the messages are correctly printed to the memory buffer, but the system locks before klogd can do its job.
The best way to implement a delay, however, is to ask the kernel to do it for you. There are two ways of setting up short-term timeouts, depending on whether your driver is waiting for other events or not.
If your driver uses a wait queue to wait for some other event, but you also want to be sure it runs within a certain period of time, it can use the timeout versions of the sleep functions, as shown in Section 5.2.1 in Chapter 5:
sleep_on_timeout(wait_queue_head_t *q, unsigned long timeout); interruptible_sleep_on_timeout(wait_queue_head_t *q, unsigned long timeout);
Both versions will sleep on the given wait queue, but will return
within the timeout period (in jiffies) in any case. They thus
implement a bounded sleep that will not go on forever. Note that the
timeout value represents the number of jiffies to wait, not an
absolute time value. Delaying in this manner can be seen in the
implementation of /proc/jitqueue
:
wait_queue_head_t wait; init_waitqueue_head (&wait); interruptible_sleep_on_timeout(&wait, jit_delay*HZ);
In a normal driver, execution could be resumed in either of two ways: somebody calls wake_up on the wait queue, or the timeout expires. In this particular implementation, nobody will ever call wake_up on the wait queue (after all, no other code even knows about it), so the process will always wake up when the timeout expires. That is a perfectly valid implementation, but, if there are no other events of interest to your driver, delays can be achieved in a more straightforward manner with schedule_timeout:
set_current_state(TASK_INTERRUPTIBLE); schedule_timeout (jit_delay*HZ);
The previous line (for /proc/jitself
) causes the
process to sleep until the given time has passed.
schedule_timeout, too, expects a time offset, not
an absolute number of jiffies. Once again, it is worth noting that an
extra time interval could pass between the expiration of the timeout
and when your process is actually scheduled to execute.
Sometimes a real driver needs to calculate very short delays in order
to synchronize with the hardware. In this case, using the
jiffies
value is definitely not the solution.
The kernel functions udelay and mdelay serve this purpose.[27] Their prototypes are
#include <linux/delay.h> void udelay(unsigned long usecs); void mdelay(unsigned long msecs);
The functions are compiled inline on most supported architectures.
The former uses a software loop to delay execution for the required
number of microseconds, and the latter is a loop around
udelay, provided for the convenience of the
programmer. The udelay function is where the
BogoMips value is used: its loop is based on the integer value
loops_per_second
, which in turn is the result of
the BogoMips calculation performed at boot time.
The udelay call should be called only for short
time lapses because the precision of
loops_per_second
is only eight bits, and noticeable
errors accumulate when calculating long delays. Even though the
maximum allowable delay is nearly one second (since calculations
overflow for longer delays), the suggested maximum value for
udelay is 1000 microseconds (one
millisecond). The function mdelay helps in cases
where the delay must be longer than one millisecond.
It’s also important to remember that udelay is a busy-waiting function (and thus mdelay is too); other tasks can’t be run during the time lapse. You must therefore be very careful, especially with mdelay, and avoid using it unless there’s no other way to meet your goal.
Currently, support for delays longer than a few microseconds and shorter than a timer tick is very inefficient. This is not usually an issue, because delays need to be just long enough to be noticed by humans or by the hardware. One hundredth of a second is a suitable precision for human-related time intervals, while one millisecond is a long enough delay for hardware activities.
Although mdelay is not available in Linux 2.0,
sysdep.h
fills the gap.
Get Linux Device Drivers, Second Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.