Subversion Repositories shark

Rev

Rev 38 | 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: rm.c,v 1.5 2003-05-05 07:31:44 pj Exp $

 File:        $File$
 Revision:    $Revision: 1.5 $
 Last update: $Date: 2003-05-05 07:31:44 $
 ------------

 This file contains the scheduling module RM (Rate Monotonic)

 Read rm.h for further details.

 This file is equal to EDF.c except for:

 . EDF changed to RM :-)
 . q_timespec_insert changed to q_insert
 . proc_table[p].priority is also modified when we modify lev->period[p]


**/


/*
 * 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/rm.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 <kernel/trace.h>

/*+ Status used in the level +*/
#define RM_READY         MODULE_STATUS_BASE    /*+ - Ready status        +*/
#define RM_WCET_VIOLATED MODULE_STATUS_BASE+2  /*+ when wcet is finished +*/
#define RM_WAIT          MODULE_STATUS_BASE+3  /*+ to wait the deadline  +*/
#define RM_IDLE          MODULE_STATUS_BASE+4  /*+ to wait the deadline  +*/
#define RM_ZOMBIE        MODULE_STATUS_BASE+5  /*+ to wait the free time +*/

/*+ flags +*/
#define RM_FLAG_SPORADIC    1
#define RM_FLAG_NORAISEEXC  2

/*+ the level redefinition for the Rate Monotonic +*/
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                     +*/

} RM_level_des;


static void RM_timer_deadline(void *par)
{
  PID p = (PID) par;
  RM_level_des *lev;
  struct timespec *temp;

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

  switch (proc_table[p].status) {
    case RM_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 RM_IDLE:
      /* tracer stuff */
      trc_logevent(TRC_INTACTIVATION,&p);
      /* similar to RM_task_activate */
      temp = iq_query_timespec(p, &lev->ready);
      ADDUSEC2TIMESPEC(lev->period[p], temp);
      proc_table[p].status = RM_READY;
      iq_priority_insert(p,&lev->ready);
      lev->deadline_timer[p] = kern_event_post(temp,
                                               RM_timer_deadline,
                                               (void *)p);
      //printk("(d%d idle priority set to %d)",p,proc_table[p].priority );
      event_need_reschedule();
      printk("el%d|",p);
      break;

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

    default:
      /* else, a deadline miss occurred!!! */
      kern_printf("timer_deadline:AAARRRGGGHHH!!!");
      kern_raise(XDEADLINE_MISS,p);
  }
}

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

  kern_printf("AAARRRGGGHHH!!!");
  kern_raise(XDEADLINE_MISS,p);
}

