Task queues and timing issues have remained relatively constant over the years. Nonetheless, a few things have changed and must be kept in mind.
The functions sleep_on_timeout,
interruptible_sleep_on_timeout, and
schedule_timeout were all added for the 2.2
kernel. In the 2.0 days, timeouts were handled with a variable
(called timeout
) in the task structure. As a
result, code that now makes a call like
interruptible_sleep_on_timeout(my_queue, timeout);
used to be implemented as
current->timeout = jiffies + timeout; interruptible_sleep_on(my_queue);
The sysdep.h
header recreates
schedule_timeout for pre-2.4 kernels so that you
can use the new syntax and run on 2.0 and 2.2:
extern inline void schedule_timeout(int timeout) { current->timeout = jiffies + timeout; current->state = TASK_INTERRUPTIBLE; schedule(); current->timeout = 0; }
In 2.0, there were a couple of additional functions for putting
functions into task queues. queue_task_irq could
be called instead of queue_task in situations in
which interrupts were disabled, yielding a (very) small performance
benefit. queue_task_irq_off is even faster, but
does not function properly in situations in which the task is already
queued or is running, and can thus only be used where those conditions
are guaranteed not to occur. Neither of these two functions provided
much in the way of performance benefits, and they were removed in
kernel 2.1.30. Using queue_task in all cases
works with all kernel versions. (It is worth noting, though, that
queue_task had a return type of
void
in 2.2 and prior kernels.)
Prior to 2.4, the schedule_task function and
associated keventd process did not exist.
Instead, another predefined task queue,
tq_scheduler
, was provided. Tasks placed in
tq_scheduler
were run in the
schedule function, and thus always ran in process
context. The actual process whose context would be used was always
different, however; it was whatever process was being scheduled on
the CPU at the time. tq_scheduler
typically had
larger latencies, especially for tasks that resubmitted themselves.
sysdep.h
provides the following implementation
for schedule_task on 2.0 and 2.2 systems:
extern inline int schedule_task(struct tq_struct *task) { queue_task(task, &tq_scheduler); return 1; }
As has been mentioned, the 2.3 development series added the tasklet
mechanism; before, only task queues were available for “immediate
deferred” execution. The bottom-half subsystem was implemented
differently, though most of the changes are not visible to driver
writers. We didn’t emulate tasklets for older kernels in
sysdep.h
because they are not strictly needed for
driver operation; if you want to be backward compatible you’ll need to
either write your own emulation or use task queues instead.
The in_interrupt function did not exist in Linux
2.0. Instead, a global variable intr_count
kept
track of the number of interrupt handlers running. Querying
intr_count
is semantically the same as calling
in_interrupt, so compatibility is easily
implemented in sysdep.h
.
The del_timer_sync function did not exist prior
to development kernel 2.4.0-test2. The usual
sysdep.h
header defines a minimal replacement
when you build against older kernel headers. Kernel version 2.0 didn’t
have mod_timer, either. This gap is also filled
by our compatibility header.