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: posix.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 compatible with POSIX
specifications
Read posix.h for further details.
RR tasks have the CONTROL_CAP bit set
**/
/*
* 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/posix.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 POSIX_READY MODULE_STATUS_BASE
#define POSIX_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 array +*/
int slice
; /*+ the level's time slice +*/
struct multiboot_info
*multiboot
; /*+ used if the level have to insert
the main task +*/
int maxpriority
; /*+ the priority are from 0 to maxpriority
(i.e 0 to 31) +*/
int yielding
; /*+ equal to 1 when a sched_yield is called +*/
} POSIX_level_des
;
static char *POSIX_status_to_a
(WORD status
)
{
if (status
< MODULE_STATUS_BASE
)
return status_to_a
(status
);
switch (status
) {
case POSIX_READY
: return "POSIX_Ready";
case POSIX_DELAY
: return "POSIX_Delay";
default : return "POSIX_Unknown";
}
}
/*+ this function is called when a task finish his delay +*/
static void POSIX_timer_delay
(void *par
)
{
PID p
= (PID
) par
;
POSIX_level_des
*lev
;
lev
= (POSIX_level_des
*)level_table
[proc_table
[p
].
task_level];
proc_table
[p
].
status = POSIX_READY
;
qq_insertlast
(p
,&lev
->ready
[proc_table
[p
].
priority]);
proc_table
[p
].
delay_timer = NIL
; /* Paranoia */
// kern_printf(" DELAY TIMER %d ", p);
event_need_reschedule
();
}
static int POSIX_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 POSIX_level_accept_guest_model
(LEVEL l
, TASK_MODEL
*m
)
{
return -1;
}
static void POSIX_level_status
(LEVEL l
)
{
POSIX_level_des
*lev
= (POSIX_level_des
*)(level_table
[l
]);
PID p
;
kern_printf
("Slice: %d \n", lev
->slice
);
for (p
=0; p
<MAX_PROC
; p
++)
if (proc_table
[p
].
task_level == l
&& proc_table
[p
].
status != POSIX_READY
&& proc_table
[p
].
status != FREE
)
kern_printf
("Pid: %d\t Name: %20s Prio: %3ld Status: %s\n",
p
,proc_table
[p
].
name,
proc_table
[p
].
priority,
POSIX_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 POSIX_level_scheduler
(LEVEL l
)
{
POSIX_level_des
*lev
= (POSIX_level_des
*)(level_table
[l
]);
PID p
;
int prio
;
prio
= lev
->maxpriority
;
for (;;) {
p
= qq_queryfirst
(&lev
->ready
[prio
]);
if (p
== NIL
) {
if (prio
) {
prio
--;
continue;
}
else
return NIL
;
}
if ((proc_table
[p
].
control & CONTROL_CAP
) &&
(proc_table
[p
].
avail_time <= 0)) {
proc_table
[p
].
avail_time += proc_table
[p
].
wcet;
qq_extract
(p
,&lev
->ready
[prio
]);
qq_insertlast
(p
,&lev
->ready
[prio
]);
}
else
return p
;
}
}
static int POSIX_level_guarantee
(LEVEL l
, bandwidth_t
*freebandwidth
)
{
/* the POSIX level always guarantee... the function is defined because
there can be an aperiodic server at a level with less priority than
the POSIX that need guarantee (e.g., a TBS server) */
return 1;
}
static int POSIX_task_create
(LEVEL l
, PID p
, TASK_MODEL
*m
)
{
POSIX_level_des
*lev
= (POSIX_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 */
/* I used the wcet field because using wcet can account if a task
consume more than the timeslice... */
if (nrt
->inherit
== NRT_INHERIT_SCHED
&&
proc_table
[exec_shadow
].
task_level == l
) {
/* We inherit the scheduling properties if the scheduling level
*is* the same */
proc_table
[p
].
priority = proc_table
[exec_shadow
].
priority;
proc_table
[p
].
avail_time = proc_table
[exec_shadow
].
avail_time;
proc_table
[p
].
wcet = proc_table
[exec_shadow
].
wcet;
proc_table
[p
].
control = (proc_table
[p
].
control & ~CONTROL_CAP
) |
(proc_table
[exec_shadow
].
control & CONTROL_CAP
);
lev
->nact
[p
] = (lev
->nact
[exec_shadow
] == -1) ? -1 : 0;
}
else {
proc_table
[p
].
priority = nrt
->weight
;
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
;
}
if (nrt
->policy
== NRT_RR_POLICY
)
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 POSIX_task_detach
(LEVEL l
, PID p
)
{
/* the POSIX 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 POSIX_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 POSIX_task_dispatch
(LEVEL l
, PID p
, int nostop
)
{
POSIX_level_des
*lev
= (POSIX_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
[proc_table
[p
].
priority]);
#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 POSIX_task_epilogue
(LEVEL l
, PID p
)
{
POSIX_level_des
*lev
= (POSIX_level_des
*)(level_table
[l
]);
if (lev
->yielding
) {
lev
->yielding
= 0;
qq_insertlast
(p
,&lev
->ready
[proc_table
[p
].
priority]);
}
/* check if the slice is finished and insert the task in the coPOSIXect
qqueue position */
else if (proc_table
[p
].
control & CONTROL_CAP
&&
proc_table
[p
].
avail_time <= 0) {
proc_table
[p
].
avail_time += proc_table
[p
].
wcet;
qq_insertlast
(p
,&lev
->ready
[proc_table
[p
].
priority]);
}
else
qq_insertfirst
(p
,&lev
->ready
[proc_table
[p
].
priority]);
proc_table
[p
].
status = POSIX_READY
;
}
static void POSIX_task_activate
(LEVEL l
, PID p
)
{
POSIX_level_des
*lev
= (POSIX_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 correct position */
proc_table
[p
].
status = POSIX_READY
;
qq_insertlast
(p
,&lev
->ready
[proc_table
[p
].
priority]);
}
static void POSIX_task_insert
(LEVEL l
, PID p
)
{
POSIX_level_des
*lev
= (POSIX_level_des
*)(level_table
[l
]);
/* Similar to POSIX_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 coPOSIXect position */
proc_table
[p
].
status = POSIX_READY
;
qq_insertlast
(p
,&lev
->ready
[proc_table
[p
].
priority]);
}
static void POSIX_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 POSIX_task_endcycle
(LEVEL l
, PID p
)
{
POSIX_level_des
*lev
= (POSIX_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
].
priority]);
proc_table
[p
].
status = POSIX_READY
;
}
else
proc_table
[p
].
status = SLEEP
;
}
static void POSIX_task_end
(LEVEL l
, PID p
)
{
POSIX_level_des
*lev
= (POSIX_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 POSIX_task_sleep
(LEVEL l
, PID p
)
{
POSIX_level_des
*lev
= (POSIX_level_des
*)(level_table
[l
]);
lev
->nact
[p
] = 0;
proc_table
[p
].
status = SLEEP
;
}
static void POSIX_task_delay
(LEVEL l
, PID p
, TIME usdelay
)
{
// POSIX_level_des *lev = (POSIX_level_des *)(level_table[l]);
struct timespec wakeuptime
;
/* equal to POSIX_task_endcycle */
proc_table
[p
].
status = POSIX_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
,
POSIX_timer_delay
,
(void *)p
);
}
static int POSIX_guest_create
(LEVEL l
, PID p
, TASK_MODEL
*m
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); return 0; }
static void POSIX_guest_detach
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void POSIX_guest_dispatch
(LEVEL l
, PID p
, int nostop
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void POSIX_guest_epilogue
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void POSIX_guest_activate
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void POSIX_guest_insert
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void POSIX_guest_extract
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void POSIX_guest_endcycle
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void POSIX_guest_end
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void POSIX_guest_sleep
(LEVEL l
, PID p
)
{ kern_raise
(XUNVALID_GUEST
,exec_shadow
); }
static void POSIX_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 POSIX_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 aPOSIXives
to the coPOSIXect level */
mb
= ((POSIX_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
);
nrt_task_def_weight
(m
,0);
nrt_task_def_policy
(m
,NRT_RR_POLICY
);
nrt_task_def_inherit
(m
,NRT_EXPLICIT_SCHED
);
p
= task_create
("Main", __init__
, (TASK_MODEL
*)&m
, NULL
);
if (p
== NIL
)
printk
("\nPanic!!! can't create main task...\n");
POSIX_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 POSIX_register_level
(TIME slice
,
int createmain
,
struct multiboot_info
*mb
,
int prioritylevels
)
{
LEVEL l
; /* the level that we register */
POSIX_level_des
*lev
; /* for readableness only */
PID i
; /* a counter */
int x
; /* a counter */
printk
("POSIX_register_level\n");
/* request an entry in the level_table */
l
= level_alloc_descriptor
();
printk
(" alloco descrittore %d %d\n",l
,(int)sizeof(POSIX_level_des
));
/* alloc the space needed for the POSIX_level_des */
lev
= (POSIX_level_des
*)kern_alloc
(sizeof(POSIX_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, POSIX_LEVELNAME
, MAX_LEVELNAME
);
lev
->l.
level_code = POSIX_LEVEL_CODE
;
lev
->l.
level_version = POSIX_LEVEL_VERSION
;
lev
->l.
level_accept_task_model = POSIX_level_accept_task_model
;
lev
->l.
level_accept_guest_model = POSIX_level_accept_guest_model
;
lev
->l.
level_status = POSIX_level_status
;
lev
->l.
level_scheduler = POSIX_level_scheduler
;
lev
->l.
level_guarantee = POSIX_level_guarantee
;
lev
->l.
task_create = POSIX_task_create
;
lev
->l.
task_detach = POSIX_task_detach
;
lev
->l.
task_eligible = POSIX_task_eligible
;
lev
->l.
task_dispatch = POSIX_task_dispatch
;
lev
->l.
task_epilogue = POSIX_task_epilogue
;
lev
->l.
task_activate = POSIX_task_activate
;
lev
->l.
task_insert = POSIX_task_insert
;
lev
->l.
task_extract = POSIX_task_extract
;
lev
->l.
task_endcycle = POSIX_task_endcycle
;
lev
->l.
task_end = POSIX_task_end
;
lev
->l.
task_sleep = POSIX_task_sleep
;
lev
->l.
task_delay = POSIX_task_delay
;
lev
->l.
guest_create = POSIX_guest_create
;
lev
->l.
guest_detach = POSIX_guest_detach
;
lev
->l.
guest_dispatch = POSIX_guest_dispatch
;
lev
->l.
guest_epilogue = POSIX_guest_epilogue
;
lev
->l.
guest_activate = POSIX_guest_activate
;
lev
->l.
guest_insert = POSIX_guest_insert
;
lev
->l.
guest_extract = POSIX_guest_extract
;
lev
->l.
guest_endcycle = POSIX_guest_endcycle
;
lev
->l.
guest_end = POSIX_guest_end
;
lev
->l.
guest_sleep = POSIX_guest_sleep
;
lev
->l.
guest_delay = POSIX_guest_delay
;
/* fill the POSIX descriptor part */
for (i
= 0; i
< MAX_PROC
; i
++)
lev
->nact
[i
] = -1;
lev
->maxpriority
= prioritylevels
-1;
lev
->ready
= (QQUEUE
*)kern_alloc
(sizeof(QQUEUE
) * prioritylevels
);
for (x
= 0; x
< prioritylevels
; x
++)
qq_init
(&lev
->ready
[x
]);
if (slice
< POSIX_MINIMUM_SLICE
) slice
= POSIX_MINIMUM_SLICE
;
if (slice
> POSIX_MAXIMUM_SLICE
) slice
= POSIX_MAXIMUM_SLICE
;
lev
->slice
= slice
;
lev
->multiboot
= mb
;
if (createmain
)
sys_atrunlevel
(POSIX_call_main
,(void *) l
, RUNLEVEL_INIT
);
}
/*+ this function forces the running task to go to his queue tail;
(it works only on the POSIX level) +*/
int POSIX_sched_yield
(LEVEL l
)
{
POSIX_level_des
*lev
= (POSIX_level_des
*)(level_table
[l
]);
if (l
< 0 || l
>= sched_levels
)
return -1;
if (level_table
[l
]->level_code
!= POSIX_LEVEL_CODE
||
level_table
[l
]->level_version
!= POSIX_LEVEL_VERSION
)
return -1;
if (proc_table
[exec_shadow
].
task_level != l
)
return -1;
proc_table
[exec_shadow
].
context = kern_context_save
();
lev
->yielding
= 1;
scheduler
();
kern_context_load
(proc_table
[exec_shadow
].
context);
return 0;
}
/*+ this function returns the maximum level allowed for the POSIX level +*/
int POSIX_get_priority_max
(LEVEL l
)
{
POSIX_level_des
*lev
= (POSIX_level_des
*)(level_table
[l
]);
return lev
->maxpriority
;
}
/*+ this function returns the default timeslice for the POSIX level +*/
int POSIX_rr_get_interval
(LEVEL l
)
{
POSIX_level_des
*lev
= (POSIX_level_des
*)(level_table
[l
]);
return lev
->slice
;
}
/*+ this functions returns some paramaters of a task;
policy must be NRT_RR_POLICY or NRT_FIFO_POLICY;
priority must be in the range [0..prioritylevels]
returns ENOSYS or ESRCH if there are problems +*/
int POSIX_getschedparam
(LEVEL l
, PID p
, int *policy
, int *priority
)
{
if (l
< 0 || l
>= sched_levels
)
return ENOSYS
;
if (level_table
[l
]->level_code
!= POSIX_LEVEL_CODE
||
level_table
[l
]->level_version
!= POSIX_LEVEL_VERSION
)
return ENOSYS
;
if (p
<0 || p
>= MAX_PROC
|| proc_table
[p
].
status == FREE
)
return ESRCH
;
if (proc_table
[p
].
task_level != l
)
return ENOSYS
;
if (proc_table
[p
].
control & CONTROL_CAP
)
*policy
= NRT_RR_POLICY
;
else
*policy
= NRT_FIFO_POLICY
;
*priority
= proc_table
[p
].
priority;
return 0;
}
/*+ this functions sets paramaters of a task +*/
int POSIX_setschedparam
(LEVEL l
, PID p
, int policy
, int priority
)
{
POSIX_level_des
*lev
= (POSIX_level_des
*)(level_table
[l
]);
if (l
< 0 || l
>= sched_levels
)
return ENOSYS
;
if (level_table
[l
]->level_code
!= POSIX_LEVEL_CODE
||
level_table
[l
]->level_version
!= POSIX_LEVEL_VERSION
)
return ENOSYS
;
if (p
<0 || p
>= MAX_PROC
|| proc_table
[p
].
status == FREE
)
return ESRCH
;
if (proc_table
[p
].
task_level != l
)
return ENOSYS
;
if (policy
== SCHED_RR
)
proc_table
[p
].
control |= CONTROL_CAP
;
else if (policy
== SCHED_FIFO
)
proc_table
[p
].
control &= ~CONTROL_CAP
;
else
return EINVAL
;
if (proc_table
[p
].
priority != priority
) {
if (proc_table
[p
].
status == POSIX_READY
) {
qq_extract
(p
,&lev
->ready
[proc_table
[p
].
priority]);
proc_table
[p
].
priority = priority
;
qq_insertlast
(p
,&lev
->ready
[priority
]);
}
else
proc_table
[p
].
priority = priority
;
}
return 0;
}