/* The scheduler only gets the first task in the queue */
static PID RM_public_scheduler(LEVEL l)
{
  RM_level_des *lev = (RM_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 RM_public_guarantee(LEVEL l, bandwidth_t *freebandwidth)
{
  RM_level_des *lev = (RM_level_des *)(level_table[l]);

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

static int RM_public_create(LEVEL l, PID p, TASK_MODEL *m)
{
  RM_level_des *lev = (RM_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;

  /* update the bandwidth... */
  if (lev->flags & RM_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 */

  *iq_query_priority(p, &lev->ready) = lev->period[p] = h->mit;

  if (h->periodicity == APERIODIC)
    lev->flag[p] = RM_FLAG_SPORADIC;
  else
    lev->flag[p] = 0;
  lev->deadline_timer[p] = -1;

  /* Enable wcet check */
  if (lev->flags & RM_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 RM_public_detach(LEVEL l, PID p)
{
  /* the RM level doesn't introduce any dinamic allocated new field.
     we have only to reset the NO_GUARANTEE FIELD and decrement the allocated
     bandwidth */


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

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

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

//  kern_printf("(disp %d)",p);

  /* 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 RM_public_epilogue(LEVEL l, PID p)
{
  RM_level_des *lev = (RM_level_des *)(level_table[l]);

//  kern_printf("(epil %d)",p);

  /* check if the wcet is finished... */
  if ((lev->flags & RM_ENABLE_WCET_CHECK) && proc_table[p].avail_time <= 0) {
    /* if it is, raise a XWCET_VIOLATION exception */
    kern_raise(XWCET_VIOLATION,p);
    proc_table[p].status = RM_WCET_VIOLATED;
  }
  else {
    /* the task has been preempted. it returns into the ready queue... */
    iq_priority_insert(p,&lev->ready);
    proc_table[p].status = RM_READY;
  }
}

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

  if (proc_table[p].status == RM_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 != RM_WCET_VIOLATED)
    return;


  /* see also RM_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 = RM_READY;
  iq_priority_insert(p,&lev->ready);

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

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

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


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

static void RM_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 RM_public_message(LEVEL l, PID p, void *m)
{
  RM_level_des *lev = (RM_level_des *)(level_table[l]);

  /* the task has terminated his job before it consume the wcet. All OK! */
  if (lev->flag[p] & RM_FLAG_SPORADIC)
    proc_table[p].status = RM_WAIT;
  else /* pclass = sporadic_pclass */
    proc_table[p].status = RM_IDLE;

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

  jet_update_endcycle(); /* Update the Jet data... */
  trc_logevent(TRC_ENDCYCLE,&exec_shadow); /* tracer stuff */

  /* when the deadline timer fire, it recognize the situation and set
     correctly all the stuffs (like reactivation, sleep, etc... ) */


  return 0;
}

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

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

}

static void RM_private_insert(LEVEL l, PID p, TASK_MODEL *m)
{
  RM_level_des *lev = (RM_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;

  *iq_query_timespec(p,&lev->ready) = job->deadline;
  *iq_query_priority(p, &lev->ready) = lev->period[p] = job->period;
 
  lev->deadline_timer[p] = -1;

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

  if (job->noraiseexc)
    lev->flag[p] = RM_FLAG_NORAISEEXC;
  else {
    lev->flag[p] = 0;
    lev->deadline_timer[p] = kern_event_post(iq_query_timespec(p, &lev->ready),
                                             RM_timer_guest_deadline,
                                             (void *)p);
  }
}

static void RM_private_dispatch(LEVEL l, PID p, int nostop)
{
  RM_level_des *lev = (RM_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 RM_private_epilogue(LEVEL l, PID p)
{
  RM_level_des *lev = (RM_level_des *)(level_table[l]);

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

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

  //kern_printf("RM_guest_end: dline timer %d\n",lev->deadline_timer[p]);
  if (proc_table[p].status == RM_READY)
  {
    iq_extract(p, &lev->ready);
    //kern_printf("(g_end rdy extr)");
  }

  /* we remove the deadline timer, because the slice is finished */
  if (lev->deadline_timer[p] != NIL) {
//    kern_printf("RM_guest_end: dline timer %d\n",lev->deadline_timer[p]);
    kern_event_delete(lev->deadline_timer[p]);
    lev->deadline_timer[p] = NIL;
  }

}

/* Registration functions */

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

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

  printk("RM_register_level\n");

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

  lev = (RM_level_des *)level_table[l];

  printk("    lev=%d\n",(int)lev);

  /* fill the standard descriptor */
  lev->l.private_insert   = RM_private_insert;
  lev->l.private_extract  = RM_private_extract;
  lev->l.private_dispatch = RM_private_dispatch;
  lev->l.private_epilogue = RM_private_epilogue;

  lev->l.public_scheduler = RM_public_scheduler;
  if (flags & RM_ENABLE_GUARANTEE)
    lev->l.public_guarantee = RM_public_guarantee;
  else
    lev->l.public_guarantee = NULL;

  lev->l.public_create    = RM_public_create;
  lev->l.public_detach    = RM_public_detach;
  lev->l.public_end       = RM_public_end;
  lev->l.public_dispatch  = RM_public_dispatch;
  lev->l.public_epilogue  = RM_public_epilogue;
  lev->l.public_activate  = RM_public_activate;
  lev->l.public_unblock   = RM_public_unblock;
  lev->l.public_block     = RM_public_block;
  lev->l.public_message   = RM_public_message;

  /* fill the RM 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 RM_usedbandwidth(LEVEL l)
{
  RM_level_des *lev = (RM_level_des *)(level_table[l]);

  return lev->U;
}