Add P-EDF Scheduling Logic

In this step, we are going to define how the DEMO plugin selects the task that should be scheduled next. Remember that we are implementing P-EDF: we can therefore use the helper function edf_preemption_needed(), declared in litmus/edf_common.h, to determine when the previous task prev should be preempted.

Headers

Since the scheduling logic deals with job parameters and execution budgets, we need to add two additional header files.

   1 #include <litmus/jobs.h>
   2 #include <litmus/budget.h>
   3 

Helper Functions

Before diving into the scheduling function, we are going to define two helper functions that will aid in preventing the schedule() implementation from becoming too convoluted.

The first helper, demo_job_completion() is called to process a job when it is complete. It delegates most of the work to the common helper prepare_for_next_period(), which is declared in litmus/jobs.h.

   1 /* this helper is called when task `prev` exhausted its budget of when
   2  * it signaled a job completion */
   3 static void demo_job_completion(struct task_struct *prev, int buget_exhausted)
   4 {
   5         set_rt_flags(prev, RT_F_SLEEP);
   6 
   7         /* call common helper code to compute the next release time, deadline,
   8          * etc. */
   9         prepare_for_next_period(prev);
  10 }

Next, we add the demo_requeue() helper function. It is used to place a task on the appropriate queue. Depending on whether the task has a pending job, it is placed either in the (core-local) ready queue (if it has a pending job) or in the (also core-local) release queue (if the next job's earliest release time is in the future).

   1 /* Add the task `tsk` to the appropriate queue. Assumes caller holds the ready lock.
   2  */
   3 static void demo_requeue(struct task_struct *tsk, struct demo_cpu_state *cpu_state)
   4 {
   5         set_rt_flags(tsk, RT_F_RUNNING);
   6 
   7         if (is_released(tsk, litmus_clock())) {
   8                 /* Uses __add_ready() instead of add_ready() because we already
   9                  * hold the ready lock. */
  10                 __add_ready(&cpu_state->local_queues, tsk);
  11         } else {
  12                 /* Uses add_release() because we DON'T have the release lock. */
  13                 add_release(&cpu_state->local_queues, tsk);
  14         }
  15 }

Note that demo_requeue() uses __add_ready(), but not __add_release(). This is because demo_requeue() will be called only from contexts where the calling thread already holds the lock for the ready queue.

Scheduling Logic