Blame |
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: rr.c,v 1.1.1.1 2002-03-29 14:12:52 pj Exp $
File: $File$
Revision: $Revision: 1.1.1.1 $
Last update: $Date: 2002-03-29 14:12:52 $
------------
This file contains the scheduling module RR (Round Robin)
Read rr.h for further details.
**/
/*
* Copyright (C) 2000 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/rr.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>
/*+ Status used in the level +*/
#define RR_READY MODULE_STATUS_BASE
#define RR_DELAY MODULE_STATUS_BASE+1
/*+ the level redefinition for the Round Robin level +*/
typedef struct {
level_des l
; /*+ the standard level descriptor +*/
QQUEUE ready
; /*+ the ready queue +*/
int slice
; /*+ the level's time slice +*/
struct multiboot_info
*multiboot
; /*+ used if the level have to insert
the main task +*/
} RR_level_des
;
static char *RR_status_to_a
(WORD status
)
{
if (status
< MODULE_STATUS_BASE
)
return status_to_a
(status
);
switch (status
) {
case RR_READY
: return "RR_Ready";
case RR_DELAY
: return "RR_Delay";
default : return "RR_Unknown";
}
}
/*+ this function is called when a task finish his delay +*/
static void RR_timer_delay
(void *par
)
{
PID p
= (PID
) par
;
RR_level_des
*lev
;
lev
= (RR_level_des
*)level_table
[proc_table
[p
].
task_level];
proc_table
[p
].
status = RR_READY
;
qq_insertlast
(p
,&lev
->ready
);
proc_table
[p
].
delay_timer = NIL
; /* Paranoia */
// kern_printf(" DELAY TIMER %d ", p);
event_need_reschedule
();
}
static int RR_level_accept_task_model
(LEVEL l
, TASK_MODEL
*m
)
{
if (m
->pclass
== NRT_PCLASS
|| m
->pclass
== (NRT_PCLASS
| l
))
return 0;
else
return -1;
}
static int RR_level_accept_guest_model
(LEVEL l
, TASK_MODEL
*m
)
{
return -1;
}
static void RR_level_status
(LEVEL l
)
{
RR_level_des
*lev
= (RR_level_des
*)(level_table
[l
]);
PID p
= qq_queryfirst
(&lev
->ready
);
kern_printf
("Slice: %d \n", lev
->slice
);
while (p
!= NIL
) {
kern_printf
("Pid: %d\t Name: %20s Status: %s\n",p
,proc_table
[p
].
name,
RR_status_to_a
(proc_table
[p
].
status));
p
= proc_table
[p
].
next;
}
for (p
=0; p
<MAX_PROC
; p
++)
if (proc_table
[p
].
task_level == l
&& proc_table
[p
].
status != RR_READY
&& proc_table
[p
].
status != FREE
)
kern_printf
("Pid: %d\t Name: %20s Status: %s\n",p
,proc_table
[p
].
name,
RR_status_to_a
(proc_table
[p
].
status));
}
/* This is not efficient but very fair :-)
The need of all this stuff is because if a task execute a long time
due to (shadow!) priority inheritance, then the task shall go to the
tail of the queue many times... */
static PID RR_level_scheduler
(LEVEL l
)
{
RR_level_des
*lev
= (RR_level_des
*)(level_table
[l
]);
PID p
;
for (;;) {
p
= qq_queryfirst
(&lev
->ready
);
if (p
== -1)
return p
;
if (proc_table
[p
].
avail_time <= 0) {
proc_table
[p
].
avail_time += proc_table
[p
].
wcet;
qq_extract
(p
,&lev
->ready
);
qq_insertlast
(p
,&lev
->ready
);
}
else
return p
;
}
}
static int RR_level_guarantee
(LEVEL l
, bandwidth_t
*freebandwidth
)
{
/* the RR level always guarantee... the function is defined because
there can be an aperiodic server at a level with less priority than
the RR that need guarantee (e.g., a TBS server) */
return 1;
}
static int RR_task_create
(LEVEL l
, PID p
, TASK_MODEL
*m
)
{
RR_level_des
*lev
= (RR_level_des
*)(level_table
[l
]);
NRT_TASK_MODEL
*nrt
= (NRT_TASK_MODEL
*)m
;
/* the task state is set at SLEEP by the general task_create
the only thing to set remains the capacity stuffs that are set
to the values passed in the model... */
/* I used the wcet field because using wcet can account if a task
consume more than the timeslice... */
if (nrt
->slice
) {
proc_table
[p
].
avail_time = nrt
->slice
;
proc_table
[p
].
wcet = nrt
->slice
;
}
else {
proc_table
[p
].
avail_time = lev
->slice
;
proc_table
[p
].
wcet = lev
->slice
;
}
proc_table
[p
].
control |= CONTROL_CAP
;
return 0; /* OK */
}
static void RR_task_detach
(LEVEL l
, PID p
)
{
/* the RR level doesn't introduce any new field in the TASK_MODEL
so, all detach stuffs are done by the task_create
The task state is set at FREE by the general task_create */
}
static int RR_task_eligible
(LEVEL l
, PID p
)
{
return 0; /* if the task p is chosen, it is always eligible */
}
#ifdef __TEST1__
extern int testactive
;
extern struct timespec s_stime
[];
extern TIME s_curr
[];
extern TIME s_PID
[];
extern int useds
;
#endif
static void RR_task_dispatch
(LEVEL l
, PID p
, int nostop
)
{
RR_level_des
*lev
= (RR_level_des
*)(level_table
[l
]);
/* 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!!! */
qq_extract
(p
, &lev
->ready
);
#ifdef __TEST1__
if (testactive
)
{
TIMESPEC_ASSIGN
(&s_stime
[useds
],&schedule_time
);
s_curr
[useds
] = proc_table
[p
].
avail_time;
s_PID
[useds
] = p
;
useds
++;
}
#endif
// if (nostop) kern_printf("Û");
// kern_printf("(RR d %d)",nostop);
}
static void RR_task_epilogue
(LEVEL l
, PID p
)
{
RR_level_des
*lev
= (RR_level_des
*)(level_table
[l
]);
/* check if the slice is finished and insert the task in the correct
qqueue position */
if (proc_table
[p
].
avail_time <= 0) {
proc_table
[p
].
avail_time += proc_table
[p
].
wcet;
qq_insertlast
(p
,&lev
->ready
);
}
else
/* curr is >0, so the running task have to run for another curr usec */
qq_insertfirst
(p
,&lev
->ready
);
proc_table
[p
].
status = RR_READY
;
}
static void RR_task_activate
(LEVEL l
, PID p
)
{
RR_level_des
*lev
= (RR_level_des
*)(level_table
[l
]);
/* Test if we are trying to activate a non sleeping task */
/* Ignore this; the task is already active */
if (proc_table
[p
].
status != SLEEP
)
return;
ll_gettime
(TIME_EXACT
, &proc_table
[p
].
request_time);
/* Insert task in the correct position */
proc_table
[p
].
status = RR_READY
;
qq_insertlast
(p
,&lev
->ready
);
}
static void RR_task_insert
(LEVEL l
, PID p
)
{
RR_level_des
*lev
= (RR_level_des
*)(level_table
[l
]);
/* Similar to RR_task_activate, but we don't check in what state
the task is and we don't set the request_time */
/* Insert task in the correct position */
proc_table
[p
].
status = RR_READY
;
qq_insertlast
(p
,&lev
->ready
);
}
static void RR_task_extract
(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
So, we do nothing!!!
*/
}
static void RR_task_endcycle
(LEVEL l
, PID p
)
{
// RR_level_des *lev = (RR_level_des *)(level_table[l]);
/* this function is equal to the RR_task_extract, except that
the task fall asleep... */
proc_table
[p
].
status = SLEEP
;
}
static void RR_task_end
(LEVEL l
, PID p
)
{
// RR_level_des *lev = (RR_level_des *)(level_table[l]);
/* we insert the task in the free queue */
proc_table
[p
].
status = FREE
;
q_insert
(p
,&freedesc
);
}
static void RR_task_sleep
(LEVEL l
, PID p
)
{
proc_table
[p
].
status = SLEEP
;
}
static void RR_task_delay
(LEVEL l
, PID p
, TIME usdelay
)
{
// RR_level_des *lev = (RR_level_des *)(level_table[l]);
struct timespec wakeuptime
;
/* equal to RR_task_endcycle */
proc_table
[p
].
status = RR_DELAY
;
/* we need to delete this event if we kill the task while it is sleeping */
ll_gettime
(TIME_EXACT
,&wakeuptime
);
ADDUSEC2TIMESPEC
(usdelay
,&wakeuptime
);
proc_table
[p
].
delay_timer = kern_event_post
(&wakeuptime
,
RR_timer_delay
,
(void *)p
);
}
static int RR_guest_create
(LEVEL l
, PID p
, TASK_MODEL
*m
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); return 0; }
static void RR_guest_detach
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR_guest_dispatch
(LEVEL l
, PID p
, int nostop
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR_guest_epilogue
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR_guest_activate
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR_guest_insert
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR_guest_extract
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR_guest_endcycle
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR_guest_end
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR_guest_sleep
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR_guest_delay
(LEVEL l
, PID p
,DWORD tickdelay
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
/* Registration functions */
/*+ This init function install the "main" task +*/
static void RR_call_main
(void *l
)
{
LEVEL lev
;
PID p
;
NRT_TASK_MODEL m
;
void *mb
;
lev
= (LEVEL
)l
;
nrt_task_default_model
(m
);
nrt_task_def_level
(m
,lev
); /* with this we are sure that the task arrives
to the correct level */
mb
= ((RR_level_des
*)level_table
[lev
])->multiboot
;
nrt_task_def_arg
(m
,mb
);
nrt_task_def_usemath
(m
);
nrt_task_def_nokill
(m
);
nrt_task_def_ctrl_jet
(m
);
p
= task_create
("Main", __init__
, (TASK_MODEL
*)&m
, NULL
);
if (p
== NIL
)
kern_printf
("\nPanic!!! can't create main task... errno =%d\n",errno
);
RR_task_activate
(lev
,p
);
}
/*+ Registration function:
TIME slice the slice for the Round Robin queue
int createmain 1 if the level creates the main task 0 otherwise
struct multiboot_info *mb used if createmain specified +*/
void RR_register_level
(TIME slice
,
int createmain
,
struct multiboot_info
*mb
)
{
LEVEL l
; /* the level that we register */
RR_level_des
*lev
; /* for readableness only */
printk
("RR_register_level\n");
/* request an entry in the level_table */
l
= level_alloc_descriptor
();
/* alloc the space needed for the RR_level_des */
lev
= (RR_level_des
*)kern_alloc
(sizeof(RR_level_des
));
printk
(" lev=%d\n",(int)lev
);
/* update the level_table with the new entry */
level_table
[l
] = (level_des
*)lev
;
/* fill the standard descriptor */
strncpy(lev
->l.
level_name, RR_LEVELNAME
, MAX_LEVELNAME
);
lev
->l.
level_code = RR_LEVEL_CODE
;
lev
->l.
level_version = RR_LEVEL_VERSION
;
lev
->l.
level_accept_task_model = RR_level_accept_task_model
;
lev
->l.
level_accept_guest_model = RR_level_accept_guest_model
;
lev
->l.
level_status = RR_level_status
;
lev
->l.
level_scheduler = RR_level_scheduler
;
lev
->l.
level_guarantee = RR_level_guarantee
;
lev
->l.
task_create = RR_task_create
;
lev
->l.
task_detach = RR_task_detach
;
lev
->l.
task_eligible = RR_task_eligible
;
lev
->l.
task_dispatch = RR_task_dispatch
;
lev
->l.
task_epilogue = RR_task_epilogue
;
lev
->l.
task_activate = RR_task_activate
;
lev
->l.
task_insert = RR_task_insert
;
lev
->l.
task_extract = RR_task_extract
;
lev
->l.
task_endcycle = RR_task_endcycle
;
lev
->l.
task_end = RR_task_end
;
lev
->l.
task_sleep = RR_task_sleep
;
lev
->l.
task_delay = RR_task_delay
;
lev
->l.
guest_create = RR_guest_create
;
lev
->l.
guest_detach = RR_guest_detach
;
lev
->l.
guest_dispatch = RR_guest_dispatch
;
lev
->l.
guest_epilogue = RR_guest_epilogue
;
lev
->l.
guest_activate = RR_guest_activate
;
lev
->l.
guest_insert = RR_guest_insert
;
lev
->l.
guest_extract = RR_guest_extract
;
lev
->l.
guest_endcycle = RR_guest_endcycle
;
lev
->l.
guest_end = RR_guest_end
;
lev
->l.
guest_sleep = RR_guest_sleep
;
lev
->l.
guest_delay = RR_guest_delay
;
/* fill the RR descriptor part */
qq_init
(&lev
->ready
);
if (slice
< RR_MINIMUM_SLICE
) slice
= RR_MINIMUM_SLICE
;
if (slice
> RR_MAXIMUM_SLICE
) slice
= RR_MAXIMUM_SLICE
;
lev
->slice
= slice
;
lev
->multiboot
= mb
;
if (createmain
)
sys_atrunlevel
(RR_call_main
,(void *) l
, RUNLEVEL_INIT
);
}