Subversion Repositories shark

Rev

Rev 445 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Project: S.Ha.R.K.
 *
 * Coordinators:
 *   Giorgio Buttazzo    <giorgio@sssup.it>
 *   Paolo Gai           <pj@gandalf.sssup.it>
 *
 * Authors     :
 *   Paolo Gai           <pj@gandalf.sssup.it>
 *   Massimiliano Giorgi <massy@gandalf.sssup.it>
 *   Luca Abeni          <luca@gandalf.sssup.it>
 *   (see the web pages for full authors list)
 *
 * ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy)
 *
 * http://www.sssup.it
 * http://retis.sssup.it
 * http://shark.sssup.it
 */


/**
 ------------
 CVS :        $Id: edf.c,v 1.13 2004-03-10 14:51:43 giacomo Exp $

 File:        $File$
 Revision:    $Revision: 1.13 $
 Last update: $Date: 2004-03-10 14:51:43 $
 ------------

 This file contains the scheduling module EDF (Earliest Deadline First)

 Read edf.h for further details.

**/


/*
 * Copyright (C) 2000,2002 Paolo Gai
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */



#include <modules/edf.h>
#include <ll/stdio.h>
#include <ll/string.h>
#include <kernel/model.h>
#include <kernel/descr.h>
#include <kernel/var.h>
#include <kernel/func.h>

#include <tracer.h>

//#define EDF_DEBUG
#define edf_printf kern_printf

/*+ Status used in the level +*/
#define EDF_READY         MODULE_STATUS_BASE    /*+ - Ready status        +*/
#define EDF_WCET_VIOLATED MODULE_STATUS_BASE+2  /*+ when wcet is finished +*/
#define EDF_WAIT          MODULE_STATUS_BASE+3  /*+ to wait the deadline  +*/
#define EDF_IDLE          MODULE_STATUS_BASE+4  /*+ to wait the deadline  +*/
#define EDF_ZOMBIE        MODULE_STATUS_BASE+5  /*+ to wait the free time +*/

/*+ flags +*/
#define EDF_FLAG_SPORADIC    1
#define EDF_FLAG_NORAISEEXC  2
#define EDF_FLAG_SLEEP       4

/*+ the level redefinition for the Earliest Deadline First level +*/
typedef struct {
  level_des l;     /*+ the standard level descriptor          +*/

  TIME period[MAX_PROC]; /*+ The task periods; the deadlines are
                       stored in the priority field           +*/

  int deadline_timer[MAX_PROC];
                   /*+ The task deadline timers               +*/

  int flag[MAX_PROC];
                   /*+ used to manage the JOB_TASK_MODEL and the
                       periodicity                            +*/


  IQUEUE ready;     /*+ the ready queue                        +*/

  int flags;       /*+ the init flags...                      +*/

  bandwidth_t U;   /*+ the used bandwidth                     +*/

} EDF_level_des;


static void EDF_timer_deadline(void *par)
{
  PID p = (PID) par;
  EDF_level_des *lev;
  struct timespec *temp;

  lev = (EDF_level_des *)level_table[proc_table[p].task_level];

  #ifdef EDF_DEBUG
    edf_printf("(EDF:Dl TIMER:%d)",p);
  #endif

  switch (proc_table[p].status) {
    case EDF_ZOMBIE:
      /* we finally put the task in the ready queue */
      proc_table[p].status = FREE;
      iq_insertfirst(p,&freedesc);
      /* and free the allocated bandwidth */
      lev->U -= (MAX_BANDWIDTH/lev->period[p]) * proc_table[p].wcet;
      break;

    case EDF_IDLE:
      /* tracer stuff */
      TRACER_LOGEVENT(FTrace_EVT_task_timer,(unsigned short int)proc_table[p].context,(unsigned int)proc_table[p].task_level);
      /* similar to EDF_task_activate */
      temp = iq_query_timespec(p,&lev->ready);
      ADDUSEC2TIMESPEC(lev->period[p], temp);
      proc_table[p].status = EDF_READY;
      iq_timespec_insert(p,&lev->ready);
      lev->deadline_timer[p] = kern_event_post(temp,
                                               EDF_timer_deadline,
                                               (void *)p);
      event_need_reschedule();
      break;

    case EDF_WAIT:
      /* Without this, the task cannot be reactivated!!! */
      proc_table[p].status = SLEEP;

      /* Reset the EDF_FLAG_SLEEP */
      lev->flag[p] &= ~EDF_FLAG_SLEEP;

      break;

    default:
      /* else, a deadline miss occurred!!! */
      TRACER_LOGEVENT(FTrace_EVT_task_deadline_miss,(unsigned short int)proc_table[p].context,0);
      kern_raise(XDEADLINE_MISS,p);
  }
}

