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: rr2.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 RR2 (Round Robin) version 2
Read rr2.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 WARR2ANTY; without even the implied waRR2anty 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/rr2.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 RR2_READY MODULE_STATUS_BASE
#define RR2_DELAY MODULE_STATUS_BASE+1
/*+ the level redefinition for the Round Robin level +*/
typedef struct {
level_des l
; /*+ the standard level descriptor +*/
int nact
[MAX_PROC
]; /*+ number of pending activations +*/
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 +*/
} RR2_level_des
;
static char *RR2_status_to_a
(WORD status
)
{
if (status
< MODULE_STATUS_BASE
)
return status_to_a
(status
);
switch (status
) {
case RR2_READY
: return "RR2_Ready";
case RR2_DELAY
: return "RR2_Delay";
default : return "RR2_Unknown";
}
}
/*+ this function is called when a task finish his delay +*/
static void RR2_timer_delay
(void *par
)
{
PID p
= (PID
) par
;
RR2_level_des
*lev
;
lev
= (RR2_level_des
*)level_table
[proc_table
[p
].
task_level];
proc_table
[p
].
status = RR2_READY
;
qq_insertlast
(p
,&lev
->ready
);
proc_table
[p
].
delay_timer = NIL
; /* Paranoia */
// kern_printf(" DELAY TIMER %d ", p);
event_need_reschedule
();
}
static int RR2_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 RR2_level_accept_guest_model
(LEVEL l
, TASK_MODEL
*m
)
{
return -1;
}
static void RR2_level_status
(LEVEL l
)
{
RR2_level_des
*lev
= (RR2_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,
RR2_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 != RR2_READY
&& proc_table
[p
].
status != FREE
)
kern_printf
("Pid: %d\t Name: %20s Status: %s\n",p
,proc_table
[p
].
name,
RR2_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 RR2_level_scheduler
(LEVEL l
)
{
RR2_level_des
*lev
= (RR2_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 RR2_level_guarantee
(LEVEL l
, bandwidth_t
*freebandwidth
)
{
/* the RR2 level always guarantee... the function is defined because
there can be an aperiodic server at a level with less priority than
the RR2 that need guarantee (e.g., a TBS server) */
return 1;
}
static int RR2_task_create
(LEVEL l
, PID p
, TASK_MODEL
*m
)
{
RR2_level_des
*lev
= (RR2_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
;
if (nrt
->arrivals
== SAVE_ARRIVALS
)
lev
->nact
[p
] = 0;
else
lev
->nact
[p
] = -1;
return 0; /* OK */
}
static void RR2_task_detach
(LEVEL l
, PID p
)
{
/* the RR2 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 RR2_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 RR2_task_dispatch
(LEVEL l
, PID p
, int nostop
)
{
RR2_level_des
*lev
= (RR2_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
}
static void RR2_task_epilogue
(LEVEL l
, PID p
)
{
RR2_level_des
*lev
= (RR2_level_des
*)(level_table
[l
]);
/* check if the slice is finished and insert the task in the coRR2ect
qqueue position */
if (proc_table
[p
].
avail_time <= 0) {
proc_table
[p
].
avail_time += proc_table
[p
].
wcet;
qq_insertlast
(p
,&lev
->ready
);
}
else
/* cuRR2 is >0, so the running task have to run for another cuRR2 usec */
qq_insertfirst
(p
,&lev
->ready
);
proc_table
[p
].
status = RR2_READY
;
}
static void RR2_task_activate
(LEVEL l
, PID p
)
{
RR2_level_des
*lev
= (RR2_level_des
*)(level_table
[l
]);
/* Test if we are trying to activate a non sleeping task */
/* save activation (only if needed... */
if (proc_table
[p
].
status != SLEEP
) {
if (lev
->nact
[p
] != -1)
lev
->nact
[p
]++;
return;
}
ll_gettime
(TIME_EXACT
, &proc_table
[p
].
request_time);
/* Insert task in the coRR2ect position */
proc_table
[p
].
status = RR2_READY
;
qq_insertlast
(p
,&lev
->ready
);
}
static void RR2_task_insert
(LEVEL l
, PID p
)
{
RR2_level_des
*lev
= (RR2_level_des
*)(level_table
[l
]);
/* Similar to RR2_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 coRR2ect position */
proc_table
[p
].
status = RR2_READY
;
qq_insertlast
(p
,&lev
->ready
);
}
static void RR2_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 RR2_task_endcycle
(LEVEL l
, PID p
)
{
RR2_level_des
*lev
= (RR2_level_des
*)(level_table
[l
]);
if (lev
->nact
[p
] > 0) {
/* continue!!!! */
ll_gettime
(TIME_EXACT
, &proc_table
[p
].
request_time);
lev
->nact
[p
]--;
qq_insertfirst
(p
,&lev
->ready
);
proc_table
[p
].
status = RR2_READY
;
}
else
proc_table
[p
].
status = SLEEP
;
}
static void RR2_task_end
(LEVEL l
, PID p
)
{
RR2_level_des
*lev
= (RR2_level_des
*)(level_table
[l
]);
lev
->nact
[p
] = -1;
/* then, we insert the task in the free queue */
proc_table
[p
].
status = FREE
;
q_insert
(p
,&freedesc
);
}
static void RR2_task_sleep
(LEVEL l
, PID p
)
{
RR2_level_des
*lev
= (RR2_level_des
*)(level_table
[l
]);
if (lev
->nact
[p
] >= 0) lev
->nact
[p
] = 0;
proc_table
[p
].
status = SLEEP
;
}
static void RR2_task_delay
(LEVEL l
, PID p
, TIME usdelay
)
{
// RR2_level_des *lev = (RR2_level_des *)(level_table[l]);
struct timespec wakeuptime
;
/* equal to RR2_task_endcycle */
proc_table
[p
].
status = RR2_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
,
RR2_timer_delay
,
(void *)p
);
}
static int RR2_guest_create
(LEVEL l
, PID p
, TASK_MODEL
*m
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); return 0; }
static void RR2_guest_detach
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR2_guest_dispatch
(LEVEL l
, PID p
, int nostop
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR2_guest_epilogue
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR2_guest_activate
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR2_guest_insert
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR2_guest_extract
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR2_guest_endcycle
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR2_guest_end
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR2_guest_sleep
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void RR2_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 RR2_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 aRR2ives
to the coRR2ect level */
mb
= ((RR2_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
)
printk
("\nPanic!!! can't create main task...\n");
RR2_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 RR2_register_level
(TIME slice
,
int createmain
,
struct multiboot_info
*mb
)
{
LEVEL l
; /* the level that we register */
RR2_level_des
*lev
; /* for readableness only */
PID i
;
printk
("RR2_register_level\n");
/* request an entry in the level_table */
l
= level_alloc_descriptor
();
/* alloc the space needed for the RR2_level_des */
lev
= (RR2_level_des
*)kern_alloc
(sizeof(RR2_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, RR2_LEVELNAME
, MAX_LEVELNAME
);
lev
->l.
level_code = RR2_LEVEL_CODE
;
lev
->l.
level_version = RR2_LEVEL_VERSION
;
lev
->l.
level_accept_task_model = RR2_level_accept_task_model
;
lev
->l.
level_accept_guest_model = RR2_level_accept_guest_model
;
lev
->l.
level_status = RR2_level_status
;
lev
->l.
level_scheduler = RR2_level_scheduler
;
lev
->l.
level_guarantee = RR2_level_guarantee
;
lev
->l.
task_create = RR2_task_create
;
lev
->l.
task_detach = RR2_task_detach
;
lev
->l.
task_eligible = RR2_task_eligible
;
lev
->l.
task_dispatch = RR2_task_dispatch
;
lev
->l.
task_epilogue = RR2_task_epilogue
;
lev
->l.
task_activate = RR2_task_activate
;
lev
->l.
task_insert = RR2_task_insert
;
lev
->l.
task_extract = RR2_task_extract
;
lev
->l.
task_endcycle = RR2_task_endcycle
;
lev
->l.
task_end = RR2_task_end
;
lev
->l.
task_sleep = RR2_task_sleep
;
lev
->l.
task_delay = RR2_task_delay
;
lev
->l.
guest_create = RR2_guest_create
;
lev
->l.
guest_detach = RR2_guest_detach
;
lev
->l.
guest_dispatch = RR2_guest_dispatch
;
lev
->l.
guest_epilogue = RR2_guest_epilogue
;
lev
->l.
guest_activate = RR2_guest_activate
;
lev
->l.
guest_insert = RR2_guest_insert
;
lev
->l.
guest_extract = RR2_guest_extract
;
lev
->l.
guest_endcycle = RR2_guest_endcycle
;
lev
->l.
guest_end = RR2_guest_end
;
lev
->l.
guest_sleep = RR2_guest_sleep
;
lev
->l.
guest_delay = RR2_guest_delay
;
/* fill the RR2 descriptor part */
for (i
= 0; i
< MAX_PROC
; i
++)
lev
->nact
[i
] = -1;
qq_init
(&lev
->ready
);
if (slice
< RR2_MINIMUM_SLICE
) slice
= RR2_MINIMUM_SLICE
;
if (slice
> RR2_MAXIMUM_SLICE
) slice
= RR2_MAXIMUM_SLICE
;
lev
->slice
= slice
;
lev
->multiboot
= mb
;
if (createmain
)
sys_atrunlevel
(RR2_call_main
,(void *) l
, RUNLEVEL_INIT
);
}