Chapter 2: Process Management and Scheduling
The following steps are required if preemption is possible:
kernel/sched.c
do {
add_preempt_count(PREEMPT_ACTIVE);
schedule();
sub_preempt_count(PREEMPT_ACTIVE);
/*
* Check again in case we missed a preemption opportunity
* between schedule and now.
*/
} while (unlikely(test_thread_flag(TIF_NEED_RESCHED)));
Before the scheduler is invoked, the value of the preemption counter is set toPREEMPT_ACTIVE.Thissets
a flag bit in the preemption counter that has such a large value that it is never affected by the regular
preemption counter increments as illustrated by Figure 2-30. It indicates to theschedulefunction that
scheduling was not invoked in the normal way but as a result of a kernel preemption. After the kernel
has rescheduled, code flow returns to the current task — possibly after some time has elapsed, because
the preempting task will have run in between — the flag bit is removed again.
Preemption counter
PREEMPT_ACTIVE
Figure 2-30: The per-process preemption
counter.
I ignored the implications of this flag forschedulebefore, so I have to discuss it now. Recall that the
scheduler deactivates a task withdeactivate_taskif it is not in a runnable state at the moment. In fact,
this operation is skipped if the scheduling was initiated via the preemption mechanism as can be seen if
PREEMPT_ACTIVEis set in the preemption counter:
kernel/sched.c
asmlinkage void __sched schedule(void) {
...
if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
if (unlikely((prev->state & TASK_INTERRUPTIBLE) &&
unlikely(signal_pending(prev)))) {
prev->state = TASK_RUNNING;
} else {
deactivate_task(rq, prev, 1);
}
}
...
}
This ensures that the next task is selected as quickly as possible without the hassle of deactivating the
current one. If a high-priority task is waiting to be scheduled, it will be picked by the scheduler class and
will be allowed to run.