static void EDF_timer_guest_deadline(void *par)
{
  PID p = (PID) par;

  #ifdef EDF_DEBUG
    edf_printf("(EDF:AAARRRGGGHHH!!!)");
  #endif
  TRACER_LOGEVENT(FTrace_EVT_task_deadline_miss,(unsigned short int)proc_table[p].context,0);
  kern_raise(XDEADLINE_MISS,p);
}

/* The scheduler only gets the first task in the queue */
static PID EDF_public_scheduler(LEVEL l)
{
  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);
  return iq_query_first(&lev->ready);
}

/* The on-line guarantee is enabled only if the appropriate flag is set... */
static int EDF_public_guarantee(LEVEL l, bandwidth_t *freebandwidth)
{
  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);

  if (*freebandwidth >= lev->U) {
    *freebandwidth -= lev->U;
    return 1;
  }
  else
    return 0;
}

static int EDF_public_create(LEVEL l, PID p, TASK_MODEL *m)
{
  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);
  HARD_TASK_MODEL *h;

  if (m->pclass != HARD_PCLASS) return -1;
  if (m->level != 0 && m->level != l) return -1;
  h = (HARD_TASK_MODEL *)m;
  if (!h->wcet || !h->mit) return -1;

  /* check the free bandwidth... */
  if (lev->flags & EDF_ENABLE_GUARANTEE) {
    bandwidth_t b;
    b = (MAX_BANDWIDTH / h->mit) * h->wcet;

    /* really update lev->U, checking an overflow... */
    if (MAX_BANDWIDTH - lev->U > b)
      lev->U += b;
    else
      return -1;
  }

  /* now we know that m is a valid model */

  #ifdef EDF_DEBUG
    edf_printf("(EDF:PubCrt:%d)", p);
  #endif

  lev->period[p] = h->mit;
 
  lev->flag[p] = 0;

  if (h->periodicity == APERIODIC)
    lev->flag[p] |= EDF_FLAG_SPORADIC;
 
  lev->deadline_timer[p] = -1;

  /* Enable wcet check */
  if (lev->flags & EDF_ENABLE_WCET_CHECK) {
    proc_table[p].avail_time = h->wcet;
    proc_table[p].wcet       = h->wcet;
    proc_table[p].control |= CONTROL_CAP;
  }

  return 0; /* OK, also if the task cannot be guaranteed... */
}

static void EDF_public_detach(LEVEL l, PID p)
{
  /* the EDF level doesn't introduce any dinamic allocated new field.
     we have only to decrement the allocated bandwidth */


  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);

  #ifdef EDF_DEBUG
    edf_printf("(EDF:PubDet:%d)", p);
  #endif

  if (lev->flags & EDF_ENABLE_GUARANTEE) {
    lev->U -= (MAX_BANDWIDTH / lev->period[p]) * proc_table[p].wcet;
  }
}

static void EDF_public_dispatch(LEVEL l, PID p, int nostop)
{
  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);

  #ifdef EDF_DEBUG
    edf_printf("(EDF:PubDsp:%d)",p);
  #endif

  /* the task state is set EXE by the scheduler()
     we extract the task from the ready queue
     NB: we can't assume that p is the first task in the queue!!! */

  iq_extract(p, &lev->ready);
}

