From 990e73cba7fc82ed9990f90899214769cd6257d1 Mon Sep 17 00:00:00 2001 From: "sd386@font.cl.cam.ac.uk" Date: Mon, 17 Jan 2005 13:38:54 +0000 Subject: [PATCH 1/9] bitkeeper revision 1.1159.221.1 (41ebbfeezjfpGp20MD6f6Xl2r6Wd3Q) Added new simple EDF scheduler --- .rootkeys | 2 + tools/libxc/Makefile | 1 + tools/libxc/xc.h | 8 + tools/libxc/xc_sedf.c | 45 ++ tools/python/xen/lowlevel/xc/xc.c | 64 +++ tools/python/xen/xend/XendClient.py | 6 + tools/python/xen/xend/XendDomain.py | 18 + tools/python/xen/xend/server/SrvDomain.py | 8 + tools/python/xen/xm/main.py | 17 + xen/common/sched_sedf.c | 505 ++++++++++++++++++++++ xen/common/schedule.c | 4 +- xen/include/public/sched_ctl.h | 10 + 12 files changed, 687 insertions(+), 1 deletion(-) create mode 100644 tools/libxc/xc_sedf.c create mode 100644 xen/common/sched_sedf.c diff --git a/.rootkeys b/.rootkeys index 1691f867c4..7b301acebb 100644 --- a/.rootkeys +++ b/.rootkeys @@ -336,6 +336,7 @@ 3fbba6dctWRWlFJkYb6hdix2X4WMuw tools/libxc/xc_private.c 3fbba6dcbVrG2hPzEzwdeV_UC8kydQ tools/libxc/xc_private.h 40589968UQFnJeOMn8UIFLbXBuwXjw tools/libxc/xc_rrobin.c +41ebbfe9U0b0kI-HgjK7VEY4EvW7_w tools/libxc/xc_sedf.c 40e1b09dMYB4ItGCqcMIzirdMd9I-w tools/libxutil/Makefile 40e033325Sjqs-_4TuzeUEprP_gYFg tools/libxutil/allocate.c 40e03332KYz7o1bn2MG_KPbBlyoIMA tools/libxutil/allocate.h @@ -715,6 +716,7 @@ 4064773cJ31vZt-zhbSoxqft1Jaw0w xen/common/sched_atropos.c 40589968dD2D1aejwSOvrROg7fOvGQ xen/common/sched_bvt.c 40589968be_t_n0-w6ggceW7h-sx0w xen/common/sched_rrobin.c +41ebbfe9oF1BF3cH5v7yE3eOL9uPbA xen/common/sched_sedf.c 3e397e6619PgAfBbw2XFbXkewvUWgw xen/common/schedule.c 3ddb79bdB9RNMnkQnUyZ5C9hhMSQQw xen/common/slab.c 3ddb79bd0gVQYmL2zvuJnldvD0AGxQ xen/common/softirq.c diff --git a/tools/libxc/Makefile b/tools/libxc/Makefile index 78ba7f3bf3..87c8c337ad 100644 --- a/tools/libxc/Makefile +++ b/tools/libxc/Makefile @@ -12,6 +12,7 @@ vpath %c $(XEN_LIBXUTIL) INCLUDES += -I $(XEN_LIBXUTIL) SRCS := +SRCS += xc_sedf.c SRCS += xc_atropos.c SRCS += xc_bvtsched.c SRCS += xc_domain.c diff --git a/tools/libxc/xc.h b/tools/libxc/xc.h index 7778b71673..badfbb375d 100644 --- a/tools/libxc/xc.h +++ b/tools/libxc/xc.h @@ -132,6 +132,14 @@ int xc_rrobin_global_set(int xc_handle, u64 slice); int xc_rrobin_global_get(int xc_handle, u64 *slice); +int xc_sedf_domain_set(int xc_handle, + u32 domid, + u64 period, u64 slice); + +int xc_sedf_domain_get(int xc_handle, + u32 domid, + u64* period, u64 *slice); + typedef evtchn_status_t xc_evtchn_status_t; int xc_evtchn_alloc_unbound(int xc_handle, u32 dom, diff --git a/tools/libxc/xc_sedf.c b/tools/libxc/xc_sedf.c new file mode 100644 index 0000000000..92c052fe23 --- /dev/null +++ b/tools/libxc/xc_sedf.c @@ -0,0 +1,45 @@ +/****************************************************************************** + * xc_sedf.c + * + * API for manipulating parameters of the Simple EDF scheduler. + * + * changes by Stephan Diestelhorst + * based on code + * by Mark Williamson, Copyright (c) 2004 Intel Research Cambridge. + */ + +#include "xc_private.h" + +int xc_sedf_domain_set(int xc_handle, + u32 domid, u64 period, u64 slice) +{ + dom0_op_t op; + struct sedf_adjdom *p = &op.u.adjustdom.u.sedf; + + op.cmd = DOM0_ADJUSTDOM; + op.u.adjustdom.domain = (domid_t)domid; + op.u.adjustdom.sched_id = SCHED_SEDF; + op.u.adjustdom.direction = SCHED_INFO_PUT; + + p->period = period; + p->slice = slice; + return do_dom0_op(xc_handle, &op); +} + +int xc_sedf_domain_get(int xc_handle, u32 domid, u64 *period, u64 *slice) +{ + dom0_op_t op; + int ret; + struct sedf_adjdom *p = &op.u.adjustdom.u.sedf; + + op.cmd = DOM0_ADJUSTDOM; + op.u.adjustdom.domain = (domid_t)domid; + op.u.adjustdom.sched_id = SCHED_SEDF; + op.u.adjustdom.direction = SCHED_INFO_GET; + + ret = do_dom0_op(xc_handle, &op); + + *period = p->period; + *slice = p->slice; + return ret; +} diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c index 2dd8680b4f..55fabf9683 100644 --- a/tools/python/xen/lowlevel/xc/xc.c +++ b/tools/python/xen/lowlevel/xc/xc.c @@ -758,6 +758,50 @@ static PyObject *pyxc_rrobin_global_set(PyObject *self, return zero; } +static PyObject *pyxc_sedf_domain_set(PyObject *self, + PyObject *args, + PyObject *kwds) +{ + XcObject *xc = (XcObject *)self; + u32 domid; + u64 period, slice; + + static char *kwd_list[] = { "dom", "period", "slice", NULL }; + + if( !PyArg_ParseTupleAndKeywords(args, kwds, "iLL", kwd_list, &domid, + &period, &slice) ) + return NULL; + + if ( xc_sedf_domain_set(xc->xc_handle, domid, period, slice) != 0 ) + return PyErr_SetFromErrno(xc_error); + + Py_INCREF(zero); + return zero; +} + +static PyObject *pyxc_sedf_domain_get(PyObject *self, + PyObject *args, + PyObject *kwds) +{ + XcObject *xc = (XcObject *)self; + u32 domid; + u64 period, slice; + + static char *kwd_list[] = { "dom", NULL }; + + if( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &domid) ) + return NULL; + + if ( xc_sedf_domain_get( xc->xc_handle, domid, &period, + &slice) ) + return PyErr_SetFromErrno(xc_error); + + return Py_BuildValue("{s:i,s:L,s:L}", + "domain", domid, + "period", period, + "slice", slice); +} + static PyObject *pyxc_shadow_control(PyObject *self, PyObject *args, PyObject *kwds) @@ -984,6 +1028,26 @@ static PyMethodDef pyxc_methods[] = { "Get Round Robin scheduler settings\n" "Returns [dict]:\n" " slice [long]: Scheduler time slice.\n" }, + + { "sedf_domain_set", + (PyCFunction)pyxc_sedf_domain_set, + METH_KEYWORDS, "\n" + "Set the scheduling parameters for a domain when running with Atropos.\n" + " dom [int]: domain to set\n" + " period [long]: domain's scheduling period\n" + " slice [long]: domain's slice per period\n" + "Returns: [int] 0 on success; -1 on error.\n" }, + + { "sedf_domain_get", + (PyCFunction)pyxc_sedf_domain_get, + METH_KEYWORDS, "\n" + "Get the current scheduling parameters for a domain when running with\n" + "the Atropos scheduler." + " dom [int]: domain to query\n" + "Returns: [dict]\n" + " domain [int]: domain ID\n" + " period [long]: scheduler period\n" + " slice [long]: CPU reservation per period\n"}, { "evtchn_alloc_unbound", (PyCFunction)pyxc_evtchn_alloc_unbound, diff --git a/tools/python/xen/xend/XendClient.py b/tools/python/xen/xend/XendClient.py index 8290165412..0d5ac106f2 100644 --- a/tools/python/xen/xend/XendClient.py +++ b/tools/python/xen/xend/XendClient.py @@ -272,6 +272,12 @@ class Xend: 'latency' : latency, 'xtratime': xtratime }) + def xend_domain_cpu_sedf_set(self, id, period, slice): + return self.xendPost(self.domainurl(id), + {'op' : 'cpu_sedf_set', + 'period' : period, + 'slice' : slice }) + def xend_domain_maxmem_set(self, id, memory): return self.xendPost(self.domainurl(id), { 'op' : 'maxmem_set', diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index fc53b4de03..fc5fba1efe 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -659,6 +659,24 @@ class XendDomain: return xc.atropos_domain_get(dominfo.dom) except Exception, ex: raise XendError(str(ex)) + + def domain_cpu_sedf_set(self, id, period, slice): + """Set Atropos scheduler parameters for a domain. + """ + dominfo = self.domain_lookup(id) + try: + return xc.sedf_domain_set(dominfo.dom, period, slice) + except Exception, ex: + raise XendError(str(ex)) + + def domain_cpu_sedf_get(self, id): + """Get Atropos scheduler parameters for a domain. + """ + dominfo = self.domain_lookup(id) + try: + return xc.sedf_domain_get(dominfo.dom) + except Exception, ex: + raise XendError(str(ex)) def domain_device_create(self, id, devconfig): """Create a new device for a domain. diff --git a/tools/python/xen/xend/server/SrvDomain.py b/tools/python/xen/xend/server/SrvDomain.py index c06b260ce1..46868f859c 100644 --- a/tools/python/xen/xend/server/SrvDomain.py +++ b/tools/python/xen/xend/server/SrvDomain.py @@ -132,6 +132,14 @@ class SrvDomain(SrvDir): ['xtratime', 'int']]) val = fn(req.args, {'dom': self.dom.id}) return val + + def op_cpu_sedf_set(self, op, req): + fn = FormFn(self.xd.domain_cpu_sedf_set, + [['dom', 'str'], + ['period', 'int'], + ['slice', 'int']]) + val = fn(req.args, {'dom': self.dom.id}) + return val def op_maxmem_set(self, op, req): fn = FormFn(self.xd.domain_maxmem_set, diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py index e7454e507a..845e475a9c 100644 --- a/tools/python/xen/xm/main.py +++ b/tools/python/xen/xm/main.py @@ -587,6 +587,23 @@ class ProgRrobin(Prog): xm.prog(ProgRrobin) +class ProgSedf(Prog): + group = 'scheduler' + name= "sedf" + info = """Set simple EDF parameters.""" + + def help(self, args): + print args[0], "DOM PERIOD SLICE" + print "\nSet simple EDF parameters." + + def main(self, args): + if len(args) != 4: self.err("%s: Invalid argument(s)" % args[0]) + dom = args[1] + v = map(int, args[2:4]) + server.xend_domain_cpu_sedf_set(dom, *v) + +xm.prog(ProgSedf) + class ProgInfo(Prog): group = 'host' name = "info" diff --git a/xen/common/sched_sedf.c b/xen/common/sched_sedf.c new file mode 100644 index 0000000000..a85e112970 --- /dev/null +++ b/xen/common/sched_sedf.c @@ -0,0 +1,505 @@ +/**************************************************************************** + * Simple EDF scheduler for xen + * + * by Stephan Diestelhorst (C) 2004 Cambridge University + * based on code by Mark Williamson (C) 2004 Intel Research Cambridge + */ + +#include +#include +#include +#include +#include +#include +#include + +#define SEDFLEVEL 0 +#define PRINT(_f, _a...) \ +if ((_f)<=SEDFLEVEL) printk(_a ); + +/* + TODO: + TESTING! + implement stylish features! + tracing instead of PRINTs +*/ + + +#define TRC_SEDF 0xBEEF0000 + +struct sedf_dom_info +{ + struct domain *owner; + struct list_head list; + + //Parameters for EDF + s_time_t period; //=(relative deadline) + s_time_t slice; //=worst case execution time + int extratime; + //Bookkeeping + s_time_t absdead; + s_time_t sched_start; + s_time_t cputime; + s_time_t absblock; + //Statistics + s_time_t block_time_tot; + s_time_t penalty_time_tot; + +}; + +struct sedf_cpu_info { + struct list_head runnableq; + struct list_head waitq; +}; + +#define DOM_INFO(d) ((struct sedf_dom_info *)((d)->sched_priv)) +#define CPU_INFO(cpu) ((struct sedf_cpu_info *)schedule_data[cpu].sched_priv) +#define LIST(d) (&DOM_INFO(d)->list) +#define RUNQ(cpu) (&CPU_INFO(cpu)->runnableq) +#define WAITQ(cpu) (&CPU_INFO(cpu)->waitq) +#define IDLETASK(cpu) ((struct domain *)schedule_data[cpu].idle) + +static xmem_cache_t *dom_info_cache; + +/*static inline void __add_to_runqueue_head(struct domain *d) +{ + list_add(RUNLIST(d), RUNQUEUE(d->processor)); +} + +static inline void __add_to_runqueue_tail(struct domain *d) +{ + list_add_tail(RUNLIST(d), RUNQUEUE(d->processor)); +}*/ + +static inline void __del_from_queue(struct domain *d) +{ + struct list_head *list = LIST(d); + list_del(list); + list->next = NULL; +} + +/* adds a domain to the queue of processes which wait for the beginning of the next period + * this list is therefore sortet by this time, which is simply absol. deadline - period + */ +static inline void __add_to_waitqueue_sort(struct domain *d) { + struct list_head *cur; + struct sedf_dom_info *curinf; + + PRINT(3,"Adding domain %i (bop= %llu) to waitq\n",d->id,DOM_INFO(d)->absdead - DOM_INFO(d)->period); + //iterate through all elements to find our "hole" + list_for_each(cur,WAITQ(d->processor)){ + curinf = list_entry(cur,struct sedf_dom_info,list); + if ( DOM_INFO(d)->absdead - DOM_INFO(d)->period < curinf->absdead - curinf->period) + break; + else + PRINT(4,"\tbehind domain %i (bop= %llu)\n",curinf->owner->id,curinf->absdead - curinf->period); + } + //cur now contains the element, before which we'll enqueue + PRINT(3,"\tlist_add to %x\n",cur->prev); + list_add(LIST(d),cur->prev); + +} + +/* adds a domain to the queue of processes which have started their current period and are + * runnable (i.e. not blocked, dieing,...). The first element on this list is running on the processor, + * if the list is empty the idle task will run. As we are implementing EDF, this list is sorted by + * deadlines. + */ +static inline void __add_to_runqueue_sort(struct domain *d) { + struct list_head *cur; + struct sedf_dom_info *curinf; + + PRINT(3,"Adding domain %i (deadl= %llu) to runq\n",d->id,DOM_INFO(d)->absdead); + //iterate through all elements to find our "hole" + list_for_each(cur,RUNQ(d->processor)){ + curinf = list_entry(cur,struct sedf_dom_info,list); + if (DOM_INFO(d)->absdead < curinf->absdead) + break; + else + PRINT(4,"\tbehind domain %i (deadl= %llu)\n",curinf->owner->id,curinf->absdead); + } + //cur now contains the element, before which we'll enqueue + PRINT(3,"\tlist_add to %x\n",cur->prev); + list_add(LIST(d),cur->prev); + +} +static inline int __task_on_queue(struct domain *d) { + return (((LIST(d))->next != NULL) && (LIST(d)->next != LIST(d))); +} + +/* Initialises the queues and creates the domain info cache */ +static int sedf_init_scheduler() { + int i; + PRINT(2,"sedf_init_scheduler was called\n"); + + for ( i = 0; i < NR_CPUS; i++ ) { + schedule_data[i].sched_priv = xmalloc(sizeof(struct sedf_cpu_info)); + if ( schedule_data[i].sched_priv == NULL ) + return -1; + INIT_LIST_HEAD(WAITQ(i)); + INIT_LIST_HEAD(RUNQ(i)); + } + + dom_info_cache = xmem_cache_create( + "SEDF dom info", sizeof(struct sedf_dom_info), 0, 0, 0, NULL); + if ( dom_info_cache == NULL ) + { + printk("Could not allocate SLAB cache.\n"); + return -1; + } + + return 0; +} + +/* Allocates memory for per domain private scheduling data*/ +static int sedf_alloc_task(struct domain *d) { + PRINT(2,"sedf_alloc_task was called, domain-id %i\n",d->id); + if ( (d->sched_priv = xmem_cache_alloc(dom_info_cache)) == NULL ) + return -1; + memset(d->sched_priv, 0, sizeof(struct sedf_dom_info)); + return 0; +} + +/* Setup the sedf_dom_info */ +static void sedf_add_task(struct domain *d) +{ + //s_time_t now=NOW(); + struct sedf_dom_info *inf=DOM_INFO(d); + inf->owner = d; + + PRINT(2,"sedf_add_task was called, domain-id %i\n",d->id); + if (d->id==0) { + //set dom0 to something useful to boot the machine + inf->period = MILLISECS(20); + inf->slice = MILLISECS(15); + inf->absdead= 0; + } + else { + //other domains don't get any execution time at all in the beginning! + inf->period = MILLISECS(20); + inf->slice = 0; + inf->absdead= 0; + } + INIT_LIST_HEAD(&(inf->list)); +} + +/* Frees memory used by domain info */ +static void sedf_free_task(struct domain *d) +{ + PRINT(2,"sedf_free_task was called, domain-id %i\n",d->id); + ASSERT(d->sched_priv != NULL); + xmem_cache_free(dom_info_cache, d->sched_priv); +} + +/* Initialises idle task */ +static int sedf_init_idle_task(struct domain *d) { + PRINT(2,"sedf_init_idle_task was called, domain-id %i\n",d->id); + if ( sedf_alloc_task(d) < 0 ) + return -1; + + sedf_add_task(d); + DOM_INFO(d)->absdead=0; + set_bit(DF_RUNNING, &d->flags); + //the idle task doesn't have to turn up on any list... + return 0; +} + +#define MIN(x,y) (((x)<(y))?(x):(y)) +/* Main scheduling function + * Reasons for calling this function are: + * -timeslice for the current period used up + * -domain on waitqueue has started it's period*/ +static task_slice_t sedf_do_schedule(s_time_t now) +{ + struct sedf_dom_info *inf = DOM_INFO(current); + int cpu = current->processor; + struct list_head *runq = RUNQ(cpu); + struct list_head *waitq = WAITQ(cpu); + struct list_head *cur,*tmp; + struct sedf_dom_info *curinf; + task_slice_t ret; + + //first of all update the domains cputime + inf->cputime += now - inf->sched_start; + + //scheduling decisions, which don't involve the running domain + if (is_idle_task(inf->owner)) + goto check_waitq; //idle task doesn't get scheduled on the runq + if (!((inf->cputime >= inf->slice) || !domain_runnable(inf->owner))) + goto check_waitq; //nothing to do with the running task + + //remove tasks that can't run + __del_from_queue(inf->owner); + + //manage bookkeeping + if (inf->cputime >= inf->slice) { + inf->absdead += inf->period; + inf->cputime -= inf->slice; + if (inf->cputime<0) inf->cputime = 0; + } + if (inf->absdeadid,now,inf->absdead); + //add a runnable domain to the waitqueue + if (domain_runnable(inf->owner)) + __add_to_waitqueue_sort(inf->owner); + else + inf->absblock=now; + +check_waitq: + //check for the first elements of the waitqueue, whether their next period has already started + list_for_each_safe(cur,tmp,waitq) { + curinf = list_entry(cur,struct sedf_dom_info,list); + if (curinf->absdead - curinf->period<=now) { + __del_from_queue(curinf->owner); + __add_to_runqueue_sort(curinf->owner); + } + else + break; + } + + //process the runq + list_for_each_safe(cur,tmp,runq) { + curinf = list_entry(cur,struct sedf_dom_info,list); + if (unlikely(curinf->slice == 0)) { + //special treatment of elements with empty slice + __del_from_queue(curinf->owner); + curinf->absdead += curinf->period; + __add_to_waitqueue_sort(curinf->owner); + } + else + if (unlikely((curinf->absdead < now) || (curinf->cputime > curinf->slice))) { + //we missed the deadline or the slice was already finished... might hapen because of dom_adj. + //printk("Ouch! Domain %i missed deadline %llu\n",curinf->owner->id,curinf->absdead); + __del_from_queue(curinf->owner); + curinf->absdead += ((now - curinf->absdead) / curinf->period + 1) * curinf->period; + //force start of period to be in future! + //curinf->absdead += curinf->period; + curinf->cputime = 0; + __add_to_runqueue_sort(curinf->owner); + } + else + break; + } + + //now simply pick the first domain from the runqueue + struct sedf_dom_info *runinf, *waitinf; + + if (!list_empty(runq)) { + runinf = list_entry(runq->next,struct sedf_dom_info,list); + ret.task = runinf->owner; + if (!list_empty(waitq)) { + //rerun scheduler, when scheduled domain reaches it's end of slice or the first domain from the waitqueue gets ready + waitinf = list_entry(waitq->next,struct sedf_dom_info,list); + ret.time = MIN(now + runinf->slice - runinf->cputime,waitinf->absdead - waitinf->period) - now; + } + else { + ret.time = runinf->slice - runinf->cputime; + } + } + else { + //we have an empty runqueue => let the idle domain run and start the scheduler, when the next task becomes available + ret.task = IDLETASK(cpu); + if (!list_empty(waitq)) { + waitinf = list_entry(waitq->next,struct sedf_dom_info,list); + ret.time = (waitinf->absdead - waitinf->period) - now; + } + else { + //this could porbably never happen, but one never knows... + //it can... imagine a second CPU, which is pure scifi ATM, but one never knows ;) + ret.time=SECONDS(1); + } + } + if (ret.time<0) + printk("Ouch! We are seriously BEHIND schedule! %lli\n",ret.time); + DOM_INFO(ret.task)->sched_start=now; + return ret; +} + +static void sedf_sleep(struct domain *d) { + PRINT(2,"sedf_sleep was called, domain-id %i\n",d->id); + if ( test_bit(DF_RUNNING, &d->flags) ) + cpu_raise_softirq(d->processor, SCHEDULE_SOFTIRQ); + else if ( __task_on_queue(d) ) + __del_from_queue(d); +} + +/* This function wakes ifup a domain, i.e. moves them into the waitqueue + * things to mention are: admission control is taking place nowhere at + * the moment, so we can't be sure, whether it is safe to wake the domain + * up at all. Anyway, even if it is safe (total cpu usage <=100%) there are + * some considerations on when to allow the domain to wake up and have it's + * first deadline... + * I detected 3 cases, which could describe the possible behaviour of the scheduler, + * and I'll try to make them more clear: + * + * 1. Very conservative + * -when a blocked domain unblocks, it is allowed to start execution at + * the beginning of the next complete period + * (D..deadline, R..running, B..blocking/sleeping, U..unblocking/waking up + * + * DRRB_____D__U_____DRRRRR___D________ ... + * + * -this causes the domain to miss a period (and a deadlline) + * -doesn't disturb the schedule at all + * -deadlines keep occuring isochronous + * + * 2. Conservative Part 1 + * -when a domain unblocks in the same period as it was blocked it unblocks and + * may consume the rest of it's original time-slice minus the time it was blocked + * (assume period=9, slice=5) + * + * DRB_UR___DRRRRR___D... + * + * -this also doesn't disturb scheduling, but might lead to the fact, that the domain + * can't finish it's workload in the period + * + * Part 2a + * -it is obvious that such behaviour, applied when then unblocking is happening in + * later domains,tinyvnc works fine aswell + * + * DRB______D___UR___D... + * + * Part 2b + * -if one needs the full slice in the next period, it is necessary to treat the unblocking + * time as the start of the new period, i.e. move the deadline further back (later) + * -this doesn't disturb scheduling as well, because for EDF periods can be treated as minimal + * inter-release times and scheduling stays correct, when deadlines are kept relative to the time + * the process unblocks + * + * DRB______D___URRRR___D... + * (D) + * -problem: deadlines don't occur isochronous anymore + * + * 3. Unconservative (i.e. incorrect) + * -to boost the performance of I/O dependent domains it would be possible to put the domain into + * the runnable queue immediately, and let it run for the remainder of the slice of the current period + * (or even worse: allocate a new full slice for the domain) (and probably tweaking the deadline/slice even more) + * -either behaviour can lead to missed deadlines in other domains as opposed to approaches 1,2a,2b + */ +void sedf_wake(struct domain *d) { + //for the first try just implement the "very conservative" way of waking domains up + s_time_t now = NOW(); + struct sedf_dom_info* inf = DOM_INFO(d); + + PRINT(3,"sedf_wake was called, domain-id %i\n",d->id); + + if (unlikely(is_idle_task(d))) + return; + + if ( unlikely(__task_on_queue(d)) ) { + PRINT(3,"\tdomain %i is already in some queue\n",d->id); + return; + } + + //very conservative way of unblocking + //make sure that the start of the period for this + //domain is happening in the future + PRINT(3,"waking up domain %i (deadl= %llu period= %llu now= %llu)\n",d->id,inf->absdead,inf->period,now); + inf->absdead += ((now - inf->absdead) / inf->period+1)*inf->period; + PRINT(3,"waking up domain %i (deadl= %llu period= %llu now= %llu)\n",d->id,inf->absdead,inf->period,now); + + __add_to_waitqueue_sort(d); + PRINT(3,"added to waitq\n"); + + //TODO: Implement more fancy unblocking schemes! + /*if (now < inf->absdead) { + //short blocking + } + else { + //long blocking + }*/ + + //do some statistics here... + if (inf->absblock!=0) { + inf->block_time_tot += now - inf->absblock; + inf->penalty_time_tot += (inf->absdead - inf-> period) - inf->absblock; + /*if (DOM_INFO(d)->block_time_tot) + PRINT(3,"penalty: %lu\n",(DOM_INFO(d)->penalty_time_tot*100)/DOM_INFO(d)->block_time_tot);*/ + } + /*if ( is_idle_task(schedule_data[d->processor].curr)) { + cpu_raise_softirq(d->processor, SCHEDULE_SOFTIRQ); + return; + }*/ + + //check whether the awakened task needs to get scheduled before the next sched. decision + if (inf->absdead - inf->period < schedule_data[d->processor].s_timer.expires) + cpu_raise_softirq(d->processor, SCHEDULE_SOFTIRQ); +} + + +/* This could probably be a bit more specific!*/ +static void sedf_dump_domain(struct domain *d) { + printk("%u has=%c ", d->id, + test_bit(DF_RUNNING, &d->flags) ? 'T':'F'); + printk("c=%llu p=%llu sl=%llu ddl=%llu", d->cpu_time,DOM_INFO(d)->period,DOM_INFO(d)->slice,DOM_INFO(d)->absdead); + if (DOM_INFO(d)->block_time_tot!=0) + printf(" penalty: %lu",(DOM_INFO(d)->penalty_time_tot*100)/DOM_INFO(d)->block_time_tot); + printf("\n"); +} + +static void sedf_dump_cpu_state(int i) +{ + struct list_head *list, *queue; + int loop = 0; + struct sedf_dom_info *d_inf; + + printk("now=%llu\n",NOW()); + queue = RUNQ(i); + printk("RUNQ rq %lx n: %lx, p: %lx\n", (unsigned long)queue, + (unsigned long) queue->next, (unsigned long) queue->prev); + list_for_each ( list, queue ) + { + printk("%3d: ",loop++); + d_inf = list_entry(list, struct sedf_dom_info, list); + sedf_dump_domain(d_inf->owner); + } + + queue = WAITQ(i); + printk("\nWAITQ rq %lx n: %lx, p: %lx\n", (unsigned long)queue, + (unsigned long) queue->next, (unsigned long) queue->prev); + list_for_each ( list, queue ) + { + printk("%3d: ",loop++); + d_inf = list_entry(list, struct sedf_dom_info, list); + sedf_dump_domain(d_inf->owner); + } + +} + +/* set or fetch domain scheduling parameters */ +static int sedf_adjdom(struct domain *p, struct sched_adjdom_cmd *cmd) { + PRINT(2,"sedf_adjdom was called, domain-id %i new period %llu new slice %llu\n",p->id,cmd->u.sedf.period,cmd->u.sedf.slice); + if ( cmd->direction == SCHED_INFO_PUT ) + { + /* sanity checking! */ + if(cmd->u.sedf.slice > cmd->u.sedf.period ) + return -EINVAL; + + DOM_INFO(p)->period = cmd->u.sedf.period; + DOM_INFO(p)->slice = cmd->u.sedf.slice; + } + else if ( cmd->direction == SCHED_INFO_GET ) + { + cmd->u.sedf.period = DOM_INFO(p)->period; + cmd->u.sedf.slice = DOM_INFO(p)->slice; + } + PRINT(2,"sedf_adjdom_finished\n"); + return 0; +} + +struct scheduler sched_sedf_def = { + .name = "Simple EDF Scheduler", + .opt_name = "sedf", + .sched_id = SCHED_SEDF, + + .init_idle_task = sedf_init_idle_task, + .alloc_task = sedf_alloc_task, + .add_task = sedf_add_task, + .free_task = sedf_free_task, + .init_scheduler = sedf_init_scheduler, + .do_schedule = sedf_do_schedule, + .dump_cpu_state = sedf_dump_cpu_state, + .sleep = sedf_sleep, + .wake = sedf_wake, + .adjdom = sedf_adjdom, +}; diff --git a/xen/common/schedule.c b/xen/common/schedule.c index 4aceef7d2a..27e87cd0f5 100644 --- a/xen/common/schedule.c +++ b/xen/common/schedule.c @@ -68,10 +68,12 @@ schedule_data_t schedule_data[NR_CPUS]; extern struct scheduler sched_bvt_def; extern struct scheduler sched_rrobin_def; extern struct scheduler sched_atropos_def; +extern struct scheduler sched_sedf_def; static struct scheduler *schedulers[] = { &sched_bvt_def, &sched_rrobin_def, &sched_atropos_def, + &sched_sedf_def, NULL }; @@ -285,7 +287,7 @@ long sched_adjdom(struct sched_adjdom_cmd *cmd) if ( cmd->sched_id != ops.sched_id ) return -EINVAL; - + if ( cmd->direction != SCHED_INFO_PUT && cmd->direction != SCHED_INFO_GET ) return -EINVAL; diff --git a/xen/include/public/sched_ctl.h b/xen/include/public/sched_ctl.h index bd6a717521..f2b9722a4d 100644 --- a/xen/include/public/sched_ctl.h +++ b/xen/include/public/sched_ctl.h @@ -11,6 +11,7 @@ #define SCHED_BVT 0 #define SCHED_ATROPOS 2 #define SCHED_RROBIN 3 +#define SCHED_SEDF 4 /* these describe the intended direction used for a scheduler control or domain * command */ @@ -64,6 +65,15 @@ struct sched_adjdom_cmd u64 latency; /* 32 */ u32 xtratime; /* 36 */ } PACKED atropos; + + struct sedf_adjdom + { + u64 period; /* 16 */ + u64 slice; /* 24 */ + u64 dummy1; /* 32 */ + u32 dummy2; /* 36 */ + } PACKED sedf; + } PACKED u; } PACKED; /* 40 bytes */ From b1cbe136f63e49928273d8cde72accbf1b65a9d2 Mon Sep 17 00:00:00 2001 From: "sd386@font.cl.cam.ac.uk" Date: Fri, 28 Jan 2005 11:51:45 +0000 Subject: [PATCH 2/9] bitkeeper revision 1.1159.170.102 (41fa2751heRkQ76AYa9Oe-6cOyJuLw) Enhanced SEDF scheduler --- tools/libxc/xc.h | 4 +- tools/libxc/xc_sedf.c | 18 +- tools/python/xen/lowlevel/xc/xc.c | 50 ++- tools/python/xen/xend/XendClient.py | 11 +- tools/python/xen/xend/XendDomain.py | 8 +- tools/python/xen/xend/server/SrvDomain.py | 5 +- tools/python/xen/xm/main.py | 10 +- xen/common/sched_sedf.c | 492 +++++++++++++++++----- xen/include/public/sched_ctl.h | 5 +- 9 files changed, 445 insertions(+), 158 deletions(-) diff --git a/tools/libxc/xc.h b/tools/libxc/xc.h index 159e71fb45..24e35bda39 100644 --- a/tools/libxc/xc.h +++ b/tools/libxc/xc.h @@ -243,11 +243,11 @@ int xc_rrobin_global_get(int xc_handle, u64 *slice); int xc_sedf_domain_set(int xc_handle, u32 domid, - u64 period, u64 slice); + u64 period, u64 slice, u64 latency, u16 extratime, u16 weight); int xc_sedf_domain_get(int xc_handle, u32 domid, - u64* period, u64 *slice); + u64* period, u64 *slice, u64 *latency, u16 *extratime, u16* weight); typedef evtchn_status_t xc_evtchn_status_t; diff --git a/tools/libxc/xc_sedf.c b/tools/libxc/xc_sedf.c index 92c052fe23..a3a0598a7d 100644 --- a/tools/libxc/xc_sedf.c +++ b/tools/libxc/xc_sedf.c @@ -11,7 +11,7 @@ #include "xc_private.h" int xc_sedf_domain_set(int xc_handle, - u32 domid, u64 period, u64 slice) + u32 domid, u64 period, u64 slice,u64 latency, u16 extratime,u16 weight) { dom0_op_t op; struct sedf_adjdom *p = &op.u.adjustdom.u.sedf; @@ -21,12 +21,15 @@ int xc_sedf_domain_set(int xc_handle, op.u.adjustdom.sched_id = SCHED_SEDF; op.u.adjustdom.direction = SCHED_INFO_PUT; - p->period = period; - p->slice = slice; + p->period = period; + p->slice = slice; + p->latency = latency; + p->extratime = extratime; + p->weight = weight; return do_dom0_op(xc_handle, &op); } -int xc_sedf_domain_get(int xc_handle, u32 domid, u64 *period, u64 *slice) +int xc_sedf_domain_get(int xc_handle, u32 domid, u64 *period, u64 *slice, u64* latency, u16* extratime, u16* weight) { dom0_op_t op; int ret; @@ -39,7 +42,10 @@ int xc_sedf_domain_get(int xc_handle, u32 domid, u64 *period, u64 *slice) ret = do_dom0_op(xc_handle, &op); - *period = p->period; - *slice = p->slice; + *period = p->period; + *slice = p->slice; + *latency = p->latency; + *extratime = p->extratime; + *weight = p->weight; return ret; } diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c index be28b6d945..e772494f5f 100644 --- a/tools/python/xen/lowlevel/xc/xc.c +++ b/tools/python/xen/lowlevel/xc/xc.c @@ -791,15 +791,14 @@ static PyObject *pyxc_sedf_domain_set(PyObject *self, { XcObject *xc = (XcObject *)self; u32 domid; - u64 period, slice; + u64 period, slice, latency; + u16 extratime, weight; + static char *kwd_list[] = { "dom", "period", "slice", "latency", "extratime", "weight",NULL }; - static char *kwd_list[] = { "dom", "period", "slice", NULL }; - - if( !PyArg_ParseTupleAndKeywords(args, kwds, "iLL", kwd_list, &domid, - &period, &slice) ) + if( !PyArg_ParseTupleAndKeywords(args, kwds, "iLLLhh", kwd_list, &domid, + &period, &slice, &latency, &extratime, &weight) ) return NULL; - - if ( xc_sedf_domain_set(xc->xc_handle, domid, period, slice) != 0 ) + if ( xc_sedf_domain_set(xc->xc_handle, domid, period, slice, latency, extratime,weight) != 0 ) return PyErr_SetFromErrno(xc_error); Py_INCREF(zero); @@ -812,21 +811,24 @@ static PyObject *pyxc_sedf_domain_get(PyObject *self, { XcObject *xc = (XcObject *)self; u32 domid; - u64 period, slice; - + u64 period, slice,latency; + u16 weight, extratime; + static char *kwd_list[] = { "dom", NULL }; if( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &domid) ) return NULL; if ( xc_sedf_domain_get( xc->xc_handle, domid, &period, - &slice) ) + &slice,&latency,&extratime,&weight) ) return PyErr_SetFromErrno(xc_error); - return Py_BuildValue("{s:i,s:L,s:L}", - "domain", domid, - "period", period, - "slice", slice); + return Py_BuildValue("{s:i,s:L,s:L,s:L,s:i}", + "domain", domid, + "period", period, + "slice", slice, + "latency", latency, + "extratime", extratime); } static PyObject *pyxc_shadow_control(PyObject *self, @@ -1068,9 +1070,11 @@ static PyMethodDef pyxc_methods[] = { (PyCFunction)pyxc_sedf_domain_set, METH_KEYWORDS, "\n" "Set the scheduling parameters for a domain when running with Atropos.\n" - " dom [int]: domain to set\n" - " period [long]: domain's scheduling period\n" - " slice [long]: domain's slice per period\n" + " dom [int]: domain to set\n" + " period [long]: domain's scheduling period\n" + " slice [long]: domain's slice per period\n" + " latency [long]: domain's wakeup latency hint\n" + " extratime [int]: domain aware of extratime?\n" "Returns: [int] 0 on success; -1 on error.\n" }, { "sedf_domain_get", @@ -1078,11 +1082,13 @@ static PyMethodDef pyxc_methods[] = { METH_KEYWORDS, "\n" "Get the current scheduling parameters for a domain when running with\n" "the Atropos scheduler." - " dom [int]: domain to query\n" - "Returns: [dict]\n" - " domain [int]: domain ID\n" - " period [long]: scheduler period\n" - " slice [long]: CPU reservation per period\n"}, + " dom [int]: domain to query\n" + "Returns: [dict]\n" + " domain [int]: domain ID\n" + " period [long]: scheduler period\n" + " slice [long]: CPU reservation per period\n" + " latency [long]: domain's wakeup latency hint\n" + " extratime [int]: domain aware of extratime?\n"}, { "evtchn_alloc_unbound", (PyCFunction)pyxc_evtchn_alloc_unbound, diff --git a/tools/python/xen/xend/XendClient.py b/tools/python/xen/xend/XendClient.py index af6cc68971..fc14e307b0 100644 --- a/tools/python/xen/xend/XendClient.py +++ b/tools/python/xen/xend/XendClient.py @@ -273,11 +273,14 @@ class Xend: 'latency' : latency, 'xtratime': xtratime }) - def xend_domain_cpu_sedf_set(self, id, period, slice): + def xend_domain_cpu_sedf_set(self, id, period, slice, latency, extratime, weight): return self.xendPost(self.domainurl(id), - {'op' : 'cpu_sedf_set', - 'period' : period, - 'slice' : slice }) + {'op' : 'cpu_sedf_set', + 'period' : period, + 'slice' : slice, + 'latency' : latency, + 'extratime' : extratime, + 'weight' : weight }) def xend_domain_maxmem_set(self, id, memory): return self.xendPost(self.domainurl(id), diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index f280bc5786..2d46562660 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -660,12 +660,12 @@ class XendDomain: except Exception, ex: raise XendError(str(ex)) - def domain_cpu_sedf_set(self, id, period, slice): - """Set Atropos scheduler parameters for a domain. + def domain_cpu_sedf_set(self, id, period, slice, latency, extratime, weight): + """Set Simple EDF scheduler parameters for a domain. """ - dominfo = self.domain_lookup(id) + dominfo = self.domain_lookup(id) try: - return xc.sedf_domain_set(dominfo.dom, period, slice) + return xc.sedf_domain_set(dominfo.dom, period, slice, latency, extratime, weight) except Exception, ex: raise XendError(str(ex)) diff --git a/tools/python/xen/xend/server/SrvDomain.py b/tools/python/xen/xend/server/SrvDomain.py index ced4f1f83a..50664bfab6 100644 --- a/tools/python/xen/xend/server/SrvDomain.py +++ b/tools/python/xen/xend/server/SrvDomain.py @@ -138,7 +138,10 @@ class SrvDomain(SrvDir): fn = FormFn(self.xd.domain_cpu_sedf_set, [['dom', 'str'], ['period', 'int'], - ['slice', 'int']]) + ['slice', 'int'], + ['latency', 'int'], + ['extratime', 'int'], + ['weight', 'int']]) val = fn(req.args, {'dom': self.dom.id}) return val diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py index 42a79bac56..11b7617c03 100644 --- a/tools/python/xen/xm/main.py +++ b/tools/python/xen/xm/main.py @@ -606,14 +606,14 @@ class ProgSedf(Prog): info = """Set simple EDF parameters.""" def help(self, args): - print args[0], "DOM PERIOD SLICE" + print args[0], "DOM PERIOD SLICE LATENCY EXTRATIME WEIGHT" print "\nSet simple EDF parameters." def main(self, args): - if len(args) != 4: self.err("%s: Invalid argument(s)" % args[0]) - dom = args[1] - v = map(int, args[2:4]) - server.xend_domain_cpu_sedf_set(dom, *v) + if len(args) != 7: self.err("%s: Invalid argument(s)" % args[0]) + dom = args[1] + v = map(int, args[2:7]) + server.xend_domain_cpu_sedf_set(dom, *v) xm.prog(ProgSedf) diff --git a/xen/common/sched_sedf.c b/xen/common/sched_sedf.c index a85e112970..3c2913965d 100644 --- a/xen/common/sched_sedf.c +++ b/xen/common/sched_sedf.c @@ -13,7 +13,7 @@ #include #include -#define SEDFLEVEL 0 +#define SEDFLEVEL 2 #define PRINT(_f, _a...) \ if ((_f)<=SEDFLEVEL) printk(_a ); @@ -26,50 +26,104 @@ if ((_f)<=SEDFLEVEL) printk(_a ); #define TRC_SEDF 0xBEEF0000 +#define EXTRA_NONE (0) +#define EXTRA_AWARE (1) +#define EXTRA_RUNNING (2) +#define EXTRA_QUANTUM (MICROSECS(1000)) +#define WEIGHT_PERIOD (MILLISECS(100)) +#define WEIGHT_SAFETY (MILLISECS(5)) struct sedf_dom_info { struct domain *owner; struct list_head list; + struct list_head extralist; //Parameters for EDF s_time_t period; //=(relative deadline) s_time_t slice; //=worst case execution time - int extratime; + + //Advaced Parameters + //Latency Scaling + s_time_t period_orig; + s_time_t slice_orig; + s_time_t latency; + //extra-time status of domain + short extra; + //weights for "Scheduling for Beginners/ Lazy/ etc." + short weight; + //Bookkeeping s_time_t absdead; s_time_t sched_start; s_time_t cputime; s_time_t absblock; + //Statistics s_time_t block_time_tot; s_time_t penalty_time_tot; - + s_time_t extra_time_tot; + int block_tot; + int short_block_tot; + int long_block_tot; + int short_cont; }; struct sedf_cpu_info { struct list_head runnableq; struct list_head waitq; + struct list_head extraq; }; -#define DOM_INFO(d) ((struct sedf_dom_info *)((d)->sched_priv)) -#define CPU_INFO(cpu) ((struct sedf_cpu_info *)schedule_data[cpu].sched_priv) -#define LIST(d) (&DOM_INFO(d)->list) -#define RUNQ(cpu) (&CPU_INFO(cpu)->runnableq) -#define WAITQ(cpu) (&CPU_INFO(cpu)->waitq) -#define IDLETASK(cpu) ((struct domain *)schedule_data[cpu].idle) +#define DOM_INFO(d) ((struct sedf_dom_info *)((d)->sched_priv)) +#define CPU_INFO(cpu) ((struct sedf_cpu_info *)schedule_data[cpu].sched_priv) +#define LIST(d) (&DOM_INFO(d)->list) +#define EXTRALIST(d) (&DOM_INFO(d)->extralist) +#define RUNQ(cpu) (&CPU_INFO(cpu)->runnableq) +#define WAITQ(cpu) (&CPU_INFO(cpu)->waitq) +#define EXTRAQ(cpu) (&CPU_INFO(cpu)->extraq) +#define IDLETASK(cpu) ((struct domain *)schedule_data[cpu].idle) + +#define PERIOD_BEGIN(inf) ((inf)->absdead - (inf)->period) static xmem_cache_t *dom_info_cache; -/*static inline void __add_to_runqueue_head(struct domain *d) +static inline void extraq_add_head(struct domain *d) { - list_add(RUNLIST(d), RUNQUEUE(d->processor)); + list_add(EXTRALIST(d), EXTRAQ(d->processor)); } -static inline void __add_to_runqueue_tail(struct domain *d) +static inline void extraq_add_tail(struct domain *d) { - list_add_tail(RUNLIST(d), RUNQUEUE(d->processor)); -}*/ + list_add_tail(EXTRALIST(d), EXTRAQ(d->processor)); +} + +static inline void extraq_del(struct domain *d) +{ + struct list_head *list = EXTRALIST(d); + list_del(list); + list->next = NULL; +} + +static inline int extraq_on(struct domain *d) { + return (((EXTRALIST(d))->next != NULL) && (EXTRALIST(d)->next != EXTRALIST(d))); +} + +static inline void extraq_check(struct domain *d) { + if (extraq_on(d)) { + PRINT(2,"Dom %i is on extraQ\n",d->id); + if (DOM_INFO(d)->extra == EXTRA_NONE) { + extraq_del(d); + PRINT(2,"Removed dom %i from extraQ\n",d->id); + } + } else { + PRINT(2,"Dom %i is NOT on extraQ\n",d->id); + if (DOM_INFO(d)->extra != EXTRA_NONE) { + PRINT(2,"Added dom %i to extraQ\n",d->id); + extraq_add_tail(d); + } + } +} static inline void __del_from_queue(struct domain *d) { @@ -85,14 +139,14 @@ static inline void __add_to_waitqueue_sort(struct domain *d) { struct list_head *cur; struct sedf_dom_info *curinf; - PRINT(3,"Adding domain %i (bop= %llu) to waitq\n",d->id,DOM_INFO(d)->absdead - DOM_INFO(d)->period); + PRINT(3,"Adding domain %i (bop= %llu) to waitq\n",d->id,PERIOD_BEGIN(DOM_INFO(d))); //iterate through all elements to find our "hole" list_for_each(cur,WAITQ(d->processor)){ curinf = list_entry(cur,struct sedf_dom_info,list); - if ( DOM_INFO(d)->absdead - DOM_INFO(d)->period < curinf->absdead - curinf->period) + if (PERIOD_BEGIN(DOM_INFO(d)) < PERIOD_BEGIN(curinf)) break; else - PRINT(4,"\tbehind domain %i (bop= %llu)\n",curinf->owner->id,curinf->absdead - curinf->period); + PRINT(4,"\tbehind domain %i (bop= %llu)\n",curinf->owner->id,PERIOD_BEGIN(curinf)); } //cur now contains the element, before which we'll enqueue PRINT(3,"\tlist_add to %x\n",cur->prev); @@ -136,10 +190,11 @@ static int sedf_init_scheduler() { schedule_data[i].sched_priv = xmalloc(sizeof(struct sedf_cpu_info)); if ( schedule_data[i].sched_priv == NULL ) return -1; - INIT_LIST_HEAD(WAITQ(i)); + INIT_LIST_HEAD(WAITQ(i));//used for Latency Scaling INIT_LIST_HEAD(RUNQ(i)); + INIT_LIST_HEAD(EXTRAQ(i)); } - + //we could not find any suitable domain => look for domains that are aware of extratime dom_info_cache = xmem_cache_create( "SEDF dom info", sizeof(struct sedf_dom_info), 0, 0, 0, NULL); if ( dom_info_cache == NULL ) @@ -170,17 +225,23 @@ static void sedf_add_task(struct domain *d) PRINT(2,"sedf_add_task was called, domain-id %i\n",d->id); if (d->id==0) { //set dom0 to something useful to boot the machine - inf->period = MILLISECS(20); - inf->slice = MILLISECS(15); - inf->absdead= 0; + inf->period = MILLISECS(20); + inf->slice = MILLISECS(15); + inf->latency = 0; + inf->absdead = 0; + inf->extra = EXTRA_NONE;//EXTRA_AWARE; } else { //other domains don't get any execution time at all in the beginning! - inf->period = MILLISECS(20); - inf->slice = 0; - inf->absdead= 0; + inf->period = MILLISECS(20); + inf->slice = 0; + inf->absdead = 0; + inf->latency = 0; + inf->extra = EXTRA_NONE;//EXTRA_AWARE } + inf->period_orig = inf->period; inf->slice_orig = inf->slice; INIT_LIST_HEAD(&(inf->list)); + INIT_LIST_HEAD(&(inf->extralist)); } /* Frees memory used by domain info */ @@ -208,48 +269,80 @@ static int sedf_init_idle_task(struct domain *d) { /* Main scheduling function * Reasons for calling this function are: * -timeslice for the current period used up - * -domain on waitqueue has started it's period*/ + * -domain on waitqueue has started it's period + * -and various others ;) in general: determin which domain to run next*/ static task_slice_t sedf_do_schedule(s_time_t now) { struct sedf_dom_info *inf = DOM_INFO(current); int cpu = current->processor; struct list_head *runq = RUNQ(cpu); struct list_head *waitq = WAITQ(cpu); + struct list_head *extraq = EXTRAQ(cpu); struct list_head *cur,*tmp; struct sedf_dom_info *curinf; task_slice_t ret; + - //first of all update the domains cputime - inf->cputime += now - inf->sched_start; - - //scheduling decisions, which don't involve the running domain + //idle tasks don't need any of the following stuf if (is_idle_task(inf->owner)) goto check_waitq; //idle task doesn't get scheduled on the runq - if (!((inf->cputime >= inf->slice) || !domain_runnable(inf->owner))) - goto check_waitq; //nothing to do with the running task - //remove tasks that can't run - __del_from_queue(inf->owner); + if (unlikely(inf->extra == EXTRA_RUNNING)) { + //special treatment of domains running in extra time + inf->extra = EXTRA_AWARE; + inf->cputime=0; + inf->extra_time_tot += now - inf->sched_start; - //manage bookkeeping - if (inf->cputime >= inf->slice) { - inf->absdead += inf->period; - inf->cputime -= inf->slice; - if (inf->cputime<0) inf->cputime = 0; + extraq_del(current); //remove extradomain from head of the queue + if (domain_runnable(inf->owner)) + extraq_add_tail(current); //and add to the tail if it is runnable => round-robin + else + __del_from_queue(inf->owner); //if domain blocked in extratime remove it from waitq(/runq) as well } - if (inf->absdeadid,now,inf->absdead); - //add a runnable domain to the waitqueue - if (domain_runnable(inf->owner)) - __add_to_waitqueue_sort(inf->owner); - else - inf->absblock=now; + else { + //current domain is running in real time mode + //update the domains cputime + inf->cputime += now - inf->sched_start; + //scheduling decisions, which don't move the running domain to any queues + if ((inf->cputime < inf->slice) && domain_runnable(inf->owner)) + goto check_waitq; //there is nothing to do with the running task + //remove tasks that can't run + __del_from_queue(inf->owner); + + //manage bookkeeping (i.e. calculate next deadline, memorize overun time of slice) of finished domains + if (inf->cputime >= inf->slice) { + inf->cputime -= inf->slice; + + if (inf->period < inf->period_orig) { + //this domain runs in latency scaling mode + inf->period *= 2; + inf->slice *= 2; + if ((inf->period > inf->period_orig) || (inf->slice > inf->slice_orig)) { + //now switch back to standard timing + inf->period = inf->period_orig; + inf->slice = inf->slice_orig; + } + } + inf->absdead += inf->period; //set next deadline + } + //if (inf->absdeadid,now,inf->absdead); + //add a runnable domain to the waitqueue + if (domain_runnable(inf->owner)) + __add_to_waitqueue_sort(inf->owner); + else { + //we have a blocked realtime task + inf->absblock=now; + if (inf->extra == EXTRA_AWARE) + extraq_del(inf->owner); //remove a blocked domain from the extraq aswell + } + } check_waitq: //check for the first elements of the waitqueue, whether their next period has already started list_for_each_safe(cur,tmp,waitq) { curinf = list_entry(cur,struct sedf_dom_info,list); - if (curinf->absdead - curinf->period<=now) { + if (PERIOD_BEGIN(curinf) <= now) { __del_from_queue(curinf->owner); __add_to_runqueue_sort(curinf->owner); } @@ -266,19 +359,19 @@ check_waitq: curinf->absdead += curinf->period; __add_to_waitqueue_sort(curinf->owner); } - else + else { if (unlikely((curinf->absdead < now) || (curinf->cputime > curinf->slice))) { //we missed the deadline or the slice was already finished... might hapen because of dom_adj. //printk("Ouch! Domain %i missed deadline %llu\n",curinf->owner->id,curinf->absdead); __del_from_queue(curinf->owner); - curinf->absdead += ((now - curinf->absdead) / curinf->period + 1) * curinf->period; - //force start of period to be in future! - //curinf->absdead += curinf->period; + curinf->absdead += ((now - curinf->absdead + (curinf->period-1)) / curinf->period) * curinf->period; + //force start of period to be in future and aligned to period borders! curinf->cputime = 0; __add_to_runqueue_sort(curinf->owner); } else break; + } } //now simply pick the first domain from the runqueue @@ -290,23 +383,33 @@ check_waitq: if (!list_empty(waitq)) { //rerun scheduler, when scheduled domain reaches it's end of slice or the first domain from the waitqueue gets ready waitinf = list_entry(waitq->next,struct sedf_dom_info,list); - ret.time = MIN(now + runinf->slice - runinf->cputime,waitinf->absdead - waitinf->period) - now; + ret.time = MIN(now + runinf->slice - runinf->cputime,PERIOD_BEGIN(waitinf)) - now; } else { ret.time = runinf->slice - runinf->cputime; } } else { - //we have an empty runqueue => let the idle domain run and start the scheduler, when the next task becomes available - ret.task = IDLETASK(cpu); if (!list_empty(waitq)) { - waitinf = list_entry(waitq->next,struct sedf_dom_info,list); - ret.time = (waitinf->absdead - waitinf->period) - now; + waitinf = list_entry(waitq->next,struct sedf_dom_info,list); + //we could not find any suitable domain => look for domains that are aware of extratime + if (!list_empty(extraq) && (PERIOD_BEGIN(waitinf) - now >= EXTRA_QUANTUM)) { + runinf = list_entry(extraq->next,struct sedf_dom_info,extralist); + runinf->extra = EXTRA_RUNNING; + ret.task = runinf->owner; + ret.time = EXTRA_QUANTUM; + } + else { + //we have an empty run- and extraqueue or too less time => idle task! + ret.task = IDLETASK(cpu); + ret.time = PERIOD_BEGIN(waitinf) - now; + } } else { - //this could porbably never happen, but one never knows... + //this could probably never happen, but one never knows... //it can... imagine a second CPU, which is pure scifi ATM, but one never knows ;) - ret.time=SECONDS(1); + ret.task = IDLETASK(cpu); + ret.time = SECONDS(1); } } if (ret.time<0) @@ -319,8 +422,12 @@ static void sedf_sleep(struct domain *d) { PRINT(2,"sedf_sleep was called, domain-id %i\n",d->id); if ( test_bit(DF_RUNNING, &d->flags) ) cpu_raise_softirq(d->processor, SCHEDULE_SOFTIRQ); - else if ( __task_on_queue(d) ) - __del_from_queue(d); + else { + if ( __task_on_queue(d) ) + __del_from_queue(d); + if (extraq_on(d)) + extraq_del(d); + } } /* This function wakes ifup a domain, i.e. moves them into the waitqueue @@ -343,7 +450,7 @@ static void sedf_sleep(struct domain *d) { * -doesn't disturb the schedule at all * -deadlines keep occuring isochronous * - * 2. Conservative Part 1 + * 2. Conservative Part 1: Short Unblocking * -when a domain unblocks in the same period as it was blocked it unblocks and * may consume the rest of it's original time-slice minus the time it was blocked * (assume period=9, slice=5) @@ -352,10 +459,13 @@ static void sedf_sleep(struct domain *d) { * * -this also doesn't disturb scheduling, but might lead to the fact, that the domain * can't finish it's workload in the period + * -in addition to that the domain can be treated prioritised when extratime is available * + * Part2: Long Unblocking * Part 2a * -it is obvious that such behaviour, applied when then unblocking is happening in - * later domains,tinyvnc works fine aswell + * later periods, works fine aswell + * -the domain is treated as if it would have been running since the start of its new period * * DRB______D___UR___D... * @@ -369,13 +479,62 @@ static void sedf_sleep(struct domain *d) { * DRB______D___URRRR___D... * (D) * -problem: deadlines don't occur isochronous anymore + * Part 2c (Improved Atropos design) + * -when a domain unblocks it is given a very short period (=latency hint) and slice length scaled + * accordingly + * -both rise again to the original value (e.g. get doubled every period) * * 3. Unconservative (i.e. incorrect) * -to boost the performance of I/O dependent domains it would be possible to put the domain into * the runnable queue immediately, and let it run for the remainder of the slice of the current period - * (or even worse: allocate a new full slice for the domain) (and probably tweaking the deadline/slice even more) + * (or even worse: allocate a new full slice for the domain) * -either behaviour can lead to missed deadlines in other domains as opposed to approaches 1,2a,2b */ +static inline void unblock_short_vcons(struct sedf_dom_info* inf, s_time_t now) { inf->absdead += inf->period;} +static inline void unblock_long_vcons(struct sedf_dom_info* inf, s_time_t now) { + inf->absdead += ((now - inf->absdead) / inf->period + 1) * inf->period; //very conservative + inf->cputime = 0; +} + +static inline void unblock_short_cons(struct sedf_dom_info* inf, s_time_t now) { + inf->cputime += now - inf->absblock; //treat blocked time as consumed by the domain + if (inf->cputime + EXTRA_QUANTUM > inf->slice) { + //we don't have a reasonable amount of time in our slice left :( + inf->cputime=0; + inf->absdead += inf->period; //start in next period! + } + else + inf->short_cont++; +} +static inline void unblock_long_cons_a(struct sedf_dom_info* inf, s_time_t now) { + inf->cputime = (now - inf->absdead) % inf->period; //treat the time the domain was blocked in the CURRENT + //period as consumed by the domain + if (inf->cputime + EXTRA_QUANTUM > inf->slice) { + //we don't have a reasonable amount of time in our slice left :( + inf->cputime=0; + inf->absdead += inf->period; //start in next period! + } +} +static inline void unblock_long_cons_b(struct sedf_dom_info* inf,s_time_t now) { + inf->absdead = now + inf->period; //Conservative 2b + inf->cputime = 0; +} +static inline void unblock_long_cons_c(struct sedf_dom_info* inf,s_time_t now) { + if (likely(inf->latency)) { + //sclae the slice and period accordingly to the latency hint + inf->period = inf->latency; //reduce period temporarily to the latency hint + ASSERT((inf->latency < ULONG_MAX) && (inf->slice_orig < ULONG_MAX)); //this results in max. 4s slice/period length + inf->slice = (inf->latency * inf->slice_orig) / inf->period_orig; //scale slice accordingly, so that utilisation stays the same + } + else { + //we don't have a latency hint.. use some other technique + inf->absdead = now + inf->period; //Conservative 2b... + inf->cputime = 0; + } +} + +/*static inline void unblock_short_vcons +static inline void unblock_short_vcons*/ void sedf_wake(struct domain *d) { //for the first try just implement the "very conservative" way of waking domains up s_time_t now = NOW(); @@ -390,29 +549,52 @@ void sedf_wake(struct domain *d) { PRINT(3,"\tdomain %i is already in some queue\n",d->id); return; } - + if ( unlikely(extraq_on(d)) ) { + PRINT(3,"\tdomain %i is already in the extraQ\n",d->id); + } + if (unlikely(inf->absdead == 0)) + inf->absdead = now + inf->slice; //initial setup of the deadline + //very conservative way of unblocking //make sure that the start of the period for this //domain is happening in the future PRINT(3,"waking up domain %i (deadl= %llu period= %llu now= %llu)\n",d->id,inf->absdead,inf->period,now); - inf->absdead += ((now - inf->absdead) / inf->period+1)*inf->period; - PRINT(3,"waking up domain %i (deadl= %llu period= %llu now= %llu)\n",d->id,inf->absdead,inf->period,now); + inf->block_tot++; + if (unlikely(now< PERIOD_BEGIN(inf))) { + //this might happen, imagine unblocking in extra-time! + if (likely(inf->extra == EXTRA_AWARE)) + extraq_add_tail(d); //SD: Could extraq_add_head be better? + //else + //This is very very unlikely, ie. might even be an error?! + } + else { + if (now < inf->absdead) { + //short blocking + inf->short_block_tot++; + //unblock_short_vcons(inf, now); + unblock_short_cons(inf, now); + if (inf->extra == EXTRA_AWARE) + extraq_add_head(d); + } + else { + //long blocking + inf->long_block_tot++; + //PRINT(3,"old=%llu ",inf->absdead); + //unblock_long_vcons(inf, now); + unblock_long_cons_c(inf,now); + if (inf->extra == EXTRA_AWARE) + extraq_add_tail(d); + } + } + PRINT(3,"waking up domain %i (deadl= %llu period= %llu now= %llu)\n",d->id,inf->absdead,inf->period,now); __add_to_waitqueue_sort(d); PRINT(3,"added to waitq\n"); - //TODO: Implement more fancy unblocking schemes! - /*if (now < inf->absdead) { - //short blocking - } - else { - //long blocking - }*/ - //do some statistics here... if (inf->absblock!=0) { inf->block_time_tot += now - inf->absblock; - inf->penalty_time_tot += (inf->absdead - inf-> period) - inf->absblock; + inf->penalty_time_tot += PERIOD_BEGIN(inf) + inf->cputime - inf->absblock; /*if (DOM_INFO(d)->block_time_tot) PRINT(3,"penalty: %lu\n",(DOM_INFO(d)->penalty_time_tot*100)/DOM_INFO(d)->block_time_tot);*/ } @@ -422,7 +604,10 @@ void sedf_wake(struct domain *d) { }*/ //check whether the awakened task needs to get scheduled before the next sched. decision - if (inf->absdead - inf->period < schedule_data[d->processor].s_timer.expires) + //and check, whether we are idling and this domain is extratime aware + if ((PERIOD_BEGIN(inf) < schedule_data[d->processor].s_timer.expires) || + (is_idle_task(schedule_data[d->processor].curr) && (now + EXTRA_QUANTUM < schedule_data[d->processor].s_timer.expires) && + (inf->extra == EXTRA_AWARE))) cpu_raise_softirq(d->processor, SCHEDULE_SOFTIRQ); } @@ -431,57 +616,140 @@ void sedf_wake(struct domain *d) { static void sedf_dump_domain(struct domain *d) { printk("%u has=%c ", d->id, test_bit(DF_RUNNING, &d->flags) ? 'T':'F'); - printk("c=%llu p=%llu sl=%llu ddl=%llu", d->cpu_time,DOM_INFO(d)->period,DOM_INFO(d)->slice,DOM_INFO(d)->absdead); + printk("p=%llu sl=%llu ddl=%llu w=%u c=%llu xtr(%s)=%llu", + DOM_INFO(d)->period, DOM_INFO(d)->slice, DOM_INFO(d)->absdead, DOM_INFO(d)->weight, d->cpu_time, + DOM_INFO(d)->extra ? "yes" : "no", DOM_INFO(d)->extra_time_tot); + if (d->cpu_time !=0) + printf(" (%lu%)", (DOM_INFO(d)->extra_time_tot * 100) / d->cpu_time); if (DOM_INFO(d)->block_time_tot!=0) - printf(" penalty: %lu",(DOM_INFO(d)->penalty_time_tot*100)/DOM_INFO(d)->block_time_tot); + printf(" pen=%lu%", (DOM_INFO(d)->penalty_time_tot * 100) / DOM_INFO(d)->block_time_tot); + if (DOM_INFO(d)->block_tot!=0) + printf("\n blks=%lu sh=%lu (%lu%) (shc=%lu (%lu%)) l=%lu (%lu%) avg: b=%llu p=%llu", DOM_INFO(d)->block_tot, + DOM_INFO(d)->short_block_tot, (DOM_INFO(d)->short_block_tot * 100) / DOM_INFO(d)->block_tot, + DOM_INFO(d)->short_cont, (DOM_INFO(d)->short_cont * 100) / DOM_INFO(d)->block_tot, + DOM_INFO(d)->long_block_tot, (DOM_INFO(d)->long_block_tot * 100) / DOM_INFO(d)->block_tot, + (DOM_INFO(d)->block_time_tot) / DOM_INFO(d)->block_tot, + (DOM_INFO(d)->penalty_time_tot) / DOM_INFO(d)->block_tot); printf("\n"); } static void sedf_dump_cpu_state(int i) { - struct list_head *list, *queue; - int loop = 0; - struct sedf_dom_info *d_inf; - - printk("now=%llu\n",NOW()); - queue = RUNQ(i); - printk("RUNQ rq %lx n: %lx, p: %lx\n", (unsigned long)queue, - (unsigned long) queue->next, (unsigned long) queue->prev); - list_for_each ( list, queue ) - { - printk("%3d: ",loop++); - d_inf = list_entry(list, struct sedf_dom_info, list); - sedf_dump_domain(d_inf->owner); - } - - queue = WAITQ(i); - printk("\nWAITQ rq %lx n: %lx, p: %lx\n", (unsigned long)queue, - (unsigned long) queue->next, (unsigned long) queue->prev); - list_for_each ( list, queue ) - { - printk("%3d: ",loop++); - d_inf = list_entry(list, struct sedf_dom_info, list); - sedf_dump_domain(d_inf->owner); - } - + struct list_head *list, *queue, *tmp; + int loop = 0; +/* int found = 0; + int id1st = 0;*/ + struct sedf_dom_info *d_inf; + struct domain* d; + + printk("now=%llu\n",NOW()); + queue = RUNQ(i); + printk("RUNQ rq %lx n: %lx, p: %lx\n", (unsigned long)queue, + (unsigned long) queue->next, (unsigned long) queue->prev); + list_for_each_safe ( list, tmp, queue ) + { + printk("%3d: ",loop++); + d_inf = list_entry(list, struct sedf_dom_info, list); + sedf_dump_domain(d_inf->owner); + } + + queue = WAITQ(i); loop = 0; + printk("\nWAITQ rq %lx n: %lx, p: %lx\n", (unsigned long)queue, + (unsigned long) queue->next, (unsigned long) queue->prev); + list_for_each_safe ( list, tmp, queue ) + { + printk("%3d: ",loop++); + d_inf = list_entry(list, struct sedf_dom_info, list); + sedf_dump_domain(d_inf->owner); + } + + queue = EXTRAQ(i); loop = 0; + printk("\nEXTRAQ rq %lx n: %lx, p: %lx\n", (unsigned long)queue, + (unsigned long) queue->next, (unsigned long) queue->prev); + list_for_each_safe ( list, tmp, queue ) + { + d_inf = list_entry(list, struct sedf_dom_info, extralist); + +/* if (found) { + if (id1st == d_inf->owner->id) + break; + } + else { id1st=d_inf->owner->id; found=1;}*/ + printk("%3d: ",loop++); + sedf_dump_domain(d_inf->owner); + } + + loop = 0; + printk("\nnot on Q\n"); + for_each_domain(d) { + if (!extraq_on(d) && !__task_on_queue(d)) { + printk("%3d: ",loop++); + sedf_dump_domain(d); + } + } +} +//Adjusts periods and slices of the domains accordingly to their weights +static inline int sedf_adjust_weights(struct domain *p, struct sched_adjdom_cmd *cmd) { + int sumw = 0; + s_time_t sumt = 0; + + //sum up all weights + for_each_domain(p) { + if (DOM_INFO(p)->weight) + sumw += DOM_INFO(p)->weight; + else { + //don't modify domains who don't have a weight, but sum up + //the time they need, projected to a WEIGHT_PERIOD, so that + //this time is not given to the weight-driven domains + ASSERT((WEIGHT_PERIOD < ULONG_MAX) && (DOM_INFO(p)->slice_orig < ULONG_MAX)); //this results in max. 4s slice/period length + sumt += (WEIGHT_PERIOD * DOM_INFO(p)->slice_orig) / DOM_INFO(p)->period_orig; + } + } + //adjust all slices (and periods) to the new weight + for_each_domain(p) { + if (DOM_INFO(p)->weight) { + DOM_INFO(p)->period_orig = + DOM_INFO(p)->period = WEIGHT_PERIOD; + DOM_INFO(p)->slice_orig = + DOM_INFO(p)->slice = (DOM_INFO(p)->weight * (WEIGHT_PERIOD - WEIGHT_SAFETY - sumt)) / sumw; + } + } + return 0; } /* set or fetch domain scheduling parameters */ static int sedf_adjdom(struct domain *p, struct sched_adjdom_cmd *cmd) { - PRINT(2,"sedf_adjdom was called, domain-id %i new period %llu new slice %llu\n",p->id,cmd->u.sedf.period,cmd->u.sedf.slice); + PRINT(2,"sedf_adjdom was called, domain-id %i new period %llu new slice %llu\n" + "latency %llu extra: %s\n",p->id,cmd->u.sedf.period,cmd->u.sedf.slice,cmd->u.sedf.latency,(cmd->u.sedf.extratime)?"yes":"no"); if ( cmd->direction == SCHED_INFO_PUT ) { - /* sanity checking! */ - if(cmd->u.sedf.slice > cmd->u.sedf.period ) - return -EINVAL; - - DOM_INFO(p)->period = cmd->u.sedf.period; - DOM_INFO(p)->slice = cmd->u.sedf.slice; + if (!cmd->u.sedf.period && !cmd->u.sedf.weight) //check for sane parameters + return -EINVAL; + if (cmd->u.sedf.weight) { + DOM_INFO(p)->weight = cmd->u.sedf.weight; //weight driven domains + } + else { + //time driven domains + if(cmd->u.sedf.slice > cmd->u.sedf.period ) /* sanity checking! */ + return -EINVAL; + DOM_INFO(p)->period_orig = + DOM_INFO(p)->period = cmd->u.sedf.period; + DOM_INFO(p)->slice_orig = + DOM_INFO(p)->slice = cmd->u.sedf.slice; + } + if (sedf_adjust_weights(p,cmd)) + return -EINVAL; + DOM_INFO(p)->extra = cmd->u.sedf.extratime; + DOM_INFO(p)->latency = cmd->u.sedf.latency; + extraq_check(p); } else if ( cmd->direction == SCHED_INFO_GET ) { - cmd->u.sedf.period = DOM_INFO(p)->period; - cmd->u.sedf.slice = DOM_INFO(p)->slice; + cmd->u.sedf.period = DOM_INFO(p)->period; + cmd->u.sedf.slice = DOM_INFO(p)->slice; + cmd->u.sedf.extratime = DOM_INFO(p)->extra; + cmd->u.sedf.latency = DOM_INFO(p)->latency; + cmd->u.sedf.weight = DOM_INFO(p)->weight; } PRINT(2,"sedf_adjdom_finished\n"); return 0; diff --git a/xen/include/public/sched_ctl.h b/xen/include/public/sched_ctl.h index f2b9722a4d..5c21769485 100644 --- a/xen/include/public/sched_ctl.h +++ b/xen/include/public/sched_ctl.h @@ -70,8 +70,9 @@ struct sched_adjdom_cmd { u64 period; /* 16 */ u64 slice; /* 24 */ - u64 dummy1; /* 32 */ - u32 dummy2; /* 36 */ + u64 latency; /* 32 */ + u16 extratime; /* 36 */ + u16 weight; /* 38 */ } PACKED sedf; } PACKED u; From 0cabc0963227bc9b83e864b087ff0bef20bdbe6b Mon Sep 17 00:00:00 2001 From: "sd386@font.cl.cam.ac.uk" Date: Fri, 28 Jan 2005 17:31:35 +0000 Subject: [PATCH 3/9] bitkeeper revision 1.1159.170.103 (41fa76f7AyfHPooBzZbsn0ctSduSJg) Bugfixing in SEDF --- xen/common/sched_sedf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/xen/common/sched_sedf.c b/xen/common/sched_sedf.c index 3c2913965d..10a12b674e 100644 --- a/xen/common/sched_sedf.c +++ b/xen/common/sched_sedf.c @@ -730,6 +730,7 @@ static int sedf_adjdom(struct domain *p, struct sched_adjdom_cmd *cmd) { } else { //time driven domains + DOM_INFO(p)->weight = 0; if(cmd->u.sedf.slice > cmd->u.sedf.period ) /* sanity checking! */ return -EINVAL; DOM_INFO(p)->period_orig = From 1276e201ddadd24b017c3550a76488ad282789c3 Mon Sep 17 00:00:00 2001 From: "sd386@font.cl.cam.ac.uk" Date: Thu, 17 Feb 2005 18:50:24 +0000 Subject: [PATCH 4/9] bitkeeper revision 1.1159.170.104 (4214e770zuNBDS-jq2cWXHSs1B4UOw) Extended sedf scheduler (weighted extraqueues), added enhanced scheduling histogramm --- xen/common/sched_sedf.c | 258 +++++++++++++++++++++++++++++-------- xen/common/schedule.c | 92 ++++++++++++- xen/include/xen/sched-if.h | 11 +- 3 files changed, 302 insertions(+), 59 deletions(-) diff --git a/xen/common/sched_sedf.c b/xen/common/sched_sedf.c index 10a12b674e..01e944d554 100644 --- a/xen/common/sched_sedf.c +++ b/xen/common/sched_sedf.c @@ -13,14 +13,33 @@ #include #include +//#include + #define SEDFLEVEL 2 #define PRINT(_f, _a...) \ if ((_f)<=SEDFLEVEL) printk(_a ); +//various ways of unblocking domains +#define UNBLOCK_ISOCHRONOUS_EDF 1 +#define UNBLOCK_EDF 2 +#define UNBLOCK_ATROPOS 3 +#define UNBLOCK_SHORT_RESUME 4 +#define UNBLOCK_BURST 5 + +#define UNBLOCK UNBLOCK_BURST + +//various ways of treating extra-time +#define EXTRA_OFF 1 +#define EXTRA_ROUNDR 2 +#define EXTRA_SLICE_WEIGHT 3 +#define EXTRA_BLOCK_WEIGHT 4 + +#define EXTRA EXTRA_OFF + + /* TODO: TESTING! - implement stylish features! tracing instead of PRINTs */ @@ -29,15 +48,16 @@ if ((_f)<=SEDFLEVEL) printk(_a ); #define EXTRA_NONE (0) #define EXTRA_AWARE (1) #define EXTRA_RUNNING (2) -#define EXTRA_QUANTUM (MICROSECS(1000)) +#define EXTRA_QUANTUM (MICROSECS(500)) #define WEIGHT_PERIOD (MILLISECS(100)) #define WEIGHT_SAFETY (MILLISECS(5)) + struct sedf_dom_info { struct domain *owner; struct list_head list; - struct list_head extralist; + struct list_head extralist[2]; //Parameters for EDF s_time_t period; //=(relative deadline) @@ -50,14 +70,17 @@ struct sedf_dom_info s_time_t latency; //extra-time status of domain short extra; - //weights for "Scheduling for Beginners/ Lazy/ etc." + //weights for "Scheduling for beginners/ lazy/ etc." short weight; //Bookkeeping s_time_t absdead; s_time_t sched_start; s_time_t cputime; - s_time_t absblock; + s_time_t absblock; + s_time_t absunblock; //time the domain unblocked, used by burst mode to determine unblocking intervals + int score[2]; //scores for {util, block penalty}-weighted extratime distribution + s_time_t short_block_lost_tot; //Statistics s_time_t block_time_tot; @@ -109,6 +132,37 @@ static inline int extraq_on(struct domain *d) { return (((EXTRALIST(d))->next != NULL) && (EXTRALIST(d)->next != EXTRALIST(d))); } +/* adds a domain to the queue of processes which are aware of extra time. List is sorted by score, + where a lower score means higher priority for an extra slice. It also updates the score, by simply subtracting + a fixed value from each entry, in order to avoid overflow. The algorithm works by simply charging each domain + that recieved extratime with an inverse of its weight. + */ +static inline void extraq_add_sort_update(struct domain *d, unsigned long sub) { + struct list_head *cur; + struct sedf_dom_info *curinf; + + PRINT(3,"Adding domain %i (score= %llu) to extraq\n",d->id,DOM_INFO(d)->score); + //iterate through all elements to find our "hole" and on our way update all the other scores + list_for_each(cur,EXTRAQ(d->processor)){ + curinf = list_entry(cur,struct sedf_dom_info,extralist); + curinf->score -= sub; + if (DOM_INFO(d)->score < curinf->score) + break; + else + PRINT(4,"\tbehind domain %i (score= %llu)\n",curinf->owner->id,curinf->score); + } + //cur now contains the element, before which we'll enqueue + PRINT(3,"\tlist_add to %x\n",cur->prev); + list_add(EXTRALIST(d),cur->prev); + + //continue updating the extraq + if ((cur != EXTRAQ(d->processor)) && sub) + for (cur = cur->next; cur != EXTRAQ(d->processor); cur = cur-> next) { + curinf = list_entry(cur,struct sedf_dom_info,extralist); + curinf->score -= sub; + PRINT(4,"\tupdating domain %i (score= %llu)\n",curinf->owner->id,curinf->score); + } +} static inline void extraq_check(struct domain *d) { if (extraq_on(d)) { PRINT(2,"Dom %i is on extraQ\n",d->id); @@ -120,11 +174,10 @@ static inline void extraq_check(struct domain *d) { PRINT(2,"Dom %i is NOT on extraQ\n",d->id); if (DOM_INFO(d)->extra != EXTRA_NONE) { PRINT(2,"Added dom %i to extraQ\n",d->id); - extraq_add_tail(d); + extraq_add_sort_update(d, 0); } } } - static inline void __del_from_queue(struct domain *d) { struct list_head *list = LIST(d); @@ -277,29 +330,45 @@ static task_slice_t sedf_do_schedule(s_time_t now) int cpu = current->processor; struct list_head *runq = RUNQ(cpu); struct list_head *waitq = WAITQ(cpu); + #if (EXTRA > EXTRA_OFF) struct list_head *extraq = EXTRAQ(cpu); + #endif struct list_head *cur,*tmp; struct sedf_dom_info *curinf; task_slice_t ret; - + #if (EXTRA == EXTRA_SLICE_WEIGHT) + unsigned long oldscore; + #endif //idle tasks don't need any of the following stuf if (is_idle_task(inf->owner)) goto check_waitq; //idle task doesn't get scheduled on the runq - + + #if (EXTRA > EXTRA_OFF) if (unlikely(inf->extra == EXTRA_RUNNING)) { //special treatment of domains running in extra time inf->extra = EXTRA_AWARE; inf->cputime=0; inf->extra_time_tot += now - inf->sched_start; - extraq_del(current); //remove extradomain from head of the queue - if (domain_runnable(inf->owner)) - extraq_add_tail(current); //and add to the tail if it is runnable => round-robin + extraq_del(current); //remove extradomain from head of the queue + + #if (EXTRA == EXTRA_ROUNDR) + if (domain_runnable(current)) + extraq_add_tail(current); //and add to the tail if it is runnable => round-robin + #elif (EXTRA == EXTRA_SLICE_WEIGHT) + oldscore = inf->score; //update the score + inf->score = (inf->period << 10) / inf->slice; //use fixed point arithmetic with 10 bits + + if (domain_runnable(current)) + extraq_add_sort_update(current, oldscore); //add according to score: weighted round robin + #endif else - __del_from_queue(inf->owner); //if domain blocked in extratime remove it from waitq(/runq) as well + __del_from_queue(inf->owner); //if domain blocked in extratime remove it from waitq(/runq) as well } - else { + else + #endif + { //current domain is running in real time mode //update the domains cputime inf->cputime += now - inf->sched_start; @@ -315,13 +384,18 @@ static task_slice_t sedf_do_schedule(s_time_t now) inf->cputime -= inf->slice; if (inf->period < inf->period_orig) { - //this domain runs in latency scaling mode - inf->period *= 2; - inf->slice *= 2; - if ((inf->period > inf->period_orig) || (inf->slice > inf->slice_orig)) { - //now switch back to standard timing - inf->period = inf->period_orig; - inf->slice = inf->slice_orig; + //this domain runs in latency scaling or burst mode + #if (UNBLOCK == UNBLOCK_BURST) + if (now - inf->absunblock >= 2 * inf->period) + #endif + { + inf->period *= 2; + inf->slice *= 2; + if ((inf->period > inf->period_orig) || (inf->slice > inf->slice_orig)) { + //now switch back to standard timing + inf->period = inf->period_orig; + inf->slice = inf->slice_orig; + } } } inf->absdead += inf->period; //set next deadline @@ -333,9 +407,11 @@ static task_slice_t sedf_do_schedule(s_time_t now) __add_to_waitqueue_sort(inf->owner); else { //we have a blocked realtime task - inf->absblock=now; + inf->absblock = now; + #if (EXTRA > EXTRA_OFF) if (inf->extra == EXTRA_AWARE) extraq_del(inf->owner); //remove a blocked domain from the extraq aswell + #endif } } check_waitq: @@ -393,13 +469,16 @@ check_waitq: if (!list_empty(waitq)) { waitinf = list_entry(waitq->next,struct sedf_dom_info,list); //we could not find any suitable domain => look for domains that are aware of extratime + #if (EXTRA > EXTRA_OFF) if (!list_empty(extraq) && (PERIOD_BEGIN(waitinf) - now >= EXTRA_QUANTUM)) { runinf = list_entry(extraq->next,struct sedf_dom_info,extralist); runinf->extra = EXTRA_RUNNING; ret.task = runinf->owner; ret.time = EXTRA_QUANTUM; } - else { + else + #endif + { //we have an empty run- and extraqueue or too less time => idle task! ret.task = IDLETASK(cpu); ret.time = PERIOD_BEGIN(waitinf) - now; @@ -420,13 +499,19 @@ check_waitq: static void sedf_sleep(struct domain *d) { PRINT(2,"sedf_sleep was called, domain-id %i\n",d->id); - if ( test_bit(DF_RUNNING, &d->flags) ) + if ( test_bit(DF_RUNNING, &d->flags) ) { +#ifdef ADV_SCHED_HISTO + adv_sched_hist_start(d->processor); +#endif cpu_raise_softirq(d->processor, SCHEDULE_SOFTIRQ); + } else { if ( __task_on_queue(d) ) __del_from_queue(d); + #if (EXTRA > EXTRA_OFF) if (extraq_on(d)) extraq_del(d); + #endif } } @@ -490,9 +575,8 @@ static void sedf_sleep(struct domain *d) { * (or even worse: allocate a new full slice for the domain) * -either behaviour can lead to missed deadlines in other domains as opposed to approaches 1,2a,2b */ -static inline void unblock_short_vcons(struct sedf_dom_info* inf, s_time_t now) { inf->absdead += inf->period;} -static inline void unblock_long_vcons(struct sedf_dom_info* inf, s_time_t now) { - inf->absdead += ((now - inf->absdead) / inf->period + 1) * inf->period; //very conservative +static inline void unblock_short_vcons(struct sedf_dom_info* inf, s_time_t now) { + inf->absdead += inf->period; inf->cputime = 0; } @@ -500,19 +584,23 @@ static inline void unblock_short_cons(struct sedf_dom_info* inf, s_time_t now) { inf->cputime += now - inf->absblock; //treat blocked time as consumed by the domain if (inf->cputime + EXTRA_QUANTUM > inf->slice) { //we don't have a reasonable amount of time in our slice left :( - inf->cputime=0; - inf->absdead += inf->period; //start in next period! + unblock_short_vcons(inf, now); //start in next period! } else - inf->short_cont++; + inf->short_cont++; //we let the domain run in the current period } + +static inline void unblock_long_vcons(struct sedf_dom_info* inf, s_time_t now) { + inf->absdead += ((now - inf->absdead) / inf->period + 1) * inf->period; //very conservative + inf->cputime = 0; +} + static inline void unblock_long_cons_a(struct sedf_dom_info* inf, s_time_t now) { inf->cputime = (now - inf->absdead) % inf->period; //treat the time the domain was blocked in the CURRENT //period as consumed by the domain if (inf->cputime + EXTRA_QUANTUM > inf->slice) { //we don't have a reasonable amount of time in our slice left :( - inf->cputime=0; - inf->absdead += inf->period; //start in next period! + unblock_long_vcons(inf, now); //start in next period! } } static inline void unblock_long_cons_b(struct sedf_dom_info* inf,s_time_t now) { @@ -521,20 +609,52 @@ static inline void unblock_long_cons_b(struct sedf_dom_info* inf,s_time_t now) { } static inline void unblock_long_cons_c(struct sedf_dom_info* inf,s_time_t now) { if (likely(inf->latency)) { - //sclae the slice and period accordingly to the latency hint + //scale the slice and period accordingly to the latency hint inf->period = inf->latency; //reduce period temporarily to the latency hint - ASSERT((inf->latency < ULONG_MAX) && (inf->slice_orig < ULONG_MAX)); //this results in max. 4s slice/period length - inf->slice = (inf->latency * inf->slice_orig) / inf->period_orig; //scale slice accordingly, so that utilisation stays the same + ASSERT((inf->period < ULONG_MAX) && (inf->slice_orig < ULONG_MAX)); //this results in max. 4s slice/period length + inf->slice = (inf->period * inf->slice_orig) / inf->period_orig; //scale slice accordingly, so that utilisation stays the same } else { //we don't have a latency hint.. use some other technique - inf->absdead = now + inf->period; //Conservative 2b... - inf->cputime = 0; + unblock_long_cons_b(inf, now); } } +//a new idea of dealing with short blocks: burst period scaling +static inline void unblock_short_burst(struct sedf_dom_info* inf, s_time_t now) { + inf->cputime += now - inf->absblock; //treat blocked time as consumed by the domain + + if (inf->cputime + EXTRA_QUANTUM <= inf->slice) { + inf->short_cont++; //we let the domain run in the current period + } + else { + //we don't have a reasonable amount of time in our slice left => switch to burst mode + if (likely(inf->absunblock)) { + inf->period = now - inf->absunblock; //set the period-length to the current blocking interval + ASSERT((inf->period < ULONG_MAX) && (inf->slice_orig < ULONG_MAX));//this results in max. 4s slice/period length + inf->slice = (inf->period * inf->slice_orig) / inf->period_orig;//scale slice accordingly, so that utilisation stays the same + } + else { + inf->cputime=0; //in Case we haven't unblocked before + inf->absdead += inf->period; //start in next period! + } + } + inf->absunblock = now; +} +static inline void unblock_long_burst(struct sedf_dom_info* inf,s_time_t now) { + if (unlikely(inf->latency && (inf->period > inf->latency))) { + //sclae the slice and period accordingly to the latency hint + inf->period = inf->latency; //reduce period temporarily to the latency hint + ASSERT((inf->period < ULONG_MAX) && (inf->slice_orig < ULONG_MAX)); //this results in max. 4s slice/period length + inf->slice = (inf->period * inf->slice_orig) / inf->period_orig; //scale slice accordingly, so that utilisation stays the same + } + else { + //we don't have a latency hint.. or we are currently in "burst mode": use some other technique + //this should be in fact the normal way of operation, when we are in sync with the device! + unblock_long_cons_b(inf, now); + } + inf->absunblock = now; +} -/*static inline void unblock_short_vcons -static inline void unblock_short_vcons*/ void sedf_wake(struct domain *d) { //for the first try just implement the "very conservative" way of waking domains up s_time_t now = NOW(); @@ -555,16 +675,19 @@ void sedf_wake(struct domain *d) { if (unlikely(inf->absdead == 0)) inf->absdead = now + inf->slice; //initial setup of the deadline - //very conservative way of unblocking - //make sure that the start of the period for this - //domain is happening in the future PRINT(3,"waking up domain %i (deadl= %llu period= %llu now= %llu)\n",d->id,inf->absdead,inf->period,now); inf->block_tot++; if (unlikely(now< PERIOD_BEGIN(inf))) { //this might happen, imagine unblocking in extra-time! if (likely(inf->extra == EXTRA_AWARE)) + #if (EXTRA == EXTRA_ROUNDR) extraq_add_tail(d); //SD: Could extraq_add_head be better? + #elif (EXTRA == EXTRA_SLICE_WEIGHT) + extraq_add_sort_update(d, 0); + #else + ; + #endif //else //This is very very unlikely, ie. might even be an error?! } @@ -572,19 +695,48 @@ void sedf_wake(struct domain *d) { if (now < inf->absdead) { //short blocking inf->short_block_tot++; - //unblock_short_vcons(inf, now); + #if (UNBLOCK <= UNBLOCK_ATROPOS) + unblock_short_vcons(inf, now); + #elif (UNBLOCK == UNBLOCK_SHORT_RESUME) unblock_short_cons(inf, now); - if (inf->extra == EXTRA_AWARE) + #elif (UNBLOCK == UNBLOCK_BURST) + unblock_short_burst(inf, now); + #endif + + if (inf->extra == EXTRA_AWARE) + #if (EXTRA == EXTRA_OFF) + ; + #elif (EXTRA == EXTRA_ROUNDR) extraq_add_head(d); + #elif (EXTRA == EXTRA_SLICE_WEIGHT) + extraq_add_sort_update(d, 0); + #endif } else { //long blocking inf->long_block_tot++; //PRINT(3,"old=%llu ",inf->absdead); - //unblock_long_vcons(inf, now); - unblock_long_cons_c(inf,now); + #if (UNBLOCK == UNBLOCK_ISOCHRONOUS_EDF) + unblock_long_vcons(inf, now); + #elif (UNBLOCK == UNBLOCK_EDF) + unblock_long_cons_b(inf, now); + #elif (UNBLOCK == UNBLOCK_ATROPOS) + unblock_long_cons_c(inf, now); + #elif (UNBLOCK == UNBLOCK_SHORT_RESUME) + unblock_long_cons_b(inf, now); + //unblock_short_cons_c(inf, now); + #elif (UNBLOCK == UNBLOCK_BURST) + unblock_long_burst(inf, now); + #endif + if (inf->extra == EXTRA_AWARE) - extraq_add_tail(d); + #if (EXTRA == EXTRA_OFF) + ; + #elif (EXTRA == EXTRA_ROUNDR) + extraq_add_head(d); + #elif (EXTRA == EXTRA_SLICE_WEIGHT) + extraq_add_sort_update(d,0); + #endif } } PRINT(3,"waking up domain %i (deadl= %llu period= %llu now= %llu)\n",d->id,inf->absdead,inf->period,now); @@ -592,7 +744,7 @@ void sedf_wake(struct domain *d) { PRINT(3,"added to waitq\n"); //do some statistics here... - if (inf->absblock!=0) { + if (inf->absblock != 0) { inf->block_time_tot += now - inf->absblock; inf->penalty_time_tot += PERIOD_BEGIN(inf) + inf->cputime - inf->absblock; /*if (DOM_INFO(d)->block_time_tot) @@ -607,8 +759,12 @@ void sedf_wake(struct domain *d) { //and check, whether we are idling and this domain is extratime aware if ((PERIOD_BEGIN(inf) < schedule_data[d->processor].s_timer.expires) || (is_idle_task(schedule_data[d->processor].curr) && (now + EXTRA_QUANTUM < schedule_data[d->processor].s_timer.expires) && - (inf->extra == EXTRA_AWARE))) + (inf->extra == EXTRA_AWARE))) { +#ifdef ADV_SCHED_HISTO + adv_sched_hist_start(d->processor); +#endif cpu_raise_softirq(d->processor, SCHEDULE_SOFTIRQ); + } } @@ -616,9 +772,9 @@ void sedf_wake(struct domain *d) { static void sedf_dump_domain(struct domain *d) { printk("%u has=%c ", d->id, test_bit(DF_RUNNING, &d->flags) ? 'T':'F'); - printk("p=%llu sl=%llu ddl=%llu w=%u c=%llu xtr(%s)=%llu", + printk("p=%llu sl=%llu ddl=%llu w=%u c=%llu sc=%i xtr(%s)=%llu", DOM_INFO(d)->period, DOM_INFO(d)->slice, DOM_INFO(d)->absdead, DOM_INFO(d)->weight, d->cpu_time, - DOM_INFO(d)->extra ? "yes" : "no", DOM_INFO(d)->extra_time_tot); + DOM_INFO(d)->score, DOM_INFO(d)->extra ? "yes" : "no", DOM_INFO(d)->extra_time_tot); if (d->cpu_time !=0) printf(" (%lu%)", (DOM_INFO(d)->extra_time_tot * 100) / d->cpu_time); if (DOM_INFO(d)->block_time_tot!=0) diff --git a/xen/common/schedule.c b/xen/common/schedule.c index 86c39e1a95..3334c19ce0 100644 --- a/xen/common/schedule.c +++ b/xen/common/schedule.c @@ -34,6 +34,8 @@ string_param("sched", opt_sched); /*#define WAKE_HISTO*/ /*#define BLOCKTIME_HISTO*/ +/*#define ADV_SCHED_HISTO*/ +//#include #if defined(WAKE_HISTO) #define BUCKETS 31 @@ -198,6 +200,10 @@ void domain_wake(struct domain *d) /* Block the currently-executing domain until a pertinent event occurs. */ long do_block(void) { +#ifdef ADV_SCHED_HISTO + adv_sched_hist_start(current->processor); +#endif + ASSERT(current->id != IDLE_DOMAIN_ID); current->shared_info->vcpu_data[0].evtchn_upcall_mask = 0; set_bit(DF_BLOCKED, ¤t->flags); @@ -209,6 +215,10 @@ long do_block(void) /* Voluntarily yield the processor for this allocation. */ static long do_yield(void) { +#ifdef ADV_SCHED_HISTO + adv_sched_hist_start(current->processor); +#endif + TRACE_2D(TRC_SCHED_YIELD, current->id, current); __enter_scheduler(); return 0; @@ -328,8 +338,14 @@ void __enter_scheduler(void) perfc_incrc(sched_run); spin_lock_irq(&schedule_data[cpu].schedule_lock); - + +#ifdef ADV_SCHED_HISTO + adv_sched_hist_from_stop(cpu); +#endif now = NOW(); +#ifdef ADV_SCHED_HISTO + adv_sched_hist_start(cpu); +#endif rem_ac_timer(&schedule_data[cpu].s_timer); @@ -369,9 +385,12 @@ void __enter_scheduler(void) if ( !is_idle_task(next) ) update_dom_time(next->shared_info); - if ( unlikely(prev == next) ) + if ( unlikely(prev == next) ) { +#ifdef ADV_SCHED_HISTO + adv_sched_hist_to_stop(cpu); +#endif return; - + } perfc_incrc(sched_ctx); #if defined(WAKE_HISTO) @@ -393,7 +412,6 @@ void __enter_scheduler(void) #endif TRACE_2D(TRC_SCHED_SWITCH, next->id, next); - switch_to(prev, next); /* @@ -407,7 +425,10 @@ void __enter_scheduler(void) /* Mark a timer event for the newly-scheduled domain. */ if ( !is_idle_task(next) ) send_guest_virq(next, VIRQ_TIMER); - + +#ifdef ADV_SCHED_HISTO + adv_sched_hist_to_stop(cpu); +#endif schedule_tail(next); BUG(); @@ -431,6 +452,10 @@ int idle_cpu(int cpu) /* The scheduler timer: force a run through the scheduler*/ static void s_timer_fn(unsigned long unused) { +#ifdef ADV_SCHED_HISTO + adv_sched_hist_start(current->processor); +#endif + TRACE_0D(TRC_SCHED_S_TIMER_FN); raise_softirq(SCHEDULE_SOFTIRQ); perfc_incrc(sched_irq); @@ -572,6 +597,63 @@ void reset_sched_histo(unsigned char key) schedule_data[j].hist[i] = 0; } #else +#if defined(ADV_SCHED_HISTO) +void print_sched_histo(unsigned char key) +{ + int i, j, k,t; + printf("Hello!\n"); + for ( k = 0; k < smp_num_cpus; k++ ) + { + j = 0; + t = 0; + printf ("CPU[%02d]: scheduler latency histogram FROM (ms:[count])\n", k); + for ( i = 0; i < BUCKETS; i++ ) + { + //if ( schedule_data[k].hist[i] != 0 ) + { + t += schedule_data[k].from_hist[i]; + if ( i < BUCKETS-1 ) + printk("%3d:[%7u] ", i, schedule_data[k].from_hist[i]); + else + printk(" >:[%7u] ", schedule_data[k].from_hist[i]); + //if ( !(++j % 5) ) + printk("\n"); + } + } + printk("\nTotal: %i\n",t); + } + for ( k = 0; k < smp_num_cpus; k++ ) + { + j = 0; t = 0; + printf ("CPU[%02d]: scheduler latency histogram TO (ms:[count])\n", k); + for ( i = 0; i < BUCKETS; i++ ) + { + //if ( schedule_data[k].hist[i] != 0 ) + { + t += schedule_data[k].from_hist[i]; + if ( i < BUCKETS-1 ) + printk("%3d:[%7u] ", i, schedule_data[k].to_hist[i]); + else + printk(" >:[%7u] ", schedule_data[k].to_hist[i]); + //if ( !(++j % 5) ) + printk("\n"); + } + } + printk("\nTotal: %i\n",t); + } + +} +void reset_sched_histo(unsigned char key) +{ + int i, j; + for ( j = 0; j < smp_num_cpus; j++ ) { + for ( i=0; i < BUCKETS; i++ ) + schedule_data[j].to_hist[i] = schedule_data[j].from_hist[i] = 0; + schedule_data[j].save_tsc = 0; + } +} +#else void print_sched_histo(unsigned char key) { } void reset_sched_histo(unsigned char key) { } #endif +#endif diff --git a/xen/include/xen/sched-if.h b/xen/include/xen/sched-if.h index 15f992614a..a33b0559ed 100644 --- a/xen/include/xen/sched-if.h +++ b/xen/include/xen/sched-if.h @@ -7,7 +7,9 @@ * Portions by Mark Williamson are (C) 2004 Intel Research Cambridge */ -#define BUCKETS 10 +//#define ADV_SCHED_HISTO +#define BUCKETS 10 +/*300*/ typedef struct schedule_data_st { @@ -17,6 +19,11 @@ typedef struct schedule_data_st struct domain *idle; /* idle task for this cpu */ void * sched_priv; struct ac_timer s_timer; /* scheduling timer */ +#ifdef ADV_SCHED_HISTO + u32 to_hist[BUCKETS]; + u32 from_hist[BUCKETS]; + u64 save_tsc; +#endif #ifdef BUCKETS u32 hist[BUCKETS]; /* for scheduler latency histogram */ #endif @@ -55,5 +62,3 @@ struct scheduler /* per CPU scheduler information */ extern schedule_data_t schedule_data[]; - - From ffcf0352a2d552197e34214121bb85dc9583d247 Mon Sep 17 00:00:00 2001 From: "sd386@font.cl.cam.ac.uk" Date: Wed, 9 Mar 2005 14:35:02 +0000 Subject: [PATCH 5/9] bitkeeper revision 1.1159.170.105 (422f0996UBOVE7bb-GrY-kHTfrNOMQ) Implemented twol level extra-time scheduling and added an advanced scheduling history --- .rootkeys | 1 + xen/common/sched_sedf.c | 688 ++++++++++++++++++++----------- xen/include/xen/adv_sched_hist.h | 40 ++ 3 files changed, 498 insertions(+), 231 deletions(-) create mode 100644 xen/include/xen/adv_sched_hist.h diff --git a/.rootkeys b/.rootkeys index 3f6124f99e..be0623acf5 100644 --- a/.rootkeys +++ b/.rootkeys @@ -854,6 +854,7 @@ 3ddb79c25UE59iu4JJcbRalx95mvcg xen/include/public/xen.h 3e397e66m2tO3s-J8Jnr7Ws_tGoPTg xen/include/xen/ac_timer.h 40715b2epYl2jBbxzz9CI2rgIca7Zg xen/include/xen/acpi.h +422f0995xCgnbsVhTjSncnqIABs64g xen/include/xen/adv_sched_hist.h 3ddb79c0c0cX_DZE209-Bb-Rx1v-Aw xen/include/xen/cache.h 3f840f12CkbYSlwMrY2S11Mpyxg7Nw xen/include/xen/compiler.h 3ddb79c259jh8hE7vre_8NuE7nwNSA xen/include/xen/config.h diff --git a/xen/common/sched_sedf.c b/xen/common/sched_sedf.c index 01e944d554..1509432572 100644 --- a/xen/common/sched_sedf.c +++ b/xen/common/sched_sedf.c @@ -5,6 +5,14 @@ * based on code by Mark Williamson (C) 2004 Intel Research Cambridge */ +/* + TODO: + TESTING! + tracing instead of PRINTs +*/ + + + #include #include #include @@ -25,8 +33,8 @@ if ((_f)<=SEDFLEVEL) printk(_a ); #define UNBLOCK_ATROPOS 3 #define UNBLOCK_SHORT_RESUME 4 #define UNBLOCK_BURST 5 - -#define UNBLOCK UNBLOCK_BURST +#define UNBLOCK_EXTRA_SUPPORT 6 +#define UNBLOCK UNBLOCK_EXTRA_SUPPORT //various ways of treating extra-time #define EXTRA_OFF 1 @@ -34,20 +42,19 @@ if ((_f)<=SEDFLEVEL) printk(_a ); #define EXTRA_SLICE_WEIGHT 3 #define EXTRA_BLOCK_WEIGHT 4 -#define EXTRA EXTRA_OFF +#define EXTRA EXTRA_BLOCK_WEIGHT - -/* - TODO: - TESTING! - tracing instead of PRINTs -*/ - - -#define TRC_SEDF 0xBEEF0000 #define EXTRA_NONE (0) #define EXTRA_AWARE (1) -#define EXTRA_RUNNING (2) +#define EXTRA_RUN_PEN (2) +#define EXTRA_RUN_UTIL (4) +#define EXTRA_WANT_PEN_Q (8) +#define EXTRA_PEN_Q (0) +#define EXTRA_UTIL_Q (1) + +#define extra_runs(inf) ((inf->extra) & 6) +#define extra_get_cur_q(inf) (((inf->extra & 6) >> 1)-1) + #define EXTRA_QUANTUM (MICROSECS(500)) #define WEIGHT_PERIOD (MILLISECS(100)) #define WEIGHT_SAFETY (MILLISECS(5)) @@ -70,7 +77,7 @@ struct sedf_dom_info s_time_t latency; //extra-time status of domain short extra; - //weights for "Scheduling for beginners/ lazy/ etc." + //weights for "Scheduling for beginners/ lazy/ etc." ;) short weight; //Bookkeeping @@ -90,46 +97,58 @@ struct sedf_dom_info int short_block_tot; int long_block_tot; int short_cont; + int pen_extra_blocks; + int pen_extra_slices; }; struct sedf_cpu_info { struct list_head runnableq; struct list_head waitq; - struct list_head extraq; + struct list_head extraq[2]; }; #define DOM_INFO(d) ((struct sedf_dom_info *)((d)->sched_priv)) #define CPU_INFO(cpu) ((struct sedf_cpu_info *)schedule_data[cpu].sched_priv) #define LIST(d) (&DOM_INFO(d)->list) -#define EXTRALIST(d) (&DOM_INFO(d)->extralist) +#define EXTRALIST(d,i) (&(DOM_INFO(d)->extralist[i])) #define RUNQ(cpu) (&CPU_INFO(cpu)->runnableq) #define WAITQ(cpu) (&CPU_INFO(cpu)->waitq) -#define EXTRAQ(cpu) (&CPU_INFO(cpu)->extraq) +#define EXTRAQ(cpu,i) (&(CPU_INFO(cpu)->extraq[i])) #define IDLETASK(cpu) ((struct domain *)schedule_data[cpu].idle) #define PERIOD_BEGIN(inf) ((inf)->absdead - (inf)->period) +#define MIN(x,y) (((x)<(y))?(x):(y)) +#define DIV_UP(x,y) (((x) + (y) - 1) / y) + static xmem_cache_t *dom_info_cache; -static inline void extraq_add_head(struct domain *d) -{ - list_add(EXTRALIST(d), EXTRAQ(d->processor)); +static void sedf_dump_cpu_state(int i); + +static inline int extraq_on(struct domain *d, int i) { + return ((EXTRALIST(d,i)->next != NULL) && (EXTRALIST(d,i)->next != EXTRALIST(d,i))); } -static inline void extraq_add_tail(struct domain *d) +static inline void extraq_add_head(struct domain *d, int i) { - list_add_tail(EXTRALIST(d), EXTRAQ(d->processor)); + list_add(EXTRALIST(d,i), EXTRAQ(d->processor,i)); } -static inline void extraq_del(struct domain *d) +static inline void extraq_add_tail(struct domain *d, int i) { - struct list_head *list = EXTRALIST(d); - list_del(list); - list->next = NULL; + list_add_tail(EXTRALIST(d,i), EXTRAQ(d->processor,i)); } -static inline int extraq_on(struct domain *d) { - return (((EXTRALIST(d))->next != NULL) && (EXTRALIST(d)->next != EXTRALIST(d))); +static inline void extraq_del(struct domain *d, int i) +{ + struct list_head *list = EXTRALIST(d,i); + /*if (!extraq_on(d,i)) { + PRINT(0,"extraq_del: domain %i is NOT on L%i extraq! HALTING\n",d->id,i); + sedf_dump_cpu_state(0);(*((int*)0))++; + }*/ + PRINT(3, "Removing domain %i from L%i extraq\n", d->id,i); + list_del(list); + list->next = NULL; } /* adds a domain to the queue of processes which are aware of extra time. List is sorted by score, @@ -137,50 +156,57 @@ static inline int extraq_on(struct domain *d) { a fixed value from each entry, in order to avoid overflow. The algorithm works by simply charging each domain that recieved extratime with an inverse of its weight. */ -static inline void extraq_add_sort_update(struct domain *d, unsigned long sub) { +static inline void extraq_add_sort_update(struct domain *d, int i, int sub) { struct list_head *cur; struct sedf_dom_info *curinf; - PRINT(3,"Adding domain %i (score= %llu) to extraq\n",d->id,DOM_INFO(d)->score); + /*if (extraq_on(d,i)) { + PRINT(0,"extraq_add_sort_update: domain %i is already on L%i extraq! HALTING\n",d->id,i); + sedf_dump_cpu_state(0);(*((int*)0))++; + }*/ + PRINT(3, "Adding domain %i (score= %i, short_pen= %lli) to L%i extraq\n", d->id, + DOM_INFO(d)->score[i], DOM_INFO(d)->short_block_lost_tot, i); //iterate through all elements to find our "hole" and on our way update all the other scores - list_for_each(cur,EXTRAQ(d->processor)){ - curinf = list_entry(cur,struct sedf_dom_info,extralist); - curinf->score -= sub; - if (DOM_INFO(d)->score < curinf->score) + list_for_each(cur,EXTRAQ(d->processor,i)){ + curinf = list_entry(cur,struct sedf_dom_info,extralist[i]); + curinf->score[i] -= sub; + if (DOM_INFO(d)->score[i] < curinf->score[i]) break; else - PRINT(4,"\tbehind domain %i (score= %llu)\n",curinf->owner->id,curinf->score); + PRINT(4,"\tbehind domain %i (score= %i)\n", curinf->owner->id, curinf->score[i]); } - //cur now contains the element, before which we'll enqueue - PRINT(3,"\tlist_add to %x\n",cur->prev); - list_add(EXTRALIST(d),cur->prev); + //cur now contains the element, before which we'll enq ueue + PRINT(3, "\tlist_add to %x\n", cur->prev); + list_add(EXTRALIST(d,i),cur->prev); //continue updating the extraq - if ((cur != EXTRAQ(d->processor)) && sub) - for (cur = cur->next; cur != EXTRAQ(d->processor); cur = cur-> next) { - curinf = list_entry(cur,struct sedf_dom_info,extralist); - curinf->score -= sub; - PRINT(4,"\tupdating domain %i (score= %llu)\n",curinf->owner->id,curinf->score); + if ((cur != EXTRAQ(d->processor,i)) && sub) + for (cur = cur->next; cur != EXTRAQ(d->processor,i); cur = cur-> next) { + curinf = list_entry(cur,struct sedf_dom_info,extralist[i]); + curinf->score[i] -= sub; + PRINT(4, "\tupdating domain %i (score= %llu)\n", curinf->owner->id, curinf->score[i]); } } static inline void extraq_check(struct domain *d) { - if (extraq_on(d)) { + if (extraq_on(d, EXTRA_UTIL_Q)) { PRINT(2,"Dom %i is on extraQ\n",d->id); - if (DOM_INFO(d)->extra == EXTRA_NONE) { - extraq_del(d); - PRINT(2,"Removed dom %i from extraQ\n",d->id); + if (!(DOM_INFO(d)->extra & EXTRA_AWARE)) { + extraq_del(d, EXTRA_UTIL_Q); + PRINT(2,"Removed dom %i from L1 extraQ\n",d->id); } } else { - PRINT(2,"Dom %i is NOT on extraQ\n",d->id); - if (DOM_INFO(d)->extra != EXTRA_NONE) { - PRINT(2,"Added dom %i to extraQ\n",d->id); - extraq_add_sort_update(d, 0); + PRINT(2,"Dom %i is NOT on L1 extraQ\n",d->id); + if ((DOM_INFO(d)->extra & EXTRA_AWARE) && domain_runnable(d)) { + extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); + PRINT(2,"Added dom %i to L1 extraQ\n",d->id); + //TODO: add extraq_add_tail } } } static inline void __del_from_queue(struct domain *d) { struct list_head *list = LIST(d); + PRINT(3,"Removing domain %i (bop= %llu) from runq/waitq\n",d->id,PERIOD_BEGIN(DOM_INFO(d))); list_del(list); list->next = NULL; } @@ -192,7 +218,7 @@ static inline void __add_to_waitqueue_sort(struct domain *d) { struct list_head *cur; struct sedf_dom_info *curinf; - PRINT(3,"Adding domain %i (bop= %llu) to waitq\n",d->id,PERIOD_BEGIN(DOM_INFO(d))); + PRINT(3,"Adding domain %i (bop= %llu) to waitq\n",d->id,PERIOD_BEGIN(DOM_INFO(d))); //iterate through all elements to find our "hole" list_for_each(cur,WAITQ(d->processor)){ curinf = list_entry(cur,struct sedf_dom_info,list); @@ -243,13 +269,12 @@ static int sedf_init_scheduler() { schedule_data[i].sched_priv = xmalloc(sizeof(struct sedf_cpu_info)); if ( schedule_data[i].sched_priv == NULL ) return -1; - INIT_LIST_HEAD(WAITQ(i));//used for Latency Scaling + INIT_LIST_HEAD(WAITQ(i)); INIT_LIST_HEAD(RUNQ(i)); - INIT_LIST_HEAD(EXTRAQ(i)); + INIT_LIST_HEAD(EXTRAQ(i,EXTRA_PEN_Q)); + INIT_LIST_HEAD(EXTRAQ(i,EXTRA_UTIL_Q)); } - //we could not find any suitable domain => look for domains that are aware of extratime - dom_info_cache = xmem_cache_create( - "SEDF dom info", sizeof(struct sedf_dom_info), 0, 0, 0, NULL); + dom_info_cache = xmem_cache_create("SEDF dom info", sizeof(struct sedf_dom_info), 0, 0, 0, NULL); if ( dom_info_cache == NULL ) { printk("Could not allocate SLAB cache.\n"); @@ -294,7 +319,8 @@ static void sedf_add_task(struct domain *d) } inf->period_orig = inf->period; inf->slice_orig = inf->slice; INIT_LIST_HEAD(&(inf->list)); - INIT_LIST_HEAD(&(inf->extralist)); + INIT_LIST_HEAD(&(inf->extralist[EXTRA_PEN_Q])); + INIT_LIST_HEAD(&(inf->extralist[EXTRA_UTIL_Q])); } /* Frees memory used by domain info */ @@ -312,112 +338,72 @@ static int sedf_init_idle_task(struct domain *d) { return -1; sedf_add_task(d); - DOM_INFO(d)->absdead=0; + DOM_INFO(d)->absdead = 0; set_bit(DF_RUNNING, &d->flags); //the idle task doesn't have to turn up on any list... return 0; } -#define MIN(x,y) (((x)<(y))?(x):(y)) -/* Main scheduling function - * Reasons for calling this function are: - * -timeslice for the current period used up - * -domain on waitqueue has started it's period - * -and various others ;) in general: determin which domain to run next*/ -static task_slice_t sedf_do_schedule(s_time_t now) -{ - struct sedf_dom_info *inf = DOM_INFO(current); - int cpu = current->processor; - struct list_head *runq = RUNQ(cpu); - struct list_head *waitq = WAITQ(cpu); - #if (EXTRA > EXTRA_OFF) - struct list_head *extraq = EXTRAQ(cpu); - #endif - struct list_head *cur,*tmp; - struct sedf_dom_info *curinf; - task_slice_t ret; - #if (EXTRA == EXTRA_SLICE_WEIGHT) - unsigned long oldscore; - #endif +/* handles the rescheduling, bookkeeping of domains running in their realtime-time :)*/ +static inline void desched_edf_dom (s_time_t now, struct domain* d) { + struct sedf_dom_info* inf = DOM_INFO(d); - //idle tasks don't need any of the following stuf - if (is_idle_task(inf->owner)) - goto check_waitq; //idle task doesn't get scheduled on the runq - - #if (EXTRA > EXTRA_OFF) - if (unlikely(inf->extra == EXTRA_RUNNING)) { - //special treatment of domains running in extra time - inf->extra = EXTRA_AWARE; - inf->cputime=0; - inf->extra_time_tot += now - inf->sched_start; + //current domain is running in real time mode + inf->cputime += now - inf->sched_start; //update the domains cputime + //scheduling decisions, which don't remove the running domain from the runq + if ((inf->cputime < inf->slice) && domain_runnable(d)) + return; //there is nothing to do with the running task - extraq_del(current); //remove extradomain from head of the queue + __del_from_queue(d); + /*if (__task_on_queue(current)) { + PRINT(0,"domain %i was removed but still on run/waitq => HALT\n",current->id); + sedf_dump_cpu_state(0);(*((int*)0))++; + }*/ + + //manage bookkeeping (i.e. calculate next deadline, memorize overun time of slice) of finished domains + if (inf->cputime >= inf->slice) { + inf->cputime -= inf->slice; - #if (EXTRA == EXTRA_ROUNDR) - if (domain_runnable(current)) - extraq_add_tail(current); //and add to the tail if it is runnable => round-robin - #elif (EXTRA == EXTRA_SLICE_WEIGHT) - oldscore = inf->score; //update the score - inf->score = (inf->period << 10) / inf->slice; //use fixed point arithmetic with 10 bits - - if (domain_runnable(current)) - extraq_add_sort_update(current, oldscore); //add according to score: weighted round robin - #endif - else - __del_from_queue(inf->owner); //if domain blocked in extratime remove it from waitq(/runq) as well - } - else - #endif - { - //current domain is running in real time mode - //update the domains cputime - inf->cputime += now - inf->sched_start; - //scheduling decisions, which don't move the running domain to any queues - if ((inf->cputime < inf->slice) && domain_runnable(inf->owner)) - goto check_waitq; //there is nothing to do with the running task - - //remove tasks that can't run - __del_from_queue(inf->owner); - - //manage bookkeeping (i.e. calculate next deadline, memorize overun time of slice) of finished domains - if (inf->cputime >= inf->slice) { - inf->cputime -= inf->slice; - - if (inf->period < inf->period_orig) { - //this domain runs in latency scaling or burst mode - #if (UNBLOCK == UNBLOCK_BURST) - if (now - inf->absunblock >= 2 * inf->period) - #endif - { - inf->period *= 2; - inf->slice *= 2; - if ((inf->period > inf->period_orig) || (inf->slice > inf->slice_orig)) { - //now switch back to standard timing - inf->period = inf->period_orig; - inf->slice = inf->slice_orig; - } + if (inf->period < inf->period_orig) { + //this domain runs in latency scaling or burst mode + #if (UNBLOCK == UNBLOCK_BURST) + if (now - inf->absunblock >= 2 * inf->period) + #endif + { + inf->period *= 2; inf->slice *= 2; + if ((inf->period > inf->period_orig) || (inf->slice > inf->slice_orig)) { + inf->period = inf->period_orig; //update the domains cputime + inf->slice = inf->slice_orig; } } - inf->absdead += inf->period; //set next deadline - } - //if (inf->absdeadid,now,inf->absdead); - //add a runnable domain to the waitqueue - if (domain_runnable(inf->owner)) - __add_to_waitqueue_sort(inf->owner); - else { - //we have a blocked realtime task - inf->absblock = now; - #if (EXTRA > EXTRA_OFF) - if (inf->extra == EXTRA_AWARE) - extraq_del(inf->owner); //remove a blocked domain from the extraq aswell - #endif } + inf->absdead += inf->period; //set next deadline } -check_waitq: + //if (inf->absdeadid,now,inf->absdead); + if (domain_runnable(d)) //add a runnable domain to the waitqueue + __add_to_waitqueue_sort(d); + else { + inf->absblock = now; //we have a blocked realtime task + #if (EXTRA > EXTRA_OFF) + #if (EXTRA == EXTRA_BLOCK_WEIGHT) + if (extraq_on(d,EXTRA_PEN_Q)) extraq_del(d,EXTRA_PEN_Q); + if (extraq_on(d,EXTRA_UTIL_Q)) extraq_del(d,EXTRA_UTIL_Q); + #endif + #endif + } +} + +/* Update all elements on the queues */ +static inline void update_queues(s_time_t now, struct list_head* runq, struct list_head* waitq) { + struct list_head *cur,*tmp; + struct sedf_dom_info *curinf; + + PRINT(3,"Updating waitq..\n"); //check for the first elements of the waitqueue, whether their next period has already started list_for_each_safe(cur,tmp,waitq) { curinf = list_entry(cur,struct sedf_dom_info,list); + PRINT(4,"\tLooking @ dom %i\n",curinf->owner->id); if (PERIOD_BEGIN(curinf) <= now) { __del_from_queue(curinf->owner); __add_to_runqueue_sort(curinf->owner); @@ -426,29 +412,188 @@ check_waitq: break; } - //process the runq + PRINT(3,"Updating runq..\n"); + //process the runq, find domains that are on the runqueue which shouldn't be there list_for_each_safe(cur,tmp,runq) { curinf = list_entry(cur,struct sedf_dom_info,list); - if (unlikely(curinf->slice == 0)) { - //special treatment of elements with empty slice + PRINT(4,"\tLooking @ dom %i\n",curinf->owner->id); + if (unlikely(curinf->slice == 0)) { //ignore domains with empty slice + PRINT(4,"\tUpdating zero-slice domain %i\n",curinf->owner->id); __del_from_queue(curinf->owner); - curinf->absdead += curinf->period; - __add_to_waitqueue_sort(curinf->owner); + curinf->absdead += curinf->period; //move to their next period + __add_to_waitqueue_sort(curinf->owner); //and put them back into the queue } else { if (unlikely((curinf->absdead < now) || (curinf->cputime > curinf->slice))) { //we missed the deadline or the slice was already finished... might hapen because of dom_adj. //printk("Ouch! Domain %i missed deadline %llu\n",curinf->owner->id,curinf->absdead); + PRINT(4,"\tDomain %i exceeded it's deadline/slice (%llu / %llu) now: %llu cputime: %llu\n", + curinf->owner->id, curinf->absdead, curinf->slice, now, curinf->cputime); __del_from_queue(curinf->owner); - curinf->absdead += ((now - curinf->absdead + (curinf->period-1)) / curinf->period) * curinf->period; + curinf->absdead += curinf->period; //common case: we miss one period! + if (unlikely(curinf->absdead < now)) + curinf->absdead += DIV_UP(now - curinf->absdead, curinf->period) * curinf->period; //force start of period to be in future and aligned to period borders! - curinf->cputime = 0; + curinf->cputime = 0; //give a fresh slice __add_to_runqueue_sort(curinf->owner); } else break; } } + PRINT(3,"done updating the queues\n"); +} + +#if (EXTRA > EXTRA_OFF) +/*removes a domain from the head of the according extraQ and requeues it at a specified position: + round-robin extratime: end of extraQ + weighted ext.: insert in sorted list by score + if the domain is blocked / has regained its short-block-loss time it is not put on any queue*/ +static inline void desched_extra_dom(s_time_t now, struct domain* d) { + struct sedf_dom_info *inf = DOM_INFO(d); + int i = extra_get_cur_q(inf); + + #if (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) + unsigned long oldscore; + #endif + + inf->extra &= ~(EXTRA_RUN_PEN | EXTRA_RUN_UTIL); //unset all running flags + inf->cputime = 0; //fresh slice for the next run + inf->extra_time_tot += now - inf->sched_start; //accumulate total extratime + extraq_del(d, i); //remove extradomain from head of the queue + + #if (EXTRA == EXTRA_ROUNDR) + if (domain_runnable(d)) + extraq_add_tail(d, EXTRA_UTIL_Q); //and add to the tail if it is runnable => round-robin + #elif (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) + oldscore = inf->score[i]; //update the score + #if (EXTRA == EXTRA_BLOCK_WEIGHT) + if (i == EXTRA_PEN_Q) { + //domain was running in L0 extraq + //inf->short_block_lost_tot -= EXTRA_QUANTUM; //reduce block lost, probably more sophistication here! + inf->short_block_lost_tot -= now - inf->sched_start; + PRINT(3,"Domain %i: Short_block_lost: %lli\n",inf->owner->id,inf->short_block_lost_tot); + if (inf->short_block_lost_tot <= 0) { + PRINT(4,"Domain %i compensated short block loss!\n"); + inf->short_block_lost_tot = 0; //we have (over-)compensated our block penalty + inf->extra &= ~EXTRA_WANT_PEN_Q; //we don't want a place on the penalty queue anymore! + return; //do not add us on this block extraq again! + } + //we have to go again for another try in the block-extraq + inf->score[EXTRA_PEN_Q] = (inf->period << 10) / inf->short_block_lost_tot; + oldscore = 0; + } else + #endif + { + //domain was running in L1 extraq => score is inverse of utilization and is used somewhat incremental! + inf->score[EXTRA_UTIL_Q] = (inf->period << 10) / inf->slice;//use fixed point arithmetic with 10 bits + } + if (domain_runnable(d)) + extraq_add_sort_update(d, i, oldscore); //add according to score: weighted round robin + else { + inf->absblock = now; + /*if (!__task_on_queue(d)) + printf("Oops... We attempt to remove d %i from the waitq, but it is not on :(\n",d->id);*/ + __del_from_queue(d); //also remove this blocked domain from the waitq! + //make sure that we remove a blocked domain from the other extraq aswell (this caused hours of debugging!) + if (i == EXTRA_PEN_Q) { + if (extraq_on(d,EXTRA_UTIL_Q)) extraq_del(d,EXTRA_UTIL_Q); + } + else { + if (extraq_on(d,EXTRA_PEN_Q)) extraq_del(d,EXTRA_PEN_Q); + } + } + #endif + /*if (!domain_runnable(d)) { + if (extraq_on(d,EXTRA_UTIL_Q)) { + PRINT(0,"domain %i is blocked but still on L1 xq=> HALT\n",d->id); + sedf_dump_cpu_state(0);(*((int*)0))++; + } + if (__task_on_queue(d)) { + PRINT(0,"domain %i is blocked but still on run/waitq=> HALT\n",d->id); + sedf_dump_cpu_state(0);(*((int*)0))++; + } + }*/ +} +#endif + + +static inline task_slice_t sedf_do_extra_schedule(s_time_t now, s_time_t end_xt, struct list_head *extraq[], int cpu) { + task_slice_t ret; + struct sedf_dom_info *runinf; + //TODO write this stuff as a loop and pay attention to normal stuff! + + if (end_xt - now < EXTRA_QUANTUM) + goto return_idle; + + if (!list_empty(extraq[EXTRA_PEN_Q])) { + //we still have elements on the level 0 extraq => let those run first! + runinf = list_entry(extraq[EXTRA_PEN_Q]->next, struct sedf_dom_info, extralist[EXTRA_PEN_Q]); + runinf->extra |= EXTRA_RUN_PEN; + ret.task = runinf->owner; + ret.time = EXTRA_QUANTUM; + runinf->pen_extra_slices++; + } + else if (!list_empty(extraq[EXTRA_UTIL_Q])) { + //use elements from the normal extraqueue + runinf = list_entry(extraq[EXTRA_UTIL_Q]->next,struct sedf_dom_info,extralist[EXTRA_UTIL_Q]); + runinf->extra |= EXTRA_RUN_UTIL; + ret.task = runinf->owner; + ret.time = EXTRA_QUANTUM; + } + else + goto return_idle; + + return ret; + +return_idle: + ret.task = IDLETASK(cpu); + ret.time = end_xt - now; + return ret; +} +/* Main scheduling function + * Reasons for calling this function are: + * -timeslice for the current period used up + * -domain on waitqueue has started it's period + * -and various others ;) in general: determin which domain to run next*/ +static task_slice_t sedf_do_schedule(s_time_t now) +{ + int cpu = current->processor; + struct list_head *runq = RUNQ(cpu); + struct list_head *waitq = WAITQ(cpu); + #if (EXTRA > EXTRA_OFF) + struct sedf_dom_info *inf = DOM_INFO(current); + struct list_head *extraq[] = {EXTRAQ(cpu,EXTRA_PEN_Q),EXTRAQ(cpu, EXTRA_UTIL_Q)}; + #endif + task_slice_t ret; + //int i = 0; + //idle tasks don't need any of the following stuf + if (is_idle_task(current)) + goto check_waitq; //idle task doesn't get moved around on any queues + + #if (EXTRA > EXTRA_OFF) + if (unlikely(extra_runs(inf))) { + //i=1; + desched_extra_dom(now, current); //special treatment of domains running in extra time + } + else + #endif + { + //i=2; + desched_edf_dom(now, current); + } + /*if (!domain_runnable(current)) { + if (extraq_on(current,EXTRA_UTIL_Q)) { + PRINT(0,"domain %i is blocked but still on L1 xq branch %i=> HALT\n",current->id,i); + sedf_dump_cpu_state(0);(*((int*)0))++; + } + if (__task_on_queue(current)) { + PRINT(0,"domain %i is blocked but still on run/waitq branch %i=> HALT\n",current->id,i); + sedf_dump_cpu_state(0);(*((int*)0))++; + } + }*/ +check_waitq: + update_queues(now, runq, waitq); //now simply pick the first domain from the runqueue struct sedf_dom_info *runinf, *waitinf; @@ -464,33 +609,28 @@ check_waitq: else { ret.time = runinf->slice - runinf->cputime; } + goto sched_done; + } + + if (!list_empty(waitq)) { + waitinf = list_entry(waitq->next,struct sedf_dom_info,list); + //we could not find any suitable domain => look for domains that are aware of extratime + #if (EXTRA > EXTRA_OFF) + ret = sedf_do_extra_schedule(now, PERIOD_BEGIN(waitinf), extraq, cpu); + #else + ret.task = IDLETASK(cpu); + ret.time = PERIOD_BEGIN(waitinf) - now; + #endif } else { - if (!list_empty(waitq)) { - waitinf = list_entry(waitq->next,struct sedf_dom_info,list); - //we could not find any suitable domain => look for domains that are aware of extratime - #if (EXTRA > EXTRA_OFF) - if (!list_empty(extraq) && (PERIOD_BEGIN(waitinf) - now >= EXTRA_QUANTUM)) { - runinf = list_entry(extraq->next,struct sedf_dom_info,extralist); - runinf->extra = EXTRA_RUNNING; - ret.task = runinf->owner; - ret.time = EXTRA_QUANTUM; - } - else - #endif - { - //we have an empty run- and extraqueue or too less time => idle task! - ret.task = IDLETASK(cpu); - ret.time = PERIOD_BEGIN(waitinf) - now; - } - } - else { - //this could probably never happen, but one never knows... - //it can... imagine a second CPU, which is pure scifi ATM, but one never knows ;) - ret.task = IDLETASK(cpu); - ret.time = SECONDS(1); - } + //this could probably never happen, but one never knows... + //it can... imagine a second CPU, which is pure scifi ATM, but one never knows ;) + ret.task = IDLETASK(cpu); + ret.time = SECONDS(1); } + +sched_done: + //TODO: Do something USEFUL when this happens and find out, why it still can happen!!! if (ret.time<0) printk("Ouch! We are seriously BEHIND schedule! %lli\n",ret.time); DOM_INFO(ret.task)->sched_start=now; @@ -509,8 +649,12 @@ static void sedf_sleep(struct domain *d) { if ( __task_on_queue(d) ) __del_from_queue(d); #if (EXTRA > EXTRA_OFF) - if (extraq_on(d)) - extraq_del(d); + if (extraq_on(d, EXTRA_UTIL_Q)) + extraq_del(d, EXTRA_UTIL_Q); + #endif + #if (EXTRA == EXTRA_BLOCK_WEIGHT) + if (extraq_on(d, EXTRA_PEN_Q)) + extraq_del(d, EXTRA_PEN_Q); #endif } } @@ -589,7 +733,31 @@ static inline void unblock_short_cons(struct sedf_dom_info* inf, s_time_t now) { else inf->short_cont++; //we let the domain run in the current period } - +static inline void unblock_short_extra_support(struct sedf_dom_info* inf, s_time_t now) { + /*this unblocking scheme tries to support the domain, by assigning it a priority in extratime distribution + according to the loss of time in this slice due to blocking*/ + s_time_t pen; + inf->absdead += inf->period; //no more realtime execution in this period! + if (likely(inf->absblock)) { + //inf->cputime += now - inf->absblock; //treat blocked time as consumed by the domain + pen = (inf->slice - inf->cputime); + if (pen < 0) pen = 0; + //inf->short_block_lost_tot += pen; //calc. penalty because of that + inf->short_block_lost_tot = pen; //set penalty to the current value + //not sure which one is better.. but seems to work well... + + if (inf->short_block_lost_tot) { + inf->score[0] = (inf->period << 10) / inf->short_block_lost_tot; + inf->pen_extra_blocks++; + if (extraq_on(inf->owner, EXTRA_PEN_Q)) + extraq_del(inf->owner, EXTRA_PEN_Q); //remove domain for possible resorting! + else //remember that we want to be on the penalty queue, + inf->extra |= EXTRA_WANT_PEN_Q; //so that we can continue when we (un-)blocked in pen-ext. + extraq_add_sort_update(inf->owner, EXTRA_PEN_Q, 0); //(re-)add domain to the penalty extraq + } + } + inf->cputime = 0; //give it a fresh slice in the next period! +} static inline void unblock_long_vcons(struct sedf_dom_info* inf, s_time_t now) { inf->absdead += ((now - inf->absdead) / inf->period + 1) * inf->period; //very conservative inf->cputime = 0; @@ -655,6 +823,55 @@ static inline void unblock_long_burst(struct sedf_dom_info* inf,s_time_t now) { inf->absunblock = now; } +#define DOMAIN_EDF 1 +#define DOMAIN_EXTRA_PEN 2 +#define DOMAIN_EXTRA_UTIL 3 +#define DOMAIN_IDLE 4 +static inline int get_run_type(struct domain* d) { + struct sedf_dom_info* inf = DOM_INFO(d); + if (is_idle_task(d)) + return DOMAIN_IDLE; + if (inf->extra & EXTRA_RUN_PEN) + return DOMAIN_EXTRA_PEN; + if (inf->extra & EXTRA_RUN_UTIL) + return DOMAIN_EXTRA_UTIL; + return DOMAIN_EDF; +} +/*Compares two domains in the relation of whether the one is allowed to interrupt the others execution. + It returns true (!=0) if a switch to the other domain is good. + Current Priority scheme is as follows: + EDF > L0 (penalty based) extra-time > L1 (utilization) extra-time > blocked domain > idle-domain + In the same class priorities are assigned as following: + EDF: early deadline > late deadline + L0 extra-time: lower score > higher score*/ +static inline int should_switch(struct domain* cur, struct domain* other, s_time_t now) { + struct sedf_dom_info *cur_inf, *other_inf; + cur_inf = DOM_INFO(cur); + other_inf = DOM_INFO(other); + + switch (get_run_type(cur)) { + case DOMAIN_EDF: + //check whether we need to make an earlier sched-decision + if ((PERIOD_BEGIN(other_inf) < schedule_data[other->processor].s_timer.expires) + && (other_inf->absdead < cur_inf->absdead)) + return 1; + else return 0; + case DOMAIN_EXTRA_PEN: + //check whether we need an earlier decision or we also want the L0 ex-q with lower score + if ((PERIOD_BEGIN(other_inf) < schedule_data[other->processor].s_timer.expires) + || ((other_inf->extra & EXTRA_WANT_PEN_Q) && (other_inf->score[EXTRA_PEN_Q] < cur_inf->score[EXTRA_PEN_Q]))) + return 1; + else return 0; + case DOMAIN_EXTRA_UTIL: + //check whether we need an earlier decision or we want the L0 extraq, don't switch if both domains want L1 extraq + if ((PERIOD_BEGIN(other_inf) < schedule_data[other->processor].s_timer.expires) + || (other_inf->extra & EXTRA_WANT_PEN_Q)) + return 1; + else return 0; + case DOMAIN_IDLE: + return 1; + } +} void sedf_wake(struct domain *d) { //for the first try just implement the "very conservative" way of waking domains up s_time_t now = NOW(); @@ -669,7 +886,7 @@ void sedf_wake(struct domain *d) { PRINT(3,"\tdomain %i is already in some queue\n",d->id); return; } - if ( unlikely(extraq_on(d)) ) { + if ( unlikely(extraq_on(d,EXTRA_UTIL_Q) || extraq_on(d,EXTRA_PEN_Q)) ) { PRINT(3,"\tdomain %i is already in the extraQ\n",d->id); } if (unlikely(inf->absdead == 0)) @@ -679,12 +896,18 @@ void sedf_wake(struct domain *d) { inf->block_tot++; if (unlikely(now< PERIOD_BEGIN(inf))) { + PRINT(4,"extratime unblock\n"); //this might happen, imagine unblocking in extra-time! - if (likely(inf->extra == EXTRA_AWARE)) + #if (EXTRA == EXTRA_BLOCK_WEIGHT) + if (inf->extra & EXTRA_WANT_PEN_Q) { //we have a domain that wants compensation + extraq_add_sort_update(d, EXTRA_PEN_Q, 0); + } /*else*/ + #endif + if (inf->extra & EXTRA_AWARE) #if (EXTRA == EXTRA_ROUNDR) - extraq_add_tail(d); //SD: Could extraq_add_head be better? - #elif (EXTRA == EXTRA_SLICE_WEIGHT) - extraq_add_sort_update(d, 0); + extraq_add_tail(d,EXTRA_UTIL_Q); //SD: Could extraq_add_head be better? + #elif (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) + extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); //put in on the weighted extraq #else ; #endif @@ -693,6 +916,7 @@ void sedf_wake(struct domain *d) { } else { if (now < inf->absdead) { + PRINT(4,"short unblocking\n"); //short blocking inf->short_block_tot++; #if (UNBLOCK <= UNBLOCK_ATROPOS) @@ -701,24 +925,27 @@ void sedf_wake(struct domain *d) { unblock_short_cons(inf, now); #elif (UNBLOCK == UNBLOCK_BURST) unblock_short_burst(inf, now); + #elif (UNBLOCK == UNBLOCK_EXTRA_SUPPORT) + unblock_short_extra_support(inf, now); #endif - - if (inf->extra == EXTRA_AWARE) + + if (inf->extra & EXTRA_AWARE) #if (EXTRA == EXTRA_OFF) ; #elif (EXTRA == EXTRA_ROUNDR) - extraq_add_head(d); - #elif (EXTRA == EXTRA_SLICE_WEIGHT) - extraq_add_sort_update(d, 0); + extraq_add_head(d); //Favour domains which got short unblocked + #elif (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) + extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); #endif } else { + PRINT(4,"long unblocking\n"); //long blocking inf->long_block_tot++; //PRINT(3,"old=%llu ",inf->absdead); #if (UNBLOCK == UNBLOCK_ISOCHRONOUS_EDF) unblock_long_vcons(inf, now); - #elif (UNBLOCK == UNBLOCK_EDF) + #elif (UNBLOCK == UNBLOCK_EDF || UNBLOCK == UNBLOCK_EXTRA_SUPPORT) unblock_long_cons_b(inf, now); #elif (UNBLOCK == UNBLOCK_ATROPOS) unblock_long_cons_c(inf, now); @@ -729,17 +956,21 @@ void sedf_wake(struct domain *d) { unblock_long_burst(inf, now); #endif - if (inf->extra == EXTRA_AWARE) + if (inf->extra & EXTRA_AWARE) { #if (EXTRA == EXTRA_OFF) ; #elif (EXTRA == EXTRA_ROUNDR) extraq_add_head(d); - #elif (EXTRA == EXTRA_SLICE_WEIGHT) - extraq_add_sort_update(d,0); + #elif (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) + //PRINT(2,"now try to add domain %i to the extra_util_q\n",d->id); + extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); + //PRINT(2,"added domain\n"); #endif + } + } } - PRINT(3,"waking up domain %i (deadl= %llu period= %llu now= %llu)\n",d->id,inf->absdead,inf->period,now); + PRINT(3,"woke up domain %i (deadl= %llu period= %llu now= %llu)\n",d->id,inf->absdead,inf->period,now); __add_to_waitqueue_sort(d); PRINT(3,"added to waitq\n"); @@ -747,19 +978,15 @@ void sedf_wake(struct domain *d) { if (inf->absblock != 0) { inf->block_time_tot += now - inf->absblock; inf->penalty_time_tot += PERIOD_BEGIN(inf) + inf->cputime - inf->absblock; - /*if (DOM_INFO(d)->block_time_tot) - PRINT(3,"penalty: %lu\n",(DOM_INFO(d)->penalty_time_tot*100)/DOM_INFO(d)->block_time_tot);*/ } - /*if ( is_idle_task(schedule_data[d->processor].curr)) { - cpu_raise_softirq(d->processor, SCHEDULE_SOFTIRQ); - return; - }*/ - + //sanity check: make sure each extra-aware domain IS on the util-q! + if (inf->extra & EXTRA_AWARE) { + if (!extraq_on(d, EXTRA_UTIL_Q)) + printf("sedf_wake: domain %i is extra-aware, but NOT on L1 extraq!\n",d->id); + } //check whether the awakened task needs to get scheduled before the next sched. decision //and check, whether we are idling and this domain is extratime aware - if ((PERIOD_BEGIN(inf) < schedule_data[d->processor].s_timer.expires) || - (is_idle_task(schedule_data[d->processor].curr) && (now + EXTRA_QUANTUM < schedule_data[d->processor].s_timer.expires) && - (inf->extra == EXTRA_AWARE))) { + if (should_switch(schedule_data[d->processor].curr, d, now)){ #ifdef ADV_SCHED_HISTO adv_sched_hist_start(d->processor); #endif @@ -772,17 +999,18 @@ void sedf_wake(struct domain *d) { static void sedf_dump_domain(struct domain *d) { printk("%u has=%c ", d->id, test_bit(DF_RUNNING, &d->flags) ? 'T':'F'); - printk("p=%llu sl=%llu ddl=%llu w=%u c=%llu sc=%i xtr(%s)=%llu", + printk("p=%llu sl=%llu ddl=%llu w=%hu c=%llu sc=%i xtr(%s)=%llu", DOM_INFO(d)->period, DOM_INFO(d)->slice, DOM_INFO(d)->absdead, DOM_INFO(d)->weight, d->cpu_time, - DOM_INFO(d)->score, DOM_INFO(d)->extra ? "yes" : "no", DOM_INFO(d)->extra_time_tot); + DOM_INFO(d)->score[EXTRA_UTIL_Q], (DOM_INFO(d)->extra & EXTRA_AWARE) ? "yes" : "no", DOM_INFO(d)->extra_time_tot); if (d->cpu_time !=0) printf(" (%lu%)", (DOM_INFO(d)->extra_time_tot * 100) / d->cpu_time); if (DOM_INFO(d)->block_time_tot!=0) printf(" pen=%lu%", (DOM_INFO(d)->penalty_time_tot * 100) / DOM_INFO(d)->block_time_tot); if (DOM_INFO(d)->block_tot!=0) - printf("\n blks=%lu sh=%lu (%lu%) (shc=%lu (%lu%)) l=%lu (%lu%) avg: b=%llu p=%llu", DOM_INFO(d)->block_tot, + printf("\n blks=%lu sh=%lu (%lu%) (shc=%lu (%lu%) shex=%i shexsl=%i) l=%lu (%lu%) avg: b=%llu p=%llu", DOM_INFO(d)->block_tot, DOM_INFO(d)->short_block_tot, (DOM_INFO(d)->short_block_tot * 100) / DOM_INFO(d)->block_tot, DOM_INFO(d)->short_cont, (DOM_INFO(d)->short_cont * 100) / DOM_INFO(d)->block_tot, + DOM_INFO(d)->pen_extra_blocks, DOM_INFO(d)->pen_extra_slices, DOM_INFO(d)->long_block_tot, (DOM_INFO(d)->long_block_tot * 100) / DOM_INFO(d)->block_tot, (DOM_INFO(d)->block_time_tot) / DOM_INFO(d)->block_tot, (DOM_INFO(d)->penalty_time_tot) / DOM_INFO(d)->block_tot); @@ -793,8 +1021,6 @@ static void sedf_dump_cpu_state(int i) { struct list_head *list, *queue, *tmp; int loop = 0; -/* int found = 0; - int id1st = 0;*/ struct sedf_dom_info *d_inf; struct domain* d; @@ -802,8 +1028,7 @@ static void sedf_dump_cpu_state(int i) queue = RUNQ(i); printk("RUNQ rq %lx n: %lx, p: %lx\n", (unsigned long)queue, (unsigned long) queue->next, (unsigned long) queue->prev); - list_for_each_safe ( list, tmp, queue ) - { + list_for_each_safe ( list, tmp, queue ) { printk("%3d: ",loop++); d_inf = list_entry(list, struct sedf_dom_info, list); sedf_dump_domain(d_inf->owner); @@ -812,25 +1037,26 @@ static void sedf_dump_cpu_state(int i) queue = WAITQ(i); loop = 0; printk("\nWAITQ rq %lx n: %lx, p: %lx\n", (unsigned long)queue, (unsigned long) queue->next, (unsigned long) queue->prev); - list_for_each_safe ( list, tmp, queue ) - { + list_for_each_safe ( list, tmp, queue ) { printk("%3d: ",loop++); d_inf = list_entry(list, struct sedf_dom_info, list); sedf_dump_domain(d_inf->owner); } - queue = EXTRAQ(i); loop = 0; - printk("\nEXTRAQ rq %lx n: %lx, p: %lx\n", (unsigned long)queue, + queue = EXTRAQ(i,EXTRA_PEN_Q); loop = 0; + printk("\nEXTRAQ (penalty) rq %lx n: %lx, p: %lx\n", (unsigned long)queue, (unsigned long) queue->next, (unsigned long) queue->prev); - list_for_each_safe ( list, tmp, queue ) - { - d_inf = list_entry(list, struct sedf_dom_info, extralist); - -/* if (found) { - if (id1st == d_inf->owner->id) - break; - } - else { id1st=d_inf->owner->id; found=1;}*/ + list_for_each_safe ( list, tmp, queue ) { + d_inf = list_entry(list, struct sedf_dom_info, extralist[EXTRA_PEN_Q]); + printk("%3d: ",loop++); + sedf_dump_domain(d_inf->owner); + } + + queue = EXTRAQ(i,EXTRA_UTIL_Q); loop = 0; + printk("\nEXTRAQ (utilization) rq %lx n: %lx, p: %lx\n", (unsigned long)queue, + (unsigned long) queue->next, (unsigned long) queue->prev); + list_for_each_safe ( list, tmp, queue ) { + d_inf = list_entry(list, struct sedf_dom_info, extralist[EXTRA_UTIL_Q]); printk("%3d: ",loop++); sedf_dump_domain(d_inf->owner); } @@ -838,7 +1064,7 @@ static void sedf_dump_cpu_state(int i) loop = 0; printk("\nnot on Q\n"); for_each_domain(d) { - if (!extraq_on(d) && !__task_on_queue(d)) { + if (!extraq_on(d,1) && !__task_on_queue(d)) { printk("%3d: ",loop++); sedf_dump_domain(d); } @@ -896,7 +1122,7 @@ static int sedf_adjdom(struct domain *p, struct sched_adjdom_cmd *cmd) { } if (sedf_adjust_weights(p,cmd)) return -EINVAL; - DOM_INFO(p)->extra = cmd->u.sedf.extratime; + DOM_INFO(p)->extra = (DOM_INFO(p)-> extra & ~EXTRA_AWARE) | (cmd->u.sedf.extratime & EXTRA_AWARE); DOM_INFO(p)->latency = cmd->u.sedf.latency; extraq_check(p); } @@ -904,7 +1130,7 @@ static int sedf_adjdom(struct domain *p, struct sched_adjdom_cmd *cmd) { { cmd->u.sedf.period = DOM_INFO(p)->period; cmd->u.sedf.slice = DOM_INFO(p)->slice; - cmd->u.sedf.extratime = DOM_INFO(p)->extra; + cmd->u.sedf.extratime = DOM_INFO(p)->extra & EXTRA_AWARE; cmd->u.sedf.latency = DOM_INFO(p)->latency; cmd->u.sedf.weight = DOM_INFO(p)->weight; } diff --git a/xen/include/xen/adv_sched_hist.h b/xen/include/xen/adv_sched_hist.h new file mode 100644 index 0000000000..d8d56275d4 --- /dev/null +++ b/xen/include/xen/adv_sched_hist.h @@ -0,0 +1,40 @@ +/* Some functions to suport advanced scheduler histograms + Author: Stephan.Diestelhorst@cl.cam.ac.uk */ +//#include +//#include +#include +#define ADV_SCHED_HISTO +static inline void adv_sched_hist_start(int cpu) { + u64 now; + rdtscll(now); + if (!schedule_data[cpu].save_tsc) + schedule_data[cpu].save_tsc = now; +} +static inline void adv_sched_hist_from_stop(int cpu) { + u64 now; + rdtscll(now); + if (schedule_data[cpu].save_tsc) { + now -= schedule_data[cpu].save_tsc; + now /= 7; + if (now < BUCKETS-1) + schedule_data[cpu].from_hist[now]++; + else + schedule_data[cpu].from_hist[BUCKETS-1]++; + + schedule_data[cpu].save_tsc = 0; + } +} +static inline void adv_sched_hist_to_stop(int cpu) { + u64 now; + rdtscll(now); + if (schedule_data[cpu].save_tsc) { + now -= schedule_data[cpu].save_tsc; + now /= 24; + if (now < BUCKETS-1) + schedule_data[cpu].to_hist[now]++; + else + schedule_data[cpu].to_hist[BUCKETS-1]++; + + schedule_data[cpu].save_tsc = 0; + } +} From 4be324fcbaafe9e775ca138bc17ef3fdb04c226f Mon Sep 17 00:00:00 2001 From: "sd386@font.cl.cam.ac.uk" Date: Wed, 9 Mar 2005 16:10:08 +0000 Subject: [PATCH 6/9] bitkeeper revision 1.1159.170.106 (422f1fe04lkSpzGfJ9fkoHS3KOxslw) Fixed weights on SMP machines. --- xen/common/sched_sedf.c | 70 ++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/xen/common/sched_sedf.c b/xen/common/sched_sedf.c index 1509432572..d9816c3ac6 100644 --- a/xen/common/sched_sedf.c +++ b/xen/common/sched_sedf.c @@ -5,14 +5,6 @@ * based on code by Mark Williamson (C) 2004 Intel Research Cambridge */ -/* - TODO: - TESTING! - tracing instead of PRINTs -*/ - - - #include #include #include @@ -75,10 +67,9 @@ struct sedf_dom_info s_time_t period_orig; s_time_t slice_orig; s_time_t latency; - //extra-time status of domain - short extra; - //weights for "Scheduling for beginners/ lazy/ etc." ;) - short weight; + + short extra; //extra-time status of domain + short weight; //weights for "Scheduling for beginners/ lazy/ etc." ;) //Bookkeeping s_time_t absdead; @@ -197,9 +188,14 @@ static inline void extraq_check(struct domain *d) { } else { PRINT(2,"Dom %i is NOT on L1 extraQ\n",d->id); if ((DOM_INFO(d)->extra & EXTRA_AWARE) && domain_runnable(d)) { + #if (EXTRA == EXTRA_ROUNDR) + extraq_add_tail(d, EXTRA_UTIL_Q); //Favour domains which got short unblocked + #elif (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); + #elif + ; + #endif PRINT(2,"Added dom %i to L1 extraQ\n",d->id); - //TODO: add extraq_add_tail } } } @@ -350,9 +346,8 @@ static inline void desched_edf_dom (s_time_t now, struct domain* d) { //current domain is running in real time mode inf->cputime += now - inf->sched_start; //update the domains cputime - //scheduling decisions, which don't remove the running domain from the runq - if ((inf->cputime < inf->slice) && domain_runnable(d)) - return; //there is nothing to do with the running task + if ((inf->cputime < inf->slice) && domain_runnable(d)) //scheduling decisions, which don't remove the running domain from the runq + return; //there is nothing to do with the running task __del_from_queue(d); /*if (__task_on_queue(current)) { @@ -388,8 +383,8 @@ static inline void desched_edf_dom (s_time_t now, struct domain* d) { #if (EXTRA > EXTRA_OFF) #if (EXTRA == EXTRA_BLOCK_WEIGHT) if (extraq_on(d,EXTRA_PEN_Q)) extraq_del(d,EXTRA_PEN_Q); - if (extraq_on(d,EXTRA_UTIL_Q)) extraq_del(d,EXTRA_UTIL_Q); #endif + if (extraq_on(d,EXTRA_UTIL_Q)) extraq_del(d,EXTRA_UTIL_Q); #endif } } @@ -496,12 +491,14 @@ static inline void desched_extra_dom(s_time_t now, struct domain* d) { printf("Oops... We attempt to remove d %i from the waitq, but it is not on :(\n",d->id);*/ __del_from_queue(d); //also remove this blocked domain from the waitq! //make sure that we remove a blocked domain from the other extraq aswell (this caused hours of debugging!) + #if (EXTRA == EXTRA_BLOCK_WEIGHT) if (i == EXTRA_PEN_Q) { if (extraq_on(d,EXTRA_UTIL_Q)) extraq_del(d,EXTRA_UTIL_Q); } else { if (extraq_on(d,EXTRA_PEN_Q)) extraq_del(d,EXTRA_PEN_Q); } + #endif } #endif /*if (!domain_runnable(d)) { @@ -521,7 +518,7 @@ static inline void desched_extra_dom(s_time_t now, struct domain* d) { static inline task_slice_t sedf_do_extra_schedule(s_time_t now, s_time_t end_xt, struct list_head *extraq[], int cpu) { task_slice_t ret; struct sedf_dom_info *runinf; - //TODO write this stuff as a loop and pay attention to normal stuff! + //TODO write this stuff as a loop if (end_xt - now < EXTRA_QUANTUM) goto return_idle; @@ -555,7 +552,7 @@ return_idle: * Reasons for calling this function are: * -timeslice for the current period used up * -domain on waitqueue has started it's period - * -and various others ;) in general: determin which domain to run next*/ + * -and various others ;) in general: determine which domain to run next*/ static task_slice_t sedf_do_schedule(s_time_t now) { int cpu = current->processor; @@ -631,8 +628,10 @@ check_waitq: sched_done: //TODO: Do something USEFUL when this happens and find out, why it still can happen!!! - if (ret.time<0) + if (ret.time<0) { printk("Ouch! We are seriously BEHIND schedule! %lli\n",ret.time); + ret.time = EXTRA_QUANTUM; + } DOM_INFO(ret.task)->sched_start=now; return ret; } @@ -840,7 +839,7 @@ static inline int get_run_type(struct domain* d) { /*Compares two domains in the relation of whether the one is allowed to interrupt the others execution. It returns true (!=0) if a switch to the other domain is good. Current Priority scheme is as follows: - EDF > L0 (penalty based) extra-time > L1 (utilization) extra-time > blocked domain > idle-domain + EDF > L0 (penalty based) extra-time > L1 (utilization) extra-time > idle-domain In the same class priorities are assigned as following: EDF: early deadline > late deadline L0 extra-time: lower score > higher score*/ @@ -933,7 +932,7 @@ void sedf_wake(struct domain *d) { #if (EXTRA == EXTRA_OFF) ; #elif (EXTRA == EXTRA_ROUNDR) - extraq_add_head(d); //Favour domains which got short unblocked + extraq_add_head(d, EXTRA_UTIL_Q); //Favour domains which got short unblocked #elif (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); #endif @@ -960,7 +959,7 @@ void sedf_wake(struct domain *d) { #if (EXTRA == EXTRA_OFF) ; #elif (EXTRA == EXTRA_ROUNDR) - extraq_add_head(d); + extraq_add_head(d, EXTRA_UTIL_Q); #elif (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) //PRINT(2,"now try to add domain %i to the extra_util_q\n",d->id); extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); @@ -980,10 +979,10 @@ void sedf_wake(struct domain *d) { inf->penalty_time_tot += PERIOD_BEGIN(inf) + inf->cputime - inf->absblock; } //sanity check: make sure each extra-aware domain IS on the util-q! - if (inf->extra & EXTRA_AWARE) { + /*if (inf->extra & EXTRA_AWARE) { if (!extraq_on(d, EXTRA_UTIL_Q)) printf("sedf_wake: domain %i is extra-aware, but NOT on L1 extraq!\n",d->id); - } + }*/ //check whether the awakened task needs to get scheduled before the next sched. decision //and check, whether we are idling and this domain is extratime aware if (should_switch(schedule_data[d->processor].curr, d, now)){ @@ -994,8 +993,7 @@ void sedf_wake(struct domain *d) { } } - -/* This could probably be a bit more specific!*/ +/*Print a lot of use-{full, less} information about a domains in the system*/ static void sedf_dump_domain(struct domain *d) { printk("%u has=%c ", d->id, test_bit(DF_RUNNING, &d->flags) ? 'T':'F'); @@ -1017,6 +1015,7 @@ static void sedf_dump_domain(struct domain *d) { printf("\n"); } +/*dumps all domains on hte specified cpu*/ static void sedf_dump_cpu_state(int i) { struct list_head *list, *queue, *tmp; @@ -1064,7 +1063,7 @@ static void sedf_dump_cpu_state(int i) loop = 0; printk("\nnot on Q\n"); for_each_domain(d) { - if (!extraq_on(d,1) && !__task_on_queue(d)) { + if (!__task_on_queue(d) && (d->processor == i)) { printk("%3d: ",loop++); sedf_dump_domain(d); } @@ -1072,19 +1071,24 @@ static void sedf_dump_cpu_state(int i) } //Adjusts periods and slices of the domains accordingly to their weights static inline int sedf_adjust_weights(struct domain *p, struct sched_adjdom_cmd *cmd) { - int sumw = 0; - s_time_t sumt = 0; + int sumw[NR_CPUS]; + s_time_t sumt[NR_CPUS]; + int cpu; + for (cpu=0; cpu < NR_CPUS; cpu++) { + sumw[cpu] = 0; + sumt[cpu] = 0; + } //sum up all weights for_each_domain(p) { if (DOM_INFO(p)->weight) - sumw += DOM_INFO(p)->weight; + sumw[p->processor] += DOM_INFO(p)->weight; else { //don't modify domains who don't have a weight, but sum up //the time they need, projected to a WEIGHT_PERIOD, so that //this time is not given to the weight-driven domains ASSERT((WEIGHT_PERIOD < ULONG_MAX) && (DOM_INFO(p)->slice_orig < ULONG_MAX)); //this results in max. 4s slice/period length - sumt += (WEIGHT_PERIOD * DOM_INFO(p)->slice_orig) / DOM_INFO(p)->period_orig; + sumt[p->processor] += (WEIGHT_PERIOD * DOM_INFO(p)->slice_orig) / DOM_INFO(p)->period_orig; } } //adjust all slices (and periods) to the new weight @@ -1093,7 +1097,7 @@ static inline int sedf_adjust_weights(struct domain *p, struct sched_adjdom_cmd DOM_INFO(p)->period_orig = DOM_INFO(p)->period = WEIGHT_PERIOD; DOM_INFO(p)->slice_orig = - DOM_INFO(p)->slice = (DOM_INFO(p)->weight * (WEIGHT_PERIOD - WEIGHT_SAFETY - sumt)) / sumw; + DOM_INFO(p)->slice = (DOM_INFO(p)->weight * (WEIGHT_PERIOD - WEIGHT_SAFETY - sumt[p->processor])) / sumw[p->processor]; } } return 0; From 94e7002d4975dadf0457f786a7c719e9877d522d Mon Sep 17 00:00:00 2001 From: "sd386@font.cl.cam.ac.uk" Date: Tue, 29 Mar 2005 15:57:43 +0000 Subject: [PATCH 7/9] bitkeeper revision 1.1159.170.107 (42497af7aXKR1tJnN7XlLoXsYCxABQ) cleaned up some statistics code, new domains are now best effort by default, fixed possible bug for best effort domains --- xen/common/sched_sedf.c | 47 +++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/xen/common/sched_sedf.c b/xen/common/sched_sedf.c index d9816c3ac6..af8b9ba77b 100644 --- a/xen/common/sched_sedf.c +++ b/xen/common/sched_sedf.c @@ -15,10 +15,15 @@ //#include -#define SEDFLEVEL 2 +//verbosity settings +#define SEDFLEVEL 0 #define PRINT(_f, _a...) \ if ((_f)<=SEDFLEVEL) printk(_a ); +#ifdef DEBUG + #define SEDF_STATS +#endif + //various ways of unblocking domains #define UNBLOCK_ISOCHRONOUS_EDF 1 #define UNBLOCK_EDF 2 @@ -81,15 +86,18 @@ struct sedf_dom_info s_time_t short_block_lost_tot; //Statistics + s_time_t extra_time_tot; + +#ifdef SEDF_STATS s_time_t block_time_tot; s_time_t penalty_time_tot; - s_time_t extra_time_tot; int block_tot; int short_block_tot; int long_block_tot; int short_cont; int pen_extra_blocks; int pen_extra_slices; +#endif }; struct sedf_cpu_info { @@ -181,7 +189,7 @@ static inline void extraq_add_sort_update(struct domain *d, int i, int sub) { static inline void extraq_check(struct domain *d) { if (extraq_on(d, EXTRA_UTIL_Q)) { PRINT(2,"Dom %i is on extraQ\n",d->id); - if (!(DOM_INFO(d)->extra & EXTRA_AWARE)) { + if (!(DOM_INFO(d)->extra & EXTRA_AWARE) && !extra_runs(DOM_INFO(d))) { extraq_del(d, EXTRA_UTIL_Q); PRINT(2,"Removed dom %i from L1 extraQ\n",d->id); } @@ -306,12 +314,12 @@ static void sedf_add_task(struct domain *d) inf->extra = EXTRA_NONE;//EXTRA_AWARE; } else { - //other domains don't get any execution time at all in the beginning! + //other domains run in best effort mode inf->period = MILLISECS(20); inf->slice = 0; inf->absdead = 0; inf->latency = 0; - inf->extra = EXTRA_NONE;//EXTRA_AWARE + inf->extra = EXTRA_AWARE; } inf->period_orig = inf->period; inf->slice_orig = inf->slice; INIT_LIST_HEAD(&(inf->list)); @@ -481,7 +489,10 @@ static inline void desched_extra_dom(s_time_t now, struct domain* d) { #endif { //domain was running in L1 extraq => score is inverse of utilization and is used somewhat incremental! - inf->score[EXTRA_UTIL_Q] = (inf->period << 10) / inf->slice;//use fixed point arithmetic with 10 bits + if (inf->slice) + inf->score[EXTRA_UTIL_Q] = (inf->period << 10) / inf->slice;//use fixed point arithmetic with 10 bits + else + inf->score[EXTRA_UTIL_Q] = 2^10; } if (domain_runnable(d)) extraq_add_sort_update(d, i, oldscore); //add according to score: weighted round robin @@ -529,7 +540,9 @@ static inline task_slice_t sedf_do_extra_schedule(s_time_t now, s_time_t end_xt, runinf->extra |= EXTRA_RUN_PEN; ret.task = runinf->owner; ret.time = EXTRA_QUANTUM; +#ifdef SEDF_STATS runinf->pen_extra_slices++; +#endif } else if (!list_empty(extraq[EXTRA_UTIL_Q])) { //use elements from the normal extraqueue @@ -729,8 +742,10 @@ static inline void unblock_short_cons(struct sedf_dom_info* inf, s_time_t now) { //we don't have a reasonable amount of time in our slice left :( unblock_short_vcons(inf, now); //start in next period! } +#ifdef SEDF_STATS else inf->short_cont++; //we let the domain run in the current period +#endif } static inline void unblock_short_extra_support(struct sedf_dom_info* inf, s_time_t now) { /*this unblocking scheme tries to support the domain, by assigning it a priority in extratime distribution @@ -747,7 +762,9 @@ static inline void unblock_short_extra_support(struct sedf_dom_info* inf, s_time if (inf->short_block_lost_tot) { inf->score[0] = (inf->period << 10) / inf->short_block_lost_tot; +#ifdef SEDF_STATS inf->pen_extra_blocks++; +#endif if (extraq_on(inf->owner, EXTRA_PEN_Q)) extraq_del(inf->owner, EXTRA_PEN_Q); //remove domain for possible resorting! else //remember that we want to be on the penalty queue, @@ -791,7 +808,9 @@ static inline void unblock_short_burst(struct sedf_dom_info* inf, s_time_t now) inf->cputime += now - inf->absblock; //treat blocked time as consumed by the domain if (inf->cputime + EXTRA_QUANTUM <= inf->slice) { +#ifdef SEDF_STATS inf->short_cont++; //we let the domain run in the current period +#endif } else { //we don't have a reasonable amount of time in our slice left => switch to burst mode @@ -851,8 +870,7 @@ static inline int should_switch(struct domain* cur, struct domain* other, s_time switch (get_run_type(cur)) { case DOMAIN_EDF: //check whether we need to make an earlier sched-decision - if ((PERIOD_BEGIN(other_inf) < schedule_data[other->processor].s_timer.expires) - && (other_inf->absdead < cur_inf->absdead)) + if ((PERIOD_BEGIN(other_inf) < schedule_data[other->processor].s_timer.expires)) return 1; else return 0; case DOMAIN_EXTRA_PEN: @@ -892,8 +910,9 @@ void sedf_wake(struct domain *d) { inf->absdead = now + inf->slice; //initial setup of the deadline PRINT(3,"waking up domain %i (deadl= %llu period= %llu now= %llu)\n",d->id,inf->absdead,inf->period,now); - +#ifdef SEDF_STATS inf->block_tot++; +#endif if (unlikely(now< PERIOD_BEGIN(inf))) { PRINT(4,"extratime unblock\n"); //this might happen, imagine unblocking in extra-time! @@ -917,7 +936,9 @@ void sedf_wake(struct domain *d) { if (now < inf->absdead) { PRINT(4,"short unblocking\n"); //short blocking +#ifdef SEDF_STATS inf->short_block_tot++; +#endif #if (UNBLOCK <= UNBLOCK_ATROPOS) unblock_short_vcons(inf, now); #elif (UNBLOCK == UNBLOCK_SHORT_RESUME) @@ -940,7 +961,9 @@ void sedf_wake(struct domain *d) { else { PRINT(4,"long unblocking\n"); //long blocking +#ifdef SEDF_STATS inf->long_block_tot++; +#endif //PRINT(3,"old=%llu ",inf->absdead); #if (UNBLOCK == UNBLOCK_ISOCHRONOUS_EDF) unblock_long_vcons(inf, now); @@ -973,18 +996,22 @@ void sedf_wake(struct domain *d) { __add_to_waitqueue_sort(d); PRINT(3,"added to waitq\n"); +#ifdef SEDF_STATS //do some statistics here... if (inf->absblock != 0) { inf->block_time_tot += now - inf->absblock; inf->penalty_time_tot += PERIOD_BEGIN(inf) + inf->cputime - inf->absblock; } +#endif //sanity check: make sure each extra-aware domain IS on the util-q! /*if (inf->extra & EXTRA_AWARE) { if (!extraq_on(d, EXTRA_UTIL_Q)) printf("sedf_wake: domain %i is extra-aware, but NOT on L1 extraq!\n",d->id); }*/ + //check whether the awakened task needs to get scheduled before the next sched. decision //and check, whether we are idling and this domain is extratime aware + //Save approximation: Always switch to scheduler! if (should_switch(schedule_data[d->processor].curr, d, now)){ #ifdef ADV_SCHED_HISTO adv_sched_hist_start(d->processor); @@ -1002,6 +1029,7 @@ static void sedf_dump_domain(struct domain *d) { DOM_INFO(d)->score[EXTRA_UTIL_Q], (DOM_INFO(d)->extra & EXTRA_AWARE) ? "yes" : "no", DOM_INFO(d)->extra_time_tot); if (d->cpu_time !=0) printf(" (%lu%)", (DOM_INFO(d)->extra_time_tot * 100) / d->cpu_time); +#ifdef SEDF_STATS if (DOM_INFO(d)->block_time_tot!=0) printf(" pen=%lu%", (DOM_INFO(d)->penalty_time_tot * 100) / DOM_INFO(d)->block_time_tot); if (DOM_INFO(d)->block_tot!=0) @@ -1012,6 +1040,7 @@ static void sedf_dump_domain(struct domain *d) { DOM_INFO(d)->long_block_tot, (DOM_INFO(d)->long_block_tot * 100) / DOM_INFO(d)->block_tot, (DOM_INFO(d)->block_time_tot) / DOM_INFO(d)->block_tot, (DOM_INFO(d)->penalty_time_tot) / DOM_INFO(d)->block_tot); +#endif printf("\n"); } From 0e2e29a5d07411344a4e4af1cdf96df1d439da8e Mon Sep 17 00:00:00 2001 From: "sd386@font.cl.cam.ac.uk" Date: Thu, 31 Mar 2005 23:09:27 +0000 Subject: [PATCH 8/9] bitkeeper revision 1.1159.170.108 (424c8327NU4_W8SWKkWXALwnntcq3g) mainly layout changes, but also minor bugfixes along my way --- xen/common/sched_sedf.c | 849 +++++++++++++++++++++++++--------------- 1 file changed, 540 insertions(+), 309 deletions(-) diff --git a/xen/common/sched_sedf.c b/xen/common/sched_sedf.c index af8b9ba77b..ab1a0697e5 100644 --- a/xen/common/sched_sedf.c +++ b/xen/common/sched_sedf.c @@ -13,9 +13,9 @@ #include #include -//#include +/*#include */ -//verbosity settings +/*verbosity settings*/ #define SEDFLEVEL 0 #define PRINT(_f, _a...) \ if ((_f)<=SEDFLEVEL) printk(_a ); @@ -24,7 +24,7 @@ if ((_f)<=SEDFLEVEL) printk(_a ); #define SEDF_STATS #endif -//various ways of unblocking domains +/*various ways of unblocking domains*/ #define UNBLOCK_ISOCHRONOUS_EDF 1 #define UNBLOCK_EDF 2 #define UNBLOCK_ATROPOS 3 @@ -33,7 +33,7 @@ if ((_f)<=SEDFLEVEL) printk(_a ); #define UNBLOCK_EXTRA_SUPPORT 6 #define UNBLOCK UNBLOCK_EXTRA_SUPPORT -//various ways of treating extra-time +/*various ways of treating extra-time*/ #define EXTRA_OFF 1 #define EXTRA_ROUNDR 2 #define EXTRA_SLICE_WEIGHT 3 @@ -63,29 +63,35 @@ struct sedf_dom_info struct list_head list; struct list_head extralist[2]; - //Parameters for EDF - s_time_t period; //=(relative deadline) - s_time_t slice; //=worst case execution time + /*Parameters for EDF*/ + s_time_t period; /*=(relative deadline)*/ + s_time_t slice; /*=worst case execution time*/ - //Advaced Parameters - //Latency Scaling + /*Advaced Parameters*/ + /*Latency Scaling*/ s_time_t period_orig; s_time_t slice_orig; s_time_t latency; - short extra; //extra-time status of domain - short weight; //weights for "Scheduling for beginners/ lazy/ etc." ;) + /*extra-time status of domain*/ + short extra; + /*weights for "Scheduling for beginners/ lazy/ etc." ;)*/ + short weight; - //Bookkeeping + /*Bookkeeping*/ s_time_t absdead; s_time_t sched_start; s_time_t cputime; - s_time_t absblock; - s_time_t absunblock; //time the domain unblocked, used by burst mode to determine unblocking intervals - int score[2]; //scores for {util, block penalty}-weighted extratime distribution + s_time_t absblock; + + /*time the domain unblocked, used to determine unblocking intervals*/ + s_time_t absunblock; + + /*scores for {util, block penalty}-weighted extratime distribution*/ + int score[2]; s_time_t short_block_lost_tot; - //Statistics + /*Statistics*/ s_time_t extra_time_tot; #ifdef SEDF_STATS @@ -107,7 +113,7 @@ struct sedf_cpu_info { }; #define DOM_INFO(d) ((struct sedf_dom_info *)((d)->sched_priv)) -#define CPU_INFO(cpu) ((struct sedf_cpu_info *)schedule_data[cpu].sched_priv) +#define CPU_INFO(cpu) ((struct sedf_cpu_info *)schedule_data[cpu].sched_priv) #define LIST(d) (&DOM_INFO(d)->list) #define EXTRALIST(d,i) (&(DOM_INFO(d)->extralist[i])) #define RUNQ(cpu) (&CPU_INFO(cpu)->runnableq) @@ -125,7 +131,8 @@ static xmem_cache_t *dom_info_cache; static void sedf_dump_cpu_state(int i); static inline int extraq_on(struct domain *d, int i) { - return ((EXTRALIST(d,i)->next != NULL) && (EXTRALIST(d,i)->next != EXTRALIST(d,i))); + return ((EXTRALIST(d,i)->next != NULL) && + (EXTRALIST(d,i)->next != EXTRALIST(d,i))); } static inline void extraq_add_head(struct domain *d, int i) @@ -142,7 +149,8 @@ static inline void extraq_del(struct domain *d, int i) { struct list_head *list = EXTRALIST(d,i); /*if (!extraq_on(d,i)) { - PRINT(0,"extraq_del: domain %i is NOT on L%i extraq! HALTING\n",d->id,i); + PRINT(0,"extraq_del: domain %i is NOT on L%i extraq "\ + "HALTING\n",d->id,i); sedf_dump_cpu_state(0);(*((int*)0))++; }*/ PRINT(3, "Removing domain %i from L%i extraq\n", d->id,i); @@ -150,55 +158,67 @@ static inline void extraq_del(struct domain *d, int i) list->next = NULL; } -/* adds a domain to the queue of processes which are aware of extra time. List is sorted by score, - where a lower score means higher priority for an extra slice. It also updates the score, by simply subtracting - a fixed value from each entry, in order to avoid overflow. The algorithm works by simply charging each domain - that recieved extratime with an inverse of its weight. +/* adds a domain to the queue of processes which are aware of extra time. List + is sorted by score, where a lower score means higher priority for an extra + slice. It also updates the score, by simply subtracting a fixed value from + each entry, in order to avoid overflow. The algorithm works by simply + charging each domain that recieved extratime with an inverse of its weight. */ static inline void extraq_add_sort_update(struct domain *d, int i, int sub) { struct list_head *cur; struct sedf_dom_info *curinf; /*if (extraq_on(d,i)) { - PRINT(0,"extraq_add_sort_update: domain %i is already on L%i extraq! HALTING\n",d->id,i); + PRINT(0,"extraq_add_sort_update: domain %i is already on "\ + "L%i extraq! HALTING\n",d->id,i); sedf_dump_cpu_state(0);(*((int*)0))++; }*/ - PRINT(3, "Adding domain %i (score= %i, short_pen= %lli) to L%i extraq\n", d->id, - DOM_INFO(d)->score[i], DOM_INFO(d)->short_block_lost_tot, i); - //iterate through all elements to find our "hole" and on our way update all the other scores + PRINT(3, "Adding domain %i (score= %i, short_pen= %lli) to L%i "\ + "extraq\n", d->id, DOM_INFO(d)->score[i], + DOM_INFO(d)->short_block_lost_tot, i); + /*iterate through all elements to find our "hole" and on our way + update all the other scores*/ list_for_each(cur,EXTRAQ(d->processor,i)){ - curinf = list_entry(cur,struct sedf_dom_info,extralist[i]); + curinf = list_entry(cur,struct sedf_dom_info,extralist[i]); curinf->score[i] -= sub; if (DOM_INFO(d)->score[i] < curinf->score[i]) break; else - PRINT(4,"\tbehind domain %i (score= %i)\n", curinf->owner->id, curinf->score[i]); + PRINT(4,"\tbehind domain %i (score= %i)\n", + curinf->owner->id, curinf->score[i]); } - //cur now contains the element, before which we'll enq ueue + /*cur now contains the element, before which we'll enqueue*/ PRINT(3, "\tlist_add to %x\n", cur->prev); list_add(EXTRALIST(d,i),cur->prev); - //continue updating the extraq - if ((cur != EXTRAQ(d->processor,i)) && sub) - for (cur = cur->next; cur != EXTRAQ(d->processor,i); cur = cur-> next) { - curinf = list_entry(cur,struct sedf_dom_info,extralist[i]); + /*continue updating the extraq*/ + if ((cur != EXTRAQ(d->processor,i)) && sub) + for (cur = cur->next; cur != EXTRAQ(d->processor,i); + cur = cur-> next) { + curinf = list_entry(cur,struct sedf_dom_info, + extralist[i]); curinf->score[i] -= sub; - PRINT(4, "\tupdating domain %i (score= %llu)\n", curinf->owner->id, curinf->score[i]); + PRINT(4, "\tupdating domain %i (score= %llu)\n", + curinf->owner->id, curinf->score[i]); } } static inline void extraq_check(struct domain *d) { if (extraq_on(d, EXTRA_UTIL_Q)) { PRINT(2,"Dom %i is on extraQ\n",d->id); - if (!(DOM_INFO(d)->extra & EXTRA_AWARE) && !extra_runs(DOM_INFO(d))) { + if (!(DOM_INFO(d)->extra & EXTRA_AWARE) && + !extra_runs(DOM_INFO(d))) { extraq_del(d, EXTRA_UTIL_Q); PRINT(2,"Removed dom %i from L1 extraQ\n",d->id); } } else { PRINT(2,"Dom %i is NOT on L1 extraQ\n",d->id); - if ((DOM_INFO(d)->extra & EXTRA_AWARE) && domain_runnable(d)) { + if ((DOM_INFO(d)->extra & EXTRA_AWARE) && domain_runnable(d)) + { #if (EXTRA == EXTRA_ROUNDR) - extraq_add_tail(d, EXTRA_UTIL_Q); //Favour domains which got short unblocked - #elif (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) + /*Favour domains which got short unblocked*/ + extraq_add_tail(d, EXTRA_UTIL_Q); + #elif (EXTRA == EXTRA_SLICE_WEIGHT || \ + EXTRA == EXTRA_BLOCK_WEIGHT) extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); #elif ; @@ -210,52 +230,61 @@ static inline void extraq_check(struct domain *d) { static inline void __del_from_queue(struct domain *d) { struct list_head *list = LIST(d); - PRINT(3,"Removing domain %i (bop= %llu) from runq/waitq\n",d->id,PERIOD_BEGIN(DOM_INFO(d))); + PRINT(3,"Removing domain %i (bop= %llu) from runq/waitq\n", d->id, + PERIOD_BEGIN(DOM_INFO(d))); list_del(list); list->next = NULL; } -/* adds a domain to the queue of processes which wait for the beginning of the next period - * this list is therefore sortet by this time, which is simply absol. deadline - period +/* adds a domain to the queue of processes which wait for the beginning of the + next period; this list is therefore sortet by this time, which is simply + absol. deadline - period */ static inline void __add_to_waitqueue_sort(struct domain *d) { struct list_head *cur; struct sedf_dom_info *curinf; - PRINT(3,"Adding domain %i (bop= %llu) to waitq\n",d->id,PERIOD_BEGIN(DOM_INFO(d))); - //iterate through all elements to find our "hole" + PRINT(3,"Adding domain %i (bop= %llu) to waitq\n", d->id, + PERIOD_BEGIN(DOM_INFO(d))); + + /*iterate through all elements to find our "hole"*/ list_for_each(cur,WAITQ(d->processor)){ curinf = list_entry(cur,struct sedf_dom_info,list); if (PERIOD_BEGIN(DOM_INFO(d)) < PERIOD_BEGIN(curinf)) break; else - PRINT(4,"\tbehind domain %i (bop= %llu)\n",curinf->owner->id,PERIOD_BEGIN(curinf)); + PRINT(4,"\tbehind domain %i (bop= %llu)\n", + curinf->owner->id, PERIOD_BEGIN(curinf)); } - //cur now contains the element, before which we'll enqueue + /*cur now contains the element, before which we'll enqueue*/ PRINT(3,"\tlist_add to %x\n",cur->prev); list_add(LIST(d),cur->prev); } -/* adds a domain to the queue of processes which have started their current period and are - * runnable (i.e. not blocked, dieing,...). The first element on this list is running on the processor, - * if the list is empty the idle task will run. As we are implementing EDF, this list is sorted by - * deadlines. +/* adds a domain to the queue of processes which have started their current + period and are runnable (i.e. not blocked, dieing,...). The first element + on this list is running on the processor, if the list is empty the idle + task will run. As we are implementing EDF, this list is sorted by deadlines. */ static inline void __add_to_runqueue_sort(struct domain *d) { struct list_head *cur; struct sedf_dom_info *curinf; - PRINT(3,"Adding domain %i (deadl= %llu) to runq\n",d->id,DOM_INFO(d)->absdead); - //iterate through all elements to find our "hole" - list_for_each(cur,RUNQ(d->processor)){ - curinf = list_entry(cur,struct sedf_dom_info,list); + PRINT(3,"Adding domain %i (deadl= %llu) to runq\n", d->id, + DOM_INFO(d)->absdead); + + /*iterate through all elements to find our "hole"*/ + list_for_each(cur, RUNQ(d->processor)) { + curinf = list_entry(cur, struct sedf_dom_info, list); if (DOM_INFO(d)->absdead < curinf->absdead) break; else - PRINT(4,"\tbehind domain %i (deadl= %llu)\n",curinf->owner->id,curinf->absdead); + PRINT(4,"\tbehind domain %i (deadl= %llu)\n", + curinf->owner->id, curinf->absdead); } - //cur now contains the element, before which we'll enqueue + + /*cur now contains the element, before which we'll enqueue*/ PRINT(3,"\tlist_add to %x\n",cur->prev); list_add(LIST(d),cur->prev); @@ -270,7 +299,8 @@ static int sedf_init_scheduler() { PRINT(2,"sedf_init_scheduler was called\n"); for ( i = 0; i < NR_CPUS; i++ ) { - schedule_data[i].sched_priv = xmalloc(sizeof(struct sedf_cpu_info)); + schedule_data[i].sched_priv = + xmalloc(sizeof(struct sedf_cpu_info)); if ( schedule_data[i].sched_priv == NULL ) return -1; INIT_LIST_HEAD(WAITQ(i)); @@ -278,14 +308,15 @@ static int sedf_init_scheduler() { INIT_LIST_HEAD(EXTRAQ(i,EXTRA_PEN_Q)); INIT_LIST_HEAD(EXTRAQ(i,EXTRA_UTIL_Q)); } - dom_info_cache = xmem_cache_create("SEDF dom info", sizeof(struct sedf_dom_info), 0, 0, 0, NULL); + dom_info_cache = xmem_cache_create("SEDF dom info", + sizeof(struct sedf_dom_info), 0, 0, 0, NULL); if ( dom_info_cache == NULL ) { printk("Could not allocate SLAB cache.\n"); return -1; } - return 0; + return 0; } /* Allocates memory for per domain private scheduling data*/ @@ -300,21 +331,20 @@ static int sedf_alloc_task(struct domain *d) { /* Setup the sedf_dom_info */ static void sedf_add_task(struct domain *d) { - //s_time_t now=NOW(); struct sedf_dom_info *inf=DOM_INFO(d); inf->owner = d; PRINT(2,"sedf_add_task was called, domain-id %i\n",d->id); if (d->id==0) { - //set dom0 to something useful to boot the machine + /*set dom0 to something useful to boot the machine*/ inf->period = MILLISECS(20); inf->slice = MILLISECS(15); inf->latency = 0; inf->absdead = 0; - inf->extra = EXTRA_NONE;//EXTRA_AWARE; + inf->extra = EXTRA_NONE;/*EXTRA_AWARE; */ } else { - //other domains run in best effort mode + /*other domains run in best effort mode*/ inf->period = MILLISECS(20); inf->slice = 0; inf->absdead = 0; @@ -344,50 +374,64 @@ static int sedf_init_idle_task(struct domain *d) { sedf_add_task(d); DOM_INFO(d)->absdead = 0; set_bit(DF_RUNNING, &d->flags); - //the idle task doesn't have to turn up on any list... + /*the idle task doesn't have to turn up on any list...*/ return 0; } /* handles the rescheduling, bookkeeping of domains running in their realtime-time :)*/ static inline void desched_edf_dom (s_time_t now, struct domain* d) { struct sedf_dom_info* inf = DOM_INFO(d); + /*current domain is running in real time mode*/ - //current domain is running in real time mode - inf->cputime += now - inf->sched_start; //update the domains cputime - if ((inf->cputime < inf->slice) && domain_runnable(d)) //scheduling decisions, which don't remove the running domain from the runq - return; //there is nothing to do with the running task + /*update the domains cputime*/ + inf->cputime += now - inf->sched_start; + + /*scheduling decisions, which don't remove the running domain + from the runq*/ + if ((inf->cputime < inf->slice) && domain_runnable(d)) + return; __del_from_queue(d); /*if (__task_on_queue(current)) { - PRINT(0,"domain %i was removed but still on run/waitq => HALT\n",current->id); + PRINT(0,"domain %i was removed but still on run/waitq => "\ + "HALT\n",current->id); sedf_dump_cpu_state(0);(*((int*)0))++; }*/ - //manage bookkeeping (i.e. calculate next deadline, memorize overun time of slice) of finished domains + /*manage bookkeeping (i.e. calculate next deadline, + memorize overun-time of slice) of finished domains*/ if (inf->cputime >= inf->slice) { inf->cputime -= inf->slice; if (inf->period < inf->period_orig) { - //this domain runs in latency scaling or burst mode + /*this domain runs in latency scaling or burst mode*/ #if (UNBLOCK == UNBLOCK_BURST) if (now - inf->absunblock >= 2 * inf->period) #endif { inf->period *= 2; inf->slice *= 2; - if ((inf->period > inf->period_orig) || (inf->slice > inf->slice_orig)) { - inf->period = inf->period_orig; //update the domains cputime + if ((inf->period > inf->period_orig) || + (inf->slice > inf->slice_orig)) { + /*reset slice & period*/ + inf->period = inf->period_orig; inf->slice = inf->slice_orig; } } } - inf->absdead += inf->period; //set next deadline + /*set next deadline*/ + inf->absdead += inf->period; } - //if (inf->absdeadid,now,inf->absdead); - if (domain_runnable(d)) //add a runnable domain to the waitqueue + /*if (inf->absdeadid, now, + inf->absdead);*/ + + /*add a runnable domain to the waitqueue*/ + if (domain_runnable(d)) __add_to_waitqueue_sort(d); else { - inf->absblock = now; //we have a blocked realtime task + /*we have a blocked realtime task*/ + inf->absblock = now; #if (EXTRA > EXTRA_OFF) #if (EXTRA == EXTRA_BLOCK_WEIGHT) if (extraq_on(d,EXTRA_PEN_Q)) extraq_del(d,EXTRA_PEN_Q); @@ -398,15 +442,17 @@ static inline void desched_edf_dom (s_time_t now, struct domain* d) { } /* Update all elements on the queues */ -static inline void update_queues(s_time_t now, struct list_head* runq, struct list_head* waitq) { +static inline void update_queues( +s_time_t now, struct list_head* runq, struct list_head* waitq) { struct list_head *cur,*tmp; struct sedf_dom_info *curinf; PRINT(3,"Updating waitq..\n"); - //check for the first elements of the waitqueue, whether their next period has already started - list_for_each_safe(cur,tmp,waitq) { - curinf = list_entry(cur,struct sedf_dom_info,list); - PRINT(4,"\tLooking @ dom %i\n",curinf->owner->id); + /*check for the first elements of the waitqueue, whether their + next period has already started*/ + list_for_each_safe(cur, tmp, waitq) { + curinf = list_entry(cur, struct sedf_dom_info, list); + PRINT(4,"\tLooking @ dom %i\n", curinf->owner->id); if (PERIOD_BEGIN(curinf) <= now) { __del_from_queue(curinf->owner); __add_to_runqueue_sort(curinf->owner); @@ -416,29 +462,51 @@ static inline void update_queues(s_time_t now, struct list_head* runq, struct li } PRINT(3,"Updating runq..\n"); - //process the runq, find domains that are on the runqueue which shouldn't be there - list_for_each_safe(cur,tmp,runq) { + /*process the runq, find domains that are on + the runqueue which shouldn't be there*/ + list_for_each_safe(cur, tmp, runq) { curinf = list_entry(cur,struct sedf_dom_info,list); - PRINT(4,"\tLooking @ dom %i\n",curinf->owner->id); - if (unlikely(curinf->slice == 0)) { //ignore domains with empty slice - PRINT(4,"\tUpdating zero-slice domain %i\n",curinf->owner->id); + PRINT(4,"\tLooking @ dom %i\n", curinf->owner->id); + if (unlikely(curinf->slice == 0)) { + /*ignore domains with empty slice*/ + PRINT(4,"\tUpdating zero-slice domain %i\n", + curinf->owner->id); __del_from_queue(curinf->owner); - curinf->absdead += curinf->period; //move to their next period - __add_to_waitqueue_sort(curinf->owner); //and put them back into the queue + + /*move them to their next period*/ + curinf->absdead += curinf->period; + /*and put them back into the queue*/ + __add_to_waitqueue_sort(curinf->owner); } else { - if (unlikely((curinf->absdead < now) || (curinf->cputime > curinf->slice))) { - //we missed the deadline or the slice was already finished... might hapen because of dom_adj. - //printk("Ouch! Domain %i missed deadline %llu\n",curinf->owner->id,curinf->absdead); - PRINT(4,"\tDomain %i exceeded it's deadline/slice (%llu / %llu) now: %llu cputime: %llu\n", - curinf->owner->id, curinf->absdead, curinf->slice, now, curinf->cputime); + if (unlikely((curinf->absdead < now) || + (curinf->cputime > curinf->slice))) { + /*we missed the deadline or the slice was + already finished... might hapen because + of dom_adj.*/ + PRINT(4,"\tDomain %i exceeded it's deadline/"\ + "slice (%llu / %llu) now: %llu "\ + "cputime: %llu\n", curinf->owner->id, + curinf->absdead, curinf->slice, now, + curinf->cputime); __del_from_queue(curinf->owner); - curinf->absdead += curinf->period; //common case: we miss one period! + /*common case: we miss one period!*/ + curinf->absdead += curinf->period; + + /*if we are still behind: modulo arithmetic, + force deadline to be in future and + aligned to period borders!*/ if (unlikely(curinf->absdead < now)) - curinf->absdead += DIV_UP(now - curinf->absdead, curinf->period) * curinf->period; - //force start of period to be in future and aligned to period borders! - curinf->cputime = 0; //give a fresh slice - __add_to_runqueue_sort(curinf->owner); + curinf->absdead += + DIV_UP(now - curinf->absdead, + curinf->period) * curinf->period; + + /*give a fresh slice*/ + curinf->cputime = 0; + if (PERIOD_BEGIN(curinf) < now) + __add_to_waitqueue_sort(curinf->owner); + else + __add_to_runqueue_sort(curinf->owner); } else break; @@ -448,10 +516,12 @@ static inline void update_queues(s_time_t now, struct list_head* runq, struct li } #if (EXTRA > EXTRA_OFF) -/*removes a domain from the head of the according extraQ and requeues it at a specified position: - round-robin extratime: end of extraQ - weighted ext.: insert in sorted list by score - if the domain is blocked / has regained its short-block-loss time it is not put on any queue*/ +/* removes a domain from the head of the according extraQ and + requeues it at a specified position: + round-robin extratime: end of extraQ + weighted ext.: insert in sorted list by score + if the domain is blocked / has regained its short-block-loss + time it is not put on any queue */ static inline void desched_extra_dom(s_time_t now, struct domain* d) { struct sedf_dom_info *inf = DOM_INFO(d); int i = extra_get_cur_q(inf); @@ -459,66 +529,92 @@ static inline void desched_extra_dom(s_time_t now, struct domain* d) { #if (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) unsigned long oldscore; #endif - - inf->extra &= ~(EXTRA_RUN_PEN | EXTRA_RUN_UTIL); //unset all running flags - inf->cputime = 0; //fresh slice for the next run - inf->extra_time_tot += now - inf->sched_start; //accumulate total extratime - extraq_del(d, i); //remove extradomain from head of the queue + + /*unset all running flags*/ + inf->extra &= ~(EXTRA_RUN_PEN | EXTRA_RUN_UTIL); + /*fresh slice for the next run*/ + inf->cputime = 0; + /*accumulate total extratime*/ + inf->extra_time_tot += now - inf->sched_start; + /*remove extradomain from head of the queue*/ + extraq_del(d, i); #if (EXTRA == EXTRA_ROUNDR) if (domain_runnable(d)) - extraq_add_tail(d, EXTRA_UTIL_Q); //and add to the tail if it is runnable => round-robin + /*add to the tail if it is runnable => round-robin*/ + extraq_add_tail(d, EXTRA_UTIL_Q); #elif (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) - oldscore = inf->score[i]; //update the score + /*update the score*/ + oldscore = inf->score[i]; #if (EXTRA == EXTRA_BLOCK_WEIGHT) if (i == EXTRA_PEN_Q) { - //domain was running in L0 extraq - //inf->short_block_lost_tot -= EXTRA_QUANTUM; //reduce block lost, probably more sophistication here! + /*domain was running in L0 extraq*/ + /*reduce block lost, probably more sophistication here!*/ + /*inf->short_block_lost_tot -= EXTRA_QUANTUM;*/ inf->short_block_lost_tot -= now - inf->sched_start; - PRINT(3,"Domain %i: Short_block_lost: %lli\n",inf->owner->id,inf->short_block_lost_tot); + PRINT(3,"Domain %i: Short_block_lost: %lli\n", + inf->owner->id, inf->short_block_lost_tot); if (inf->short_block_lost_tot <= 0) { PRINT(4,"Domain %i compensated short block loss!\n"); - inf->short_block_lost_tot = 0; //we have (over-)compensated our block penalty - inf->extra &= ~EXTRA_WANT_PEN_Q; //we don't want a place on the penalty queue anymore! - return; //do not add us on this block extraq again! + /*we have (over-)compensated our block penalty*/ + inf->short_block_lost_tot = 0; + /*we don't want a place on the penalty queue anymore!*/ + inf->extra &= ~EXTRA_WANT_PEN_Q; + /*do not add us on this block extraq again!*/ + return; } - //we have to go again for another try in the block-extraq - inf->score[EXTRA_PEN_Q] = (inf->period << 10) / inf->short_block_lost_tot; + /*we have to go again for another try in the block-extraq, + the score is not used incremantally here, as this is + already done by recalculating the block_lost*/ + inf->score[EXTRA_PEN_Q] = (inf->period << 10) / + inf->short_block_lost_tot; oldscore = 0; } else #endif { - //domain was running in L1 extraq => score is inverse of utilization and is used somewhat incremental! + /*domain was running in L1 extraq => score is inverse of + utilization and is used somewhat incremental!*/ if (inf->slice) - inf->score[EXTRA_UTIL_Q] = (inf->period << 10) / inf->slice;//use fixed point arithmetic with 10 bits + /*NB: use fixed point arithmetic with 10 bits*/ + inf->score[EXTRA_UTIL_Q] = (inf->period << 10) / + inf->slice; else + /*set best effort domains to the maximum value*/ inf->score[EXTRA_UTIL_Q] = 2^10; } if (domain_runnable(d)) - extraq_add_sort_update(d, i, oldscore); //add according to score: weighted round robin + /*add according to score: weighted round robin*/ + extraq_add_sort_update(d, i, oldscore); else { inf->absblock = now; /*if (!__task_on_queue(d)) - printf("Oops... We attempt to remove d %i from the waitq, but it is not on :(\n",d->id);*/ - __del_from_queue(d); //also remove this blocked domain from the waitq! - //make sure that we remove a blocked domain from the other extraq aswell (this caused hours of debugging!) + printf("Oops... We attempt to remove d %i from the "\ + "waitq, but it is not on :(\n",d->id);*/ + /*remove this blocked domain from the waitq!*/ + __del_from_queue(d); + /*make sure that we remove a blocked domain from the other + extraq aswell (this caused hours of debugging!)*/ #if (EXTRA == EXTRA_BLOCK_WEIGHT) if (i == EXTRA_PEN_Q) { - if (extraq_on(d,EXTRA_UTIL_Q)) extraq_del(d,EXTRA_UTIL_Q); + if (extraq_on(d,EXTRA_UTIL_Q)) + extraq_del(d,EXTRA_UTIL_Q); } else { - if (extraq_on(d,EXTRA_PEN_Q)) extraq_del(d,EXTRA_PEN_Q); + if (extraq_on(d,EXTRA_PEN_Q)) + extraq_del(d,EXTRA_PEN_Q); } #endif } #endif /*if (!domain_runnable(d)) { if (extraq_on(d,EXTRA_UTIL_Q)) { - PRINT(0,"domain %i is blocked but still on L1 xq=> HALT\n",d->id); + PRINT(0,"domain %i is blocked but still on L1 "\ + "xq=> HALT\n",d->id); sedf_dump_cpu_state(0);(*((int*)0))++; } if (__task_on_queue(d)) { - PRINT(0,"domain %i is blocked but still on run/waitq=> HALT\n",d->id); + PRINT(0,"domain %i is blocked but still on run/waitq"\ + "=> HALT\n",d->id); sedf_dump_cpu_state(0);(*((int*)0))++; } }*/ @@ -526,27 +622,31 @@ static inline void desched_extra_dom(s_time_t now, struct domain* d) { #endif -static inline task_slice_t sedf_do_extra_schedule(s_time_t now, s_time_t end_xt, struct list_head *extraq[], int cpu) { +static inline task_slice_t sedf_do_extra_schedule +(s_time_t now, s_time_t end_xt, struct list_head *extraq[], int cpu) { task_slice_t ret; struct sedf_dom_info *runinf; - //TODO write this stuff as a loop if (end_xt - now < EXTRA_QUANTUM) goto return_idle; - +#if (EXTRA == EXTRA_BLOCK_WEIGHT) if (!list_empty(extraq[EXTRA_PEN_Q])) { - //we still have elements on the level 0 extraq => let those run first! - runinf = list_entry(extraq[EXTRA_PEN_Q]->next, struct sedf_dom_info, extralist[EXTRA_PEN_Q]); + /*we still have elements on the level 0 extraq + => let those run first!*/ + runinf = list_entry(extraq[EXTRA_PEN_Q]->next, + struct sedf_dom_info, extralist[EXTRA_PEN_Q]); runinf->extra |= EXTRA_RUN_PEN; ret.task = runinf->owner; ret.time = EXTRA_QUANTUM; #ifdef SEDF_STATS runinf->pen_extra_slices++; #endif - } - else if (!list_empty(extraq[EXTRA_UTIL_Q])) { - //use elements from the normal extraqueue - runinf = list_entry(extraq[EXTRA_UTIL_Q]->next,struct sedf_dom_info,extralist[EXTRA_UTIL_Q]); + } else +#endif + if (!list_empty(extraq[EXTRA_UTIL_Q])) { + /*use elements from the normal extraqueue*/ + runinf = list_entry(extraq[EXTRA_UTIL_Q]->next, + struct sedf_dom_info,extralist[EXTRA_UTIL_Q]); runinf->extra |= EXTRA_RUN_UTIL; ret.task = runinf->owner; ret.time = EXTRA_QUANTUM; @@ -562,10 +662,10 @@ return_idle: return ret; } /* Main scheduling function - * Reasons for calling this function are: - * -timeslice for the current period used up - * -domain on waitqueue has started it's period - * -and various others ;) in general: determine which domain to run next*/ + Reasons for calling this function are: + -timeslice for the current period used up + -domain on waitqueue has started it's period + -and various others ;) in general: determine which domain to run next*/ static task_slice_t sedf_do_schedule(s_time_t now) { int cpu = current->processor; @@ -573,48 +673,56 @@ static task_slice_t sedf_do_schedule(s_time_t now) struct list_head *waitq = WAITQ(cpu); #if (EXTRA > EXTRA_OFF) struct sedf_dom_info *inf = DOM_INFO(current); - struct list_head *extraq[] = {EXTRAQ(cpu,EXTRA_PEN_Q),EXTRAQ(cpu, EXTRA_UTIL_Q)}; + struct list_head *extraq[] = {EXTRAQ(cpu,EXTRA_PEN_Q), + EXTRAQ(cpu, EXTRA_UTIL_Q)}; #endif task_slice_t ret; - //int i = 0; - //idle tasks don't need any of the following stuf + /*int i = 0;*/ + /*idle tasks don't need any of the following stuf*/ if (is_idle_task(current)) - goto check_waitq; //idle task doesn't get moved around on any queues + goto check_waitq; #if (EXTRA > EXTRA_OFF) if (unlikely(extra_runs(inf))) { - //i=1; - desched_extra_dom(now, current); //special treatment of domains running in extra time + /*i=1;*/ + /*special treatment of domains running in extra time*/ + desched_extra_dom(now, current); } else #endif { - //i=2; + /*i=2;*/ desched_edf_dom(now, current); } /*if (!domain_runnable(current)) { if (extraq_on(current,EXTRA_UTIL_Q)) { - PRINT(0,"domain %i is blocked but still on L1 xq branch %i=> HALT\n",current->id,i); + PRINT(0,"domain %i is blocked but still on L1 xq"\ + " branch %i=> HALT\n", current->id, i); sedf_dump_cpu_state(0);(*((int*)0))++; } if (__task_on_queue(current)) { - PRINT(0,"domain %i is blocked but still on run/waitq branch %i=> HALT\n",current->id,i); + PRINT(0,"domain %i is blocked but still on run/waitq"\ + " branch %i=> HALT\n",current->id,i); sedf_dump_cpu_state(0);(*((int*)0))++; } }*/ check_waitq: update_queues(now, runq, waitq); - //now simply pick the first domain from the runqueue + /*now simply pick the first domain from the runqueue*/ struct sedf_dom_info *runinf, *waitinf; if (!list_empty(runq)) { runinf = list_entry(runq->next,struct sedf_dom_info,list); ret.task = runinf->owner; if (!list_empty(waitq)) { - //rerun scheduler, when scheduled domain reaches it's end of slice or the first domain from the waitqueue gets ready - waitinf = list_entry(waitq->next,struct sedf_dom_info,list); - ret.time = MIN(now + runinf->slice - runinf->cputime,PERIOD_BEGIN(waitinf)) - now; + waitinf = list_entry(waitq->next, + struct sedf_dom_info,list); + /*rerun scheduler, when scheduled domain reaches it's + end of slice or the first domain from the waitqueue + gets ready*/ + ret.time = MIN(now + runinf->slice - runinf->cputime, + PERIOD_BEGIN(waitinf)) - now; } else { ret.time = runinf->slice - runinf->cputime; @@ -624,25 +732,30 @@ check_waitq: if (!list_empty(waitq)) { waitinf = list_entry(waitq->next,struct sedf_dom_info,list); - //we could not find any suitable domain => look for domains that are aware of extratime + /*we could not find any suitable domain + => look for domains that are aware of extratime*/ #if (EXTRA > EXTRA_OFF) - ret = sedf_do_extra_schedule(now, PERIOD_BEGIN(waitinf), extraq, cpu); + ret = sedf_do_extra_schedule(now, PERIOD_BEGIN(waitinf), + extraq, cpu); #else ret.task = IDLETASK(cpu); ret.time = PERIOD_BEGIN(waitinf) - now; #endif } else { - //this could probably never happen, but one never knows... - //it can... imagine a second CPU, which is pure scifi ATM, but one never knows ;) + /*this could probably never happen, but one never knows...*/ + /*it can... imagine a second CPU, which is pure scifi ATM, + but one never knows ;)*/ ret.task = IDLETASK(cpu); ret.time = SECONDS(1); } sched_done: - //TODO: Do something USEFUL when this happens and find out, why it still can happen!!! + /*TODO: Do something USEFUL when this happens and find out, why it + still can happen!!!*/ if (ret.time<0) { - printk("Ouch! We are seriously BEHIND schedule! %lli\n",ret.time); + printk("Ouch! We are seriously BEHIND schedule! %lli\n", + ret.time); ret.time = EXTRA_QUANTUM; } DOM_INFO(ret.task)->sched_start=now; @@ -671,13 +784,14 @@ static void sedf_sleep(struct domain *d) { } } -/* This function wakes ifup a domain, i.e. moves them into the waitqueue +/* This function wakes up a domain, i.e. moves them into the waitqueue * things to mention are: admission control is taking place nowhere at * the moment, so we can't be sure, whether it is safe to wake the domain * up at all. Anyway, even if it is safe (total cpu usage <=100%) there are * some considerations on when to allow the domain to wake up and have it's * first deadline... - * I detected 3 cases, which could describe the possible behaviour of the scheduler, + * I detected 3 cases, which could describe the possible behaviour of the + * scheduler, * and I'll try to make them more clear: * * 1. Very conservative @@ -692,150 +806,221 @@ static void sedf_sleep(struct domain *d) { * -deadlines keep occuring isochronous * * 2. Conservative Part 1: Short Unblocking - * -when a domain unblocks in the same period as it was blocked it unblocks and - * may consume the rest of it's original time-slice minus the time it was blocked + * -when a domain unblocks in the same period as it was blocked it + * unblocks and may consume the rest of it's original time-slice minus + * the time it was blocked * (assume period=9, slice=5) * * DRB_UR___DRRRRR___D... * - * -this also doesn't disturb scheduling, but might lead to the fact, that the domain - * can't finish it's workload in the period - * -in addition to that the domain can be treated prioritised when extratime is available - * + * -this also doesn't disturb scheduling, but might lead to the fact, that + * the domain can't finish it's workload in the period + * -in addition to that the domain can be treated prioritised when + * extratime is available + * -addition: experiments hve shown that this may have a HUGE impact on + * performance of other domains, becaus it can lead to excessive context + * switches + * Part2: Long Unblocking * Part 2a - * -it is obvious that such behaviour, applied when then unblocking is happening in - * later periods, works fine aswell - * -the domain is treated as if it would have been running since the start of its new period + * -it is obvious that such accounting of block time, applied when + * unblocking is happening in later periods, works fine aswell + * -the domain is treated as if it would have been running since the start + * of its new period * * DRB______D___UR___D... * * Part 2b - * -if one needs the full slice in the next period, it is necessary to treat the unblocking - * time as the start of the new period, i.e. move the deadline further back (later) - * -this doesn't disturb scheduling as well, because for EDF periods can be treated as minimal - * inter-release times and scheduling stays correct, when deadlines are kept relative to the time - * the process unblocks + * -if one needs the full slice in the next period, it is necessary to + * treat the unblocking time as the start of the new period, i.e. move + * the deadline further back (later) + * -this doesn't disturb scheduling as well, because for EDF periods can + * be treated as minimal inter-release times and scheduling stays + * correct, when deadlines are kept relative to the time the process + * unblocks * - * DRB______D___URRRR___D... - * (D) + * DRB______D___URRRR___D... + * (D) <- old deadline was here * -problem: deadlines don't occur isochronous anymore * Part 2c (Improved Atropos design) - * -when a domain unblocks it is given a very short period (=latency hint) and slice length scaled - * accordingly + * -when a domain unblocks it is given a very short period (=latency hint) + * and slice length scaled accordingly * -both rise again to the original value (e.g. get doubled every period) * * 3. Unconservative (i.e. incorrect) - * -to boost the performance of I/O dependent domains it would be possible to put the domain into - * the runnable queue immediately, and let it run for the remainder of the slice of the current period + * -to boost the performance of I/O dependent domains it would be possible + * to put the domain into the runnable queue immediately, and let it run + * for the remainder of the slice of the current period * (or even worse: allocate a new full slice for the domain) - * -either behaviour can lead to missed deadlines in other domains as opposed to approaches 1,2a,2b + * -either behaviour can lead to missed deadlines in other domains as + * opposed to approaches 1,2a,2b */ -static inline void unblock_short_vcons(struct sedf_dom_info* inf, s_time_t now) { +static inline void unblock_short_vcons +(struct sedf_dom_info* inf, s_time_t now) { inf->absdead += inf->period; inf->cputime = 0; } -static inline void unblock_short_cons(struct sedf_dom_info* inf, s_time_t now) { - inf->cputime += now - inf->absblock; //treat blocked time as consumed by the domain +static inline void unblock_short_cons(struct sedf_dom_info* inf, s_time_t now) +{ + /*treat blocked time as consumed by the domain*/ + inf->cputime += now - inf->absblock; if (inf->cputime + EXTRA_QUANTUM > inf->slice) { - //we don't have a reasonable amount of time in our slice left :( - unblock_short_vcons(inf, now); //start in next period! + /*we don't have a reasonable amount of time in + our slice left :( => start in next period!*/ + unblock_short_vcons(inf, now); } #ifdef SEDF_STATS else - inf->short_cont++; //we let the domain run in the current period + inf->short_cont++; #endif } -static inline void unblock_short_extra_support(struct sedf_dom_info* inf, s_time_t now) { - /*this unblocking scheme tries to support the domain, by assigning it a priority in extratime distribution - according to the loss of time in this slice due to blocking*/ +static inline void unblock_short_extra_support (struct sedf_dom_info* inf, + s_time_t now) { + /*this unblocking scheme tries to support the domain, by assigning it + a priority in extratime distribution according to the loss of time + in this slice due to blocking*/ s_time_t pen; - inf->absdead += inf->period; //no more realtime execution in this period! + + /*no more realtime execution in this period!*/ + inf->absdead += inf->period; if (likely(inf->absblock)) { - //inf->cputime += now - inf->absblock; //treat blocked time as consumed by the domain + //treat blocked time as consumed by the domain*/ + /*inf->cputime += now - inf->absblock;*/ pen = (inf->slice - inf->cputime); if (pen < 0) pen = 0; - //inf->short_block_lost_tot += pen; //calc. penalty because of that - inf->short_block_lost_tot = pen; //set penalty to the current value - //not sure which one is better.. but seems to work well... + /*accumulate all penalties over the periods*/ + /*inf->short_block_lost_tot += pen;*/ + /*set penalty to the current value*/ + inf->short_block_lost_tot = pen; + /*not sure which one is better.. but seems to work well...*/ if (inf->short_block_lost_tot) { - inf->score[0] = (inf->period << 10) / inf->short_block_lost_tot; + inf->score[0] = (inf->period << 10) / + inf->short_block_lost_tot; #ifdef SEDF_STATS inf->pen_extra_blocks++; #endif if (extraq_on(inf->owner, EXTRA_PEN_Q)) - extraq_del(inf->owner, EXTRA_PEN_Q); //remove domain for possible resorting! - else //remember that we want to be on the penalty queue, - inf->extra |= EXTRA_WANT_PEN_Q; //so that we can continue when we (un-)blocked in pen-ext. - extraq_add_sort_update(inf->owner, EXTRA_PEN_Q, 0); //(re-)add domain to the penalty extraq + /*remove domain for possible resorting!*/ + extraq_del(inf->owner, EXTRA_PEN_Q); + else + /*remember that we want to be on the penalty q + so that we can continue when we (un-)block + in penalty-extratime*/ + inf->extra |= EXTRA_WANT_PEN_Q; + + /*(re-)add domain to the penalty extraq*/ + extraq_add_sort_update(inf->owner, + EXTRA_PEN_Q, 0); } } - inf->cputime = 0; //give it a fresh slice in the next period! + /*give it a fresh slice in the next period!*/ + inf->cputime = 0; } -static inline void unblock_long_vcons(struct sedf_dom_info* inf, s_time_t now) { - inf->absdead += ((now - inf->absdead) / inf->period + 1) * inf->period; //very conservative +static inline void unblock_long_vcons(struct sedf_dom_info* inf, s_time_t now) +{ + /* align to next future period */ + inf->absdead += ((now - inf->absdead) / inf->period + 1) + * inf->period; inf->cputime = 0; } -static inline void unblock_long_cons_a(struct sedf_dom_info* inf, s_time_t now) { - inf->cputime = (now - inf->absdead) % inf->period; //treat the time the domain was blocked in the CURRENT - //period as consumed by the domain +static inline void unblock_long_cons_a (struct sedf_dom_info* inf, + s_time_t now) { + /*treat the time the domain was blocked in the + CURRENT period as consumed by the domain*/ + inf->cputime = (now - inf->absdead) % inf->period; if (inf->cputime + EXTRA_QUANTUM > inf->slice) { - //we don't have a reasonable amount of time in our slice left :( - unblock_long_vcons(inf, now); //start in next period! + /*we don't have a reasonable amount of time in our slice + left :( => start in next period!*/ + unblock_long_vcons(inf, now); } } static inline void unblock_long_cons_b(struct sedf_dom_info* inf,s_time_t now) { - inf->absdead = now + inf->period; //Conservative 2b + /*Conservative 2b*/ + /*Treat the unblocking time as a start of a new period */ + inf->absdead = now + inf->period; inf->cputime = 0; } static inline void unblock_long_cons_c(struct sedf_dom_info* inf,s_time_t now) { if (likely(inf->latency)) { - //scale the slice and period accordingly to the latency hint - inf->period = inf->latency; //reduce period temporarily to the latency hint - ASSERT((inf->period < ULONG_MAX) && (inf->slice_orig < ULONG_MAX)); //this results in max. 4s slice/period length - inf->slice = (inf->period * inf->slice_orig) / inf->period_orig; //scale slice accordingly, so that utilisation stays the same + /*scale the slice and period accordingly to the latency hint*/ + /*reduce period temporarily to the latency hint*/ + inf->period = inf->latency; + /*this results in max. 4s slice/period length*/ + ASSERT((inf->period < ULONG_MAX) + && (inf->slice_orig < ULONG_MAX)); + /*scale slice accordingly, so that utilisation stays the same*/ + inf->slice = (inf->period * inf->slice_orig) + / inf->period_orig; + inf->absdead = now + inf->period; + inf->cputime = 0; } else { - //we don't have a latency hint.. use some other technique + /*we don't have a latency hint.. use some other technique*/ unblock_long_cons_b(inf, now); } } -//a new idea of dealing with short blocks: burst period scaling -static inline void unblock_short_burst(struct sedf_dom_info* inf, s_time_t now) { - inf->cputime += now - inf->absblock; //treat blocked time as consumed by the domain +/*a new idea of dealing with short blocks: burst period scaling*/ +static inline void unblock_short_burst(struct sedf_dom_info* inf, s_time_t now) +{ + /*treat blocked time as consumed by the domain*/ + inf->cputime += now - inf->absblock; if (inf->cputime + EXTRA_QUANTUM <= inf->slice) { + /*if we can still use some time in the current slice + then use it!*/ #ifdef SEDF_STATS - inf->short_cont++; //we let the domain run in the current period + /*we let the domain run in the current period*/ + inf->short_cont++; #endif } else { - //we don't have a reasonable amount of time in our slice left => switch to burst mode + /*we don't have a reasonable amount of time in + our slice left => switch to burst mode*/ if (likely(inf->absunblock)) { - inf->period = now - inf->absunblock; //set the period-length to the current blocking interval - ASSERT((inf->period < ULONG_MAX) && (inf->slice_orig < ULONG_MAX));//this results in max. 4s slice/period length - inf->slice = (inf->period * inf->slice_orig) / inf->period_orig;//scale slice accordingly, so that utilisation stays the same + /*set the period-length to the current blocking + interval, possible enhancements: average over last + blocking intervals, user-specified minimum,...*/ + inf->period = now - inf->absunblock; + /*check for overflow on multiplication*/ + ASSERT((inf->period < ULONG_MAX) + && (inf->slice_orig < ULONG_MAX)); + /*scale slice accordingly, so that utilisation + stays the same*/ + inf->slice = (inf->period * inf->slice_orig) + / inf->period_orig; + /*set new (shorter) deadline*/ + inf->absdead += inf->period; } else { - inf->cputime=0; //in Case we haven't unblocked before - inf->absdead += inf->period; //start in next period! + /*in case we haven't unblocked before + start in next period!*/ + inf->cputime=0; + inf->absdead += inf->period; } } inf->absunblock = now; } static inline void unblock_long_burst(struct sedf_dom_info* inf,s_time_t now) { if (unlikely(inf->latency && (inf->period > inf->latency))) { - //sclae the slice and period accordingly to the latency hint - inf->period = inf->latency; //reduce period temporarily to the latency hint - ASSERT((inf->period < ULONG_MAX) && (inf->slice_orig < ULONG_MAX)); //this results in max. 4s slice/period length - inf->slice = (inf->period * inf->slice_orig) / inf->period_orig; //scale slice accordingly, so that utilisation stays the same + /*scale the slice and period accordingly to the latency hint*/ + inf->period = inf->latency; + /*check for overflows on multiplication*/ + ASSERT((inf->period < ULONG_MAX) + && (inf->slice_orig < ULONG_MAX)); + /*scale slice accordingly, so that utilisation stays the same*/ + inf->slice = (inf->period * inf->slice_orig) + / inf->period_orig; + inf->absdead = now + inf->period; + inf->cputime = 0; } else { - //we don't have a latency hint.. or we are currently in "burst mode": use some other technique - //this should be in fact the normal way of operation, when we are in sync with the device! + /*we don't have a latency hint.. or we are currently in + "burst mode": use some other technique + NB: this should be in fact the normal way of operation, + when we are in sync with the device!*/ unblock_long_cons_b(inf, now); } inf->absunblock = now; @@ -855,34 +1040,42 @@ static inline int get_run_type(struct domain* d) { return DOMAIN_EXTRA_UTIL; return DOMAIN_EDF; } -/*Compares two domains in the relation of whether the one is allowed to interrupt the others execution. +/*Compares two domains in the relation of whether the one is allowed to + interrupt the others execution. It returns true (!=0) if a switch to the other domain is good. Current Priority scheme is as follows: - EDF > L0 (penalty based) extra-time > L1 (utilization) extra-time > idle-domain + EDF > L0 (penalty based) extra-time > + L1 (utilization) extra-time > idle-domain In the same class priorities are assigned as following: EDF: early deadline > late deadline L0 extra-time: lower score > higher score*/ -static inline int should_switch(struct domain* cur, struct domain* other, s_time_t now) { +static inline int should_switch(struct domain* cur, struct domain* other, + s_time_t now) { struct sedf_dom_info *cur_inf, *other_inf; cur_inf = DOM_INFO(cur); other_inf = DOM_INFO(other); + /*check whether we need to make an earlier sched-decision*/ + if ((PERIOD_BEGIN(other_inf) < + schedule_data[other->processor].s_timer.expires)) + return 1; + /*no timing-based switches need to be taken into account here*/ switch (get_run_type(cur)) { case DOMAIN_EDF: - //check whether we need to make an earlier sched-decision - if ((PERIOD_BEGIN(other_inf) < schedule_data[other->processor].s_timer.expires)) - return 1; - else return 0; + /* do not interrupt a running EDF domain */ + return 0; case DOMAIN_EXTRA_PEN: - //check whether we need an earlier decision or we also want the L0 ex-q with lower score - if ((PERIOD_BEGIN(other_inf) < schedule_data[other->processor].s_timer.expires) - || ((other_inf->extra & EXTRA_WANT_PEN_Q) && (other_inf->score[EXTRA_PEN_Q] < cur_inf->score[EXTRA_PEN_Q]))) + /*check whether we also want + the L0 ex-q with lower score*/ + if ((other_inf->extra & EXTRA_WANT_PEN_Q) + && (other_inf->score[EXTRA_PEN_Q] < + cur_inf->score[EXTRA_PEN_Q])) return 1; else return 0; case DOMAIN_EXTRA_UTIL: - //check whether we need an earlier decision or we want the L0 extraq, don't switch if both domains want L1 extraq - if ((PERIOD_BEGIN(other_inf) < schedule_data[other->processor].s_timer.expires) - || (other_inf->extra & EXTRA_WANT_PEN_Q)) + /*check whether we want the L0 extraq, don't + switch if both domains want L1 extraq */ + if (other_inf->extra & EXTRA_WANT_PEN_Q) return 1; else return 0; case DOMAIN_IDLE: @@ -890,7 +1083,6 @@ static inline int should_switch(struct domain* cur, struct domain* other, s_time } } void sedf_wake(struct domain *d) { - //for the first try just implement the "very conservative" way of waking domains up s_time_t now = NOW(); struct sedf_dom_info* inf = DOM_INFO(d); @@ -907,35 +1099,44 @@ void sedf_wake(struct domain *d) { PRINT(3,"\tdomain %i is already in the extraQ\n",d->id); } if (unlikely(inf->absdead == 0)) - inf->absdead = now + inf->slice; //initial setup of the deadline + /*initial setup of the deadline*/ + inf->absdead = now + inf->slice; - PRINT(3,"waking up domain %i (deadl= %llu period= %llu now= %llu)\n",d->id,inf->absdead,inf->period,now); + PRINT(3,"waking up domain %i (deadl= %llu period= %llu "\ + "now= %llu)\n",d->id,inf->absdead,inf->period,now); #ifdef SEDF_STATS inf->block_tot++; #endif if (unlikely(now< PERIOD_BEGIN(inf))) { PRINT(4,"extratime unblock\n"); - //this might happen, imagine unblocking in extra-time! + /*this might happen, imagine unblocking in extra-time!*/ #if (EXTRA == EXTRA_BLOCK_WEIGHT) - if (inf->extra & EXTRA_WANT_PEN_Q) { //we have a domain that wants compensation + if (inf->extra & EXTRA_WANT_PEN_Q) { + /*we have a domain that wants compensation + for block penalty and did just block in + its compensation time. Give it another + chance!*/ extraq_add_sort_update(d, EXTRA_PEN_Q, 0); - } /*else*/ + } #endif if (inf->extra & EXTRA_AWARE) #if (EXTRA == EXTRA_ROUNDR) - extraq_add_tail(d,EXTRA_UTIL_Q); //SD: Could extraq_add_head be better? - #elif (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) - extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); //put in on the weighted extraq + extraq_add_tail(d,EXTRA_UTIL_Q); + #elif (EXTRA == EXTRA_SLICE_WEIGHT \ + || EXTRA == EXTRA_BLOCK_WEIGHT) + /*put in on the weighted extraq, + without updating any scores*/ + extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); #else ; #endif - //else - //This is very very unlikely, ie. might even be an error?! + /*else*/ + /*This is very very unlikely, ie. might even be an error?!*/ } else { if (now < inf->absdead) { PRINT(4,"short unblocking\n"); - //short blocking + /*short blocking*/ #ifdef SEDF_STATS inf->short_block_tot++; #endif @@ -953,27 +1154,29 @@ void sedf_wake(struct domain *d) { #if (EXTRA == EXTRA_OFF) ; #elif (EXTRA == EXTRA_ROUNDR) - extraq_add_head(d, EXTRA_UTIL_Q); //Favour domains which got short unblocked - #elif (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) + /*Favour domains which got short unblocked*/ + extraq_add_head(d, EXTRA_UTIL_Q); + #elif (EXTRA == EXTRA_SLICE_WEIGHT \ + || EXTRA == EXTRA_BLOCK_WEIGHT) extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); #endif } else { PRINT(4,"long unblocking\n"); - //long blocking + /*long unblocking*/ #ifdef SEDF_STATS inf->long_block_tot++; #endif - //PRINT(3,"old=%llu ",inf->absdead); #if (UNBLOCK == UNBLOCK_ISOCHRONOUS_EDF) unblock_long_vcons(inf, now); - #elif (UNBLOCK == UNBLOCK_EDF || UNBLOCK == UNBLOCK_EXTRA_SUPPORT) + #elif (UNBLOCK == UNBLOCK_EDF \ + || UNBLOCK == UNBLOCK_EXTRA_SUPPORT) unblock_long_cons_b(inf, now); #elif (UNBLOCK == UNBLOCK_ATROPOS) unblock_long_cons_c(inf, now); #elif (UNBLOCK == UNBLOCK_SHORT_RESUME) unblock_long_cons_b(inf, now); - //unblock_short_cons_c(inf, now); + /*unblock_short_cons_c(inf, now);*/ #elif (UNBLOCK == UNBLOCK_BURST) unblock_long_burst(inf, now); #endif @@ -983,35 +1186,37 @@ void sedf_wake(struct domain *d) { ; #elif (EXTRA == EXTRA_ROUNDR) extraq_add_head(d, EXTRA_UTIL_Q); - #elif (EXTRA == EXTRA_SLICE_WEIGHT || EXTRA == EXTRA_BLOCK_WEIGHT) - //PRINT(2,"now try to add domain %i to the extra_util_q\n",d->id); + #elif (EXTRA == EXTRA_SLICE_WEIGHT \ + || EXTRA == EXTRA_BLOCK_WEIGHT) extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); - //PRINT(2,"added domain\n"); #endif } } } - PRINT(3,"woke up domain %i (deadl= %llu period= %llu now= %llu)\n",d->id,inf->absdead,inf->period,now); + PRINT(3,"woke up domain %i (deadl= %llu period= %llu "\ + "now= %llu)\n",d->id,inf->absdead,inf->period,now); __add_to_waitqueue_sort(d); PRINT(3,"added to waitq\n"); #ifdef SEDF_STATS - //do some statistics here... + /*do some statistics here...*/ if (inf->absblock != 0) { inf->block_time_tot += now - inf->absblock; - inf->penalty_time_tot += PERIOD_BEGIN(inf) + inf->cputime - inf->absblock; + inf->penalty_time_tot += + PERIOD_BEGIN(inf) + inf->cputime - inf->absblock; } #endif - //sanity check: make sure each extra-aware domain IS on the util-q! + /*sanity check: make sure each extra-aware domain IS on the util-q!*/ /*if (inf->extra & EXTRA_AWARE) { if (!extraq_on(d, EXTRA_UTIL_Q)) - printf("sedf_wake: domain %i is extra-aware, but NOT on L1 extraq!\n",d->id); + printf("sedf_wake: domain %i is extra-aware, "\ + "but NOT on L1 extraq!\n",d->id); }*/ - //check whether the awakened task needs to get scheduled before the next sched. decision - //and check, whether we are idling and this domain is extratime aware - //Save approximation: Always switch to scheduler! + /*check whether the awakened task needs to invoke the do_schedule + routine. Try to avoid unnecessary runs but: + Save approximation: Always switch to scheduler!*/ if (should_switch(schedule_data[d->processor].curr, d, now)){ #ifdef ADV_SCHED_HISTO adv_sched_hist_start(d->processor); @@ -1025,21 +1230,30 @@ static void sedf_dump_domain(struct domain *d) { printk("%u has=%c ", d->id, test_bit(DF_RUNNING, &d->flags) ? 'T':'F'); printk("p=%llu sl=%llu ddl=%llu w=%hu c=%llu sc=%i xtr(%s)=%llu", - DOM_INFO(d)->period, DOM_INFO(d)->slice, DOM_INFO(d)->absdead, DOM_INFO(d)->weight, d->cpu_time, - DOM_INFO(d)->score[EXTRA_UTIL_Q], (DOM_INFO(d)->extra & EXTRA_AWARE) ? "yes" : "no", DOM_INFO(d)->extra_time_tot); + DOM_INFO(d)->period, DOM_INFO(d)->slice, DOM_INFO(d)->absdead, + DOM_INFO(d)->weight, d->cpu_time, DOM_INFO(d)->score[EXTRA_UTIL_Q], + (DOM_INFO(d)->extra & EXTRA_AWARE) ? "yes" : "no", + DOM_INFO(d)->extra_time_tot); if (d->cpu_time !=0) - printf(" (%lu%)", (DOM_INFO(d)->extra_time_tot * 100) / d->cpu_time); + printf(" (%lu%)", (DOM_INFO(d)->extra_time_tot * 100) + / d->cpu_time); #ifdef SEDF_STATS if (DOM_INFO(d)->block_time_tot!=0) - printf(" pen=%lu%", (DOM_INFO(d)->penalty_time_tot * 100) / DOM_INFO(d)->block_time_tot); + printf(" pen=%lu%", (DOM_INFO(d)->penalty_time_tot * 100) / + DOM_INFO(d)->block_time_tot); if (DOM_INFO(d)->block_tot!=0) - printf("\n blks=%lu sh=%lu (%lu%) (shc=%lu (%lu%) shex=%i shexsl=%i) l=%lu (%lu%) avg: b=%llu p=%llu", DOM_INFO(d)->block_tot, - DOM_INFO(d)->short_block_tot, (DOM_INFO(d)->short_block_tot * 100) / DOM_INFO(d)->block_tot, - DOM_INFO(d)->short_cont, (DOM_INFO(d)->short_cont * 100) / DOM_INFO(d)->block_tot, - DOM_INFO(d)->pen_extra_blocks, DOM_INFO(d)->pen_extra_slices, - DOM_INFO(d)->long_block_tot, (DOM_INFO(d)->long_block_tot * 100) / DOM_INFO(d)->block_tot, - (DOM_INFO(d)->block_time_tot) / DOM_INFO(d)->block_tot, - (DOM_INFO(d)->penalty_time_tot) / DOM_INFO(d)->block_tot); + printf("\n blks=%lu sh=%lu (%lu%) (shc=%lu (%lu%) shex=%i "\ + "shexsl=%i) l=%lu (%lu%) avg: b=%llu p=%llu", + DOM_INFO(d)->block_tot, DOM_INFO(d)->short_block_tot, + (DOM_INFO(d)->short_block_tot * 100) + / DOM_INFO(d)->block_tot, DOM_INFO(d)->short_cont, + (DOM_INFO(d)->short_cont * 100) / DOM_INFO(d)->block_tot, + DOM_INFO(d)->pen_extra_blocks, + DOM_INFO(d)->pen_extra_slices, + DOM_INFO(d)->long_block_tot, + (DOM_INFO(d)->long_block_tot * 100) / DOM_INFO(d)->block_tot, + (DOM_INFO(d)->block_time_tot) / DOM_INFO(d)->block_tot, + (DOM_INFO(d)->penalty_time_tot) / DOM_INFO(d)->block_tot); #endif printf("\n"); } @@ -1072,19 +1286,23 @@ static void sedf_dump_cpu_state(int i) } queue = EXTRAQ(i,EXTRA_PEN_Q); loop = 0; - printk("\nEXTRAQ (penalty) rq %lx n: %lx, p: %lx\n", (unsigned long)queue, - (unsigned long) queue->next, (unsigned long) queue->prev); + printk("\nEXTRAQ (penalty) rq %lx n: %lx, p: %lx\n", + (unsigned long)queue, (unsigned long) queue->next, + (unsigned long) queue->prev); list_for_each_safe ( list, tmp, queue ) { - d_inf = list_entry(list, struct sedf_dom_info, extralist[EXTRA_PEN_Q]); + d_inf = list_entry(list, struct sedf_dom_info, + extralist[EXTRA_PEN_Q]); printk("%3d: ",loop++); sedf_dump_domain(d_inf->owner); } queue = EXTRAQ(i,EXTRA_UTIL_Q); loop = 0; - printk("\nEXTRAQ (utilization) rq %lx n: %lx, p: %lx\n", (unsigned long)queue, - (unsigned long) queue->next, (unsigned long) queue->prev); + printk("\nEXTRAQ (utilization) rq %lx n: %lx, p: %lx\n", + (unsigned long)queue, (unsigned long) queue->next, + (unsigned long) queue->prev); list_for_each_safe ( list, tmp, queue ) { - d_inf = list_entry(list, struct sedf_dom_info, extralist[EXTRA_UTIL_Q]); + d_inf = list_entry(list, struct sedf_dom_info, + extralist[EXTRA_UTIL_Q]); printk("%3d: ",loop++); sedf_dump_domain(d_inf->owner); } @@ -1098,8 +1316,9 @@ static void sedf_dump_cpu_state(int i) } } } -//Adjusts periods and slices of the domains accordingly to their weights -static inline int sedf_adjust_weights(struct domain *p, struct sched_adjdom_cmd *cmd) { +/*Adjusts periods and slices of the domains accordingly to their weights*/ +static inline int sedf_adjust_weights(struct domain *p, +struct sched_adjdom_cmd *cmd) { int sumw[NR_CPUS]; s_time_t sumt[NR_CPUS]; int cpu; @@ -1108,25 +1327,31 @@ static inline int sedf_adjust_weights(struct domain *p, struct sched_adjdom_cmd sumw[cpu] = 0; sumt[cpu] = 0; } - //sum up all weights + /*sum up all weights*/ for_each_domain(p) { if (DOM_INFO(p)->weight) sumw[p->processor] += DOM_INFO(p)->weight; else { - //don't modify domains who don't have a weight, but sum up - //the time they need, projected to a WEIGHT_PERIOD, so that - //this time is not given to the weight-driven domains - ASSERT((WEIGHT_PERIOD < ULONG_MAX) && (DOM_INFO(p)->slice_orig < ULONG_MAX)); //this results in max. 4s slice/period length - sumt[p->processor] += (WEIGHT_PERIOD * DOM_INFO(p)->slice_orig) / DOM_INFO(p)->period_orig; + /*don't modify domains who don't have a weight, but sum + up the time they need, projected to a WEIGHT_PERIOD, + so that this time is not given to the weight-driven + domains*/ + /*check for overflows*/ + ASSERT((WEIGHT_PERIOD < ULONG_MAX) + && (DOM_INFO(p)->slice_orig < ULONG_MAX)); + sumt[p->processor] += (WEIGHT_PERIOD * + DOM_INFO(p)->slice_orig) / DOM_INFO(p)->period_orig; } } - //adjust all slices (and periods) to the new weight + /*adjust all slices (and periods) to the new weight*/ for_each_domain(p) { if (DOM_INFO(p)->weight) { DOM_INFO(p)->period_orig = DOM_INFO(p)->period = WEIGHT_PERIOD; DOM_INFO(p)->slice_orig = - DOM_INFO(p)->slice = (DOM_INFO(p)->weight * (WEIGHT_PERIOD - WEIGHT_SAFETY - sumt[p->processor])) / sumw[p->processor]; + DOM_INFO(p)->slice = (DOM_INFO(p)->weight * + (WEIGHT_PERIOD -WEIGHT_SAFETY - + sumt[p->processor])) / sumw[p->processor]; } } return 0; @@ -1134,19 +1359,24 @@ static inline int sedf_adjust_weights(struct domain *p, struct sched_adjdom_cmd /* set or fetch domain scheduling parameters */ static int sedf_adjdom(struct domain *p, struct sched_adjdom_cmd *cmd) { - PRINT(2,"sedf_adjdom was called, domain-id %i new period %llu new slice %llu\n" - "latency %llu extra: %s\n",p->id,cmd->u.sedf.period,cmd->u.sedf.slice,cmd->u.sedf.latency,(cmd->u.sedf.extratime)?"yes":"no"); + PRINT(2,"sedf_adjdom was called, domain-id %i new period %llu "\ + "new slice %llu\nlatency %llu extra:%s\n", + p->id, cmd->u.sedf.period, cmd->u.sedf.slice, + cmd->u.sedf.latency, (cmd->u.sedf.extratime)?"yes":"no"); if ( cmd->direction == SCHED_INFO_PUT ) { - if (!cmd->u.sedf.period && !cmd->u.sedf.weight) //check for sane parameters + /*check for sane parameters*/ + if (!cmd->u.sedf.period && !cmd->u.sedf.weight) return -EINVAL; + /*weight driven domains*/ if (cmd->u.sedf.weight) { - DOM_INFO(p)->weight = cmd->u.sedf.weight; //weight driven domains + DOM_INFO(p)->weight = cmd->u.sedf.weight; } else { - //time driven domains + /*time driven domains*/ DOM_INFO(p)->weight = 0; - if(cmd->u.sedf.slice > cmd->u.sedf.period ) /* sanity checking! */ + /* sanity checking! */ + if(cmd->u.sedf.slice > cmd->u.sedf.period ) return -EINVAL; DOM_INFO(p)->period_orig = DOM_INFO(p)->period = cmd->u.sedf.period; @@ -1155,7 +1385,8 @@ static int sedf_adjdom(struct domain *p, struct sched_adjdom_cmd *cmd) { } if (sedf_adjust_weights(p,cmd)) return -EINVAL; - DOM_INFO(p)->extra = (DOM_INFO(p)-> extra & ~EXTRA_AWARE) | (cmd->u.sedf.extratime & EXTRA_AWARE); + DOM_INFO(p)->extra = (DOM_INFO(p)-> extra & ~EXTRA_AWARE) + | (cmd->u.sedf.extratime & EXTRA_AWARE); DOM_INFO(p)->latency = cmd->u.sedf.latency; extraq_check(p); } From 60e19526de2a02efa8f66715629b25579f9f3a77 Mon Sep 17 00:00:00 2001 From: "sd386@font.cl.cam.ac.uk" Date: Fri, 1 Apr 2005 13:01:31 +0000 Subject: [PATCH 9/9] bitkeeper revision 1.1159.170.109 (424d462b3t7TNRulCLgZcZNC5I8oew) Added a very brief usage guide on the sedf scheduler. --- .rootkeys | 1 + docs/misc/sedf_scheduler_mini-HOWTO.txt | 44 +++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 docs/misc/sedf_scheduler_mini-HOWTO.txt diff --git a/.rootkeys b/.rootkeys index be0623acf5..6231a0c75c 100644 --- a/.rootkeys +++ b/.rootkeys @@ -17,6 +17,7 @@ 418a3248xjIqmNKo0v_XQSfAvlBGFw docs/html.sty 4022a73cgxX1ryj1HgS-IwwB6NUi2A docs/misc/XenDebugger-HOWTO 412f4bd9sm5mCQ8BkrgKcAKZGadq7Q docs/misc/blkif-drivers-explained.txt +424d462b5GuApQ_NyMsRFt9LbrsWow docs/misc/sedf_scheduler_mini-HOWTO.txt 40d6ccbfKKBq8jE0ula4eHEzBiQuDA docs/misc/xen_config.html 410a4c2bAO_m_l4RsiiPHnZ4ixHWbQ docs/misc/xend.tex 3f9e7d564bWFB-Czjv1qdmE6o0GqNg docs/src/interface.tex diff --git a/docs/misc/sedf_scheduler_mini-HOWTO.txt b/docs/misc/sedf_scheduler_mini-HOWTO.txt new file mode 100644 index 0000000000..ad3cc62e1f --- /dev/null +++ b/docs/misc/sedf_scheduler_mini-HOWTO.txt @@ -0,0 +1,44 @@ +sEDF scheduler +-------------- +Author: + Stephan.Diestelhorst@{cl.cam.ac.uk, inf.tu-dresden.de} + +Overview: + This scheduler provides weighted CPU sharing in an intuitive way and + uses realtime-algorithms to ensure time guarantees. + +Usage: +   -add "sched=sedf" on Xen's boot command-line +   -create domains as usual +   -use "xm sedf " +  Where: +  -period/slice are the normal EDF scheduling parameters in nanosecs +  -latency-hint is the scaled period in case the domain is doing heavy I/O + (unused by the currently compiled version) +  -extra is a flag (0/1), which controls whether the domain can run in + extra-time +  -weight is mutually exclusive with period/slice and specifies another + way of setting a domains cpu slice + +Examples: + normal EDF (20ms/5ms): +  xm sedf 20000000 5000000 0 0 0 + + best-effort domains (i.e. non-realtime): +  xm sedf 20000000 0 0 1 0 +  + normal EDF (20ms/5ms) + share of extra-time: +  xm sedf 20000000 5000000 0 1 0 + + 4 domains with weights 2:3:4:2 +  xm sedf 0 0 0 0 2 +  xm sedf 0 0 0 0 3 +  xm sedf 0 0 0 0 4 +  xm sedf 0 0 0 0 2 + + 1 fully-specified (10ms/3ms) domain, 3 other domains share + available rest in 2:7:3 ratio: +  xm sedf 10000000 3000000 0 0 0 +  xm sedf 0 0 0 0 2 +  xm sedf 0 0 0 0 7 +  xm sedf 0 0 0 0 3 \ No newline at end of file