Add Preemption Checks

The scheduling logic so far selects the next job to be scheduled using EDF priorities whenver the scheduler is invoked. However, the scheduler is not automatically invoked when a new job is released. Thus, it does not yet implement preemptive EDF scheduling. In this step, we are going to rectify this by adding a preemption check callback to the real-time domain local_queues embedded in struct demo_cpu_state.

Preemption Check Callback

The preemption check callback is invoked by the rt_domain_t code whenever a job is transferred from the release queue to the ready queue (i.e., when a future release is processed). Since the callback is invoked from within the rt_domain_t code, the calling thread already holds the ready queue lock.

   1 static int demo_check_for_preemption_on_release(rt_domain_t *local_queues)
   2 {
   3         struct demo_cpu_state *state = container_of(local_queues, struct demo_cpu_state,
   4                                                     local_queues);
   5 
   6         /* Because this is a callback from rt_domain_t we already hold
   7          * the necessary lock for the ready queue.
   8          */
   9 
  10         if (edf_preemption_needed(local_queues, state->scheduled)) {
  11                 preempt_if_preemptable(state->scheduled, state->cpu);
  12                 return 1;
  13         } else
  14                 return 0;
  15 }

The preemption check simply extracts the containing struct demo_cpu_state from the rt_domain_t pointer using Linux's standard macro container_of(). It then checks whether there exists a higher-priority job in the ready queue than what is currently scheduled. If this is the case, then an invocation of the scheduler is triggered with the preempt_if_preemptable() helper function. This LITMUSRT helper function is a wrapper around Linux's preemption mechanism that transparently works for remote cores as well as the local core.

Note that state->scheduled may be NULL; this case is transparently handled by preempt_if_preemptable().

(The ...if_preemptable() suffix of the function refers to non-preemptive section support and is of no relevance to this tutorial.)

Updated Initialization

The preemption check callback must be given to edf_domain_init() during plugin initialization. The updated initialization code looks as follows.

   1 static long demo_activate_plugin(void)
   2 {
   3         int cpu;
   4         struct demo_cpu_state *state;
   5 
   6         for_each_online_cpu(cpu) {
   7                 TRACE("Initializing CPU%d...\n", cpu);
   8 
   9                 state = cpu_state_for(cpu);
  10 
  11                 state->cpu = cpu;
  12                 state->scheduled = NULL;
  13                 edf_domain_init(&state->local_queues,
  14                                 demo_check_for_preemption_on_release,
  15                                 NULL);
  16         }
  17 
  18         return 0;
  19 }

Testing

The DEMO plugin is now a fully working plugin, except for the fact that it still rejects all tasks, which we correct in the next step.