static void EDF_public_epilogue(LEVEL l, PID p)
{
  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);

  #ifdef EDF_DEBUG
    edf_printf("(EDF:PubEpi:%d)",p);
  #endif

  /* check if the wcet is finished... */
  if ((lev->flags & EDF_ENABLE_WCET_CHECK) && proc_table[p].avail_time <= 0) {
    /* if it is, raise a XWCET_VIOLATION exception */
    TRACER_LOGEVENT(FTrace_EVT_task_wcet_violation,(unsigned short int)proc_table[p].context,0);
    kern_raise(XWCET_VIOLATION,p);
    proc_table[p].status = EDF_WCET_VIOLATED;
  }
  else {
    /* the task has been preempted. it returns into the ready queue... */
    iq_timespec_insert(p,&lev->ready);
    proc_table[p].status = EDF_READY;
  }
}

static void EDF_public_activate(LEVEL l, PID p)
{
  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);
  struct timespec *temp;

  #ifdef EDF_DEBUG
    edf_printf("(EDF:PubAct:%d)", p);
  #endif

  if (lev->flag[p] & EDF_FLAG_SLEEP) {
    lev->flag[p] &= ~EDF_FLAG_SLEEP;
    if (!(lev->flag[p] & EDF_FLAG_SPORADIC))
      proc_table[p].status = EDF_IDLE;
    return;
  }

  if (proc_table[p].status == EDF_WAIT) {
    kern_raise(XACTIVATION,p);
    return;
  }
 
  /* Test if we are trying to activate a non sleeping task    */
  /* Ignore this; the task is already active                  */
  if (proc_table[p].status != SLEEP &&
      proc_table[p].status != EDF_WCET_VIOLATED)
    return;


  /* see also EDF_timer_deadline */
  temp = iq_query_timespec(p, &lev->ready);
  kern_gettime(temp);
  ADDUSEC2TIMESPEC(lev->period[p], temp);

  /* Insert task in the correct position */
  proc_table[p].status = EDF_READY;
  iq_timespec_insert(p,&lev->ready);

  /* Set the deadline timer */
  lev->deadline_timer[p] = kern_event_post(temp,
                                           EDF_timer_deadline,
                                           (void *)p);
}

static void EDF_public_unblock(LEVEL l, PID p)
{
  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);

  /* Similar to EDF_task_activate,
     but we don't check in what state the task is */


  /* Insert task in the coEDFect position */
  proc_table[p].status = EDF_READY;
  iq_timespec_insert(p,&lev->ready);
}

static void EDF_public_block(LEVEL l, PID p)
{
  /* Extract the running task from the level
     . we have already extract it from the ready queue at the dispatch time.
     . the capacity event have to be removed by the generic kernel
     . the wcet don't need modification...
     . the state of the task is set by the calling function
     . the deadline must remain...

     So, we do nothing!!!
  */

}

static int EDF_public_message(LEVEL l, PID p, void *m)
{
  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);

  /* task_message evaluation */
  switch((long)(m)) {

    /* task_endcycle */
    case (long)(NULL):

      #ifdef EDF_DEBUG
        edf_printf("(EDF:EndCyc:%d)",p);
      #endif

      /* the task has terminated his job before it consume the wcet. All OK! */
      if (!(lev->flag[p] & EDF_FLAG_SPORADIC) &&
          !(lev->flag[p] & EDF_FLAG_SLEEP))
        proc_table[p].status = EDF_IDLE;
      else
        proc_table[p].status = EDF_WAIT;

      /* we reset the capacity counters... */
      if (lev->flags & EDF_ENABLE_WCET_CHECK)
        proc_table[p].avail_time = proc_table[p].wcet;

      jet_update_endcycle(); /* Update the Jet data... */
      TRACER_LOGEVENT(FTrace_EVT_task_end_cycle,(unsigned short int)proc_table[p].context,(unsigned int)l);

      break;

    /* task_disable */
    case 1:

      #ifdef EDF_DEBUG
        edf_printf("(EDF:Dis:%d)",p);
      #endif

      /* Set the EDF_FLAG_SLEEP, in the next endcycle the task will
         be set in EDF_WAIT */

      lev->flag[p] |= EDF_FLAG_SLEEP;

      /* If the task is EDF_IDLE, set to EDF_WAIT now */
      if (proc_table[p].status == EDF_IDLE)
        proc_table[p].status = EDF_WAIT;

      TRACER_LOGEVENT(FTrace_EVT_task_disable,(unsigned short int)proc_table[p].context,(unsigned int)l);

      break;

  }

  return 0;

}

static void EDF_public_end(LEVEL l, PID p)
{
  proc_table[p].status = EDF_ZOMBIE;

  /* When the deadline timer fire, it put the task descriptor in
     the free queue, and free the allocated bandwidth... */

}

static void EDF_private_insert(LEVEL l, PID p, TASK_MODEL *m)
{
  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);
  JOB_TASK_MODEL *job;

  if (m->pclass != JOB_PCLASS || (m->level != 0 && m->level != l) ) {
    kern_raise(XINVALID_TASK, p);
    return;
  }

  job = (JOB_TASK_MODEL *)m;

  /* Insert task in the correct position */
  *iq_query_timespec(p, &lev->ready) = job->deadline;
  iq_timespec_insert(p,&lev->ready);
  proc_table[p].status = EDF_READY;
 
  lev->deadline_timer[p] = -1;

  lev->period[p] = job->period;

  /* Set the deadline timer */
  if (job->noraiseexc)
    lev->flag[p] = EDF_FLAG_NORAISEEXC;
  else {
    lev->flag[p] = 0;
    lev->deadline_timer[p] = kern_event_post(iq_query_timespec(p, &lev->ready),
                                             EDF_timer_guest_deadline,
                                             (void *)p);
  }
}

static void EDF_private_dispatch(LEVEL l, PID p, int nostop)
{
  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);

  /* the task state is set to EXE by the scheduler()
     we extract the task from the ready queue
     NB: we can't assume that p is the first task in the queue!!! */

  iq_extract(p, &lev->ready);
}

static void EDF_private_epilogue(LEVEL l, PID p)
{
  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);

  /* the task has been preempted. it returns into the ready queue... */
  iq_timespec_insert(p,&lev->ready);
  proc_table[p].status = EDF_READY;
}

static void EDF_private_extract(LEVEL l, PID p)
{
  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);

  if (proc_table[p].status == EDF_READY)
    iq_extract(p, &lev->ready);

  /* we remove the deadline timer, because the slice is finished */
  if (lev->deadline_timer[p] != NIL) {
    kern_event_delete(lev->deadline_timer[p]);
    lev->deadline_timer[p] = NIL;
  }

}


/* Registration functions */

/*+ Registration function:
    int flags                 the init flags ... see edf.h +*/

LEVEL EDF_register_level(int flags)
{
  LEVEL l;            /* the level that we register */
  EDF_level_des *lev;  /* for readableness only */
  PID i;              /* a counter */

  printk("EDF_register_level\n");

  /* request an entry in the level_table */
  l = level_alloc_descriptor(sizeof(EDF_level_des));

  lev = (EDF_level_des *)level_table[l];

  /* fill the standard descriptor */
  lev->l.private_insert   = EDF_private_insert;
  lev->l.private_extract  = EDF_private_extract;
  lev->l.private_dispatch = EDF_private_dispatch;
  lev->l.private_epilogue = EDF_private_epilogue;

  lev->l.public_scheduler = EDF_public_scheduler;
  if (flags & EDF_ENABLE_GUARANTEE)
    lev->l.public_guarantee = EDF_public_guarantee;
  else
    lev->l.public_guarantee = NULL;

  lev->l.public_create    = EDF_public_create;
  lev->l.public_detach    = EDF_public_detach;
  lev->l.public_end       = EDF_public_end;
  lev->l.public_dispatch  = EDF_public_dispatch;
  lev->l.public_epilogue  = EDF_public_epilogue;
  lev->l.public_activate  = EDF_public_activate;
  lev->l.public_unblock   = EDF_public_unblock;
  lev->l.public_block     = EDF_public_block;
  lev->l.public_message   = EDF_public_message;

  /* fill the EDF descriptor part */
  for(i=0; i<MAX_PROC; i++) {
    lev->period[i]         = 0;
    lev->deadline_timer[i] = -1;
    lev->flag[i]          = 0;
  }

  iq_init(&lev->ready, &freedesc, 0);
  lev->flags = flags;
  lev->U     = 0;

  return l;
}

bandwidth_t EDF_usedbandwidth(LEVEL l)
{
  EDF_level_des *lev = (EDF_level_des *)(level_table[l]);

  return lev->U;
}