This is a patch to the Xen Hypervisor that adds a scheduler
that provides partial ARINC653 CPU scheduling.
Anthony Boorsma
www.DornerWorks.com
*** Diffing -rupN
diff -rupN a/tools/libxc/xc_core.c b/tools/libxc/xc_core.c
--- a/tools/libxc/xc_core.c 2009-08-06
09:57:25.000000000 -0400
+++ b/tools/libxc/xc_core.c 2010-03-19
09:07:29.595745100 -0400
@@ -321,7 +321,15 @@ elfnote_dump_none(void *args,
dumpcore_r
struct xen_dumpcore_elfnote_none_desc
none;
elfnote_init(&elfnote);
- memset(&none, 0, sizeof(none));
+ /*
+ * josh holtrop
<DornerWorks.com> - 2009-01-04 - avoid compilation problem
+ * with warning "memset used
with constant zero length parameter" and
+ * warnings treated as errors with
the new gcc in Ubuntu 9.10
+ */
+ if (sizeof(none) > 0)
+ {
+
memset(&none, 0, sizeof(none));
+ }
elfnote.descsz = sizeof(none);
elfnote.type =
XEN_ELFNOTE_DUMPCORE_NONE;
diff -rupN a/tools/libxc/xc_misc.c b/tools/libxc/xc_misc.c
--- a/tools/libxc/xc_misc.c 2009-08-06
09:57:25.000000000 -0400
+++ b/tools/libxc/xc_misc.c 2010-03-19
09:09:45.906278500 -0400
@@ -2,6 +2,8 @@
* xc_misc.c
*
* Miscellaneous control interface functions.
+ *
+ * xc_sched_op function added by DornerWorks
<DornerWorks.com>.
*/
#include "xc_private.h"
@@ -358,6 +360,60 @@ void *xc_map_foreign_pages(int
xc_handle
return res;
}
+int xc_sched_op(int xc_handle, int sched_op, void * arg)
+{
+ DECLARE_HYPERCALL;
+ int rc;
+ int argsize = 0;
+
+ hypercall.op =
__HYPERVISOR_sched_op;
+ hypercall.arg[0] = sched_op;
+ hypercall.arg[1] = (unsigned long) arg;
+
+ switch (sched_op)
+ {
+ case SCHEDOP_yield:
+ argsize = 0;
+ break;
+ case SCHEDOP_block:
+ argsize = 0;
+ break;
+ case SCHEDOP_shutdown:
+ argsize =
sizeof(sched_shutdown_t);
+ break;
+ case SCHEDOP_poll:
+ argsize =
sizeof(sched_poll_t);
+ break;
+ case SCHEDOP_remote_shutdown:
+ argsize =
sizeof(sched_remote_shutdown_t);
+ break;
+ case SCHEDOP_arinc653_sched_set:
+ argsize =
sizeof(sched_arinc653_sched_set_t);
+ break;
+ default:
+
PERROR("xc_sched_op(): Unknown scheduler operation.");
+ break;
+ }
+
+ if (argsize > 0)
+ {
+ if ( (rc =
lock_pages(arg, argsize)) != 0 )
+ {
+
PERROR("Could not lock memory");
+
return rc;
+ }
+ }
+
+ rc = do_xen_hypercall(xc_handle,
&hypercall);
+
+ if (argsize > 0)
+ {
+
unlock_pages(arg, argsize);
+ }
+
+ return rc;
+}
+
/*
* Local variables:
* mode: C
diff -rupN a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h
--- a/tools/libxc/xenctrl.h 2009-08-06
09:57:25.000000000 -0400
+++ b/tools/libxc/xenctrl.h 2010-03-19
09:16:32.104190500 -0400
@@ -7,6 +7,9 @@
*
* xc_gnttab functions:
* Copyright (c) 2007-2008, D G Murray <Derek.Murray@xxxxxxxxxxxx>
+ *
+ * xc_sched_op function:
+ * Copyright (c) 2010, DornerWorks, Ltd.
<DornerWorks.com>
*/
#ifndef XENCTRL_H
@@ -1267,4 +1270,7 @@ int xc_get_vcpu_migration_delay(int
xc_h
int xc_get_cpuidle_max_cstate(int xc_handle, uint32_t
*value);
int xc_set_cpuidle_max_cstate(int xc_handle, uint32_t
value);
+/* perform a scheduler operation */
+int xc_sched_op(int xc_handle, int sched_op, void * arg);
+
#endif /* XENCTRL_H */
diff -rupN a/xen/common/Makefile b/xen/common/Makefile
--- a/xen/common/Makefile 2009-08-06
09:57:27.000000000 -0400
+++ b/xen/common/Makefile 2010-03-18
19:34:05.200130400 -0400
@@ -13,6 +13,7 @@ obj-y += page_alloc.o
obj-y += rangeset.o
obj-y += sched_credit.o
obj-y += sched_sedf.o
+obj-y += sched_arinc653.o
obj-y += schedule.o
obj-y += shutdown.o
obj-y += softirq.o
diff -rupN a/xen/common/sched_arinc653.c
b/xen/common/sched_arinc653.c
--- a/xen/common/sched_arinc653.c 1969-12-31
19:00:00.000000000 -0500
+++ b/xen/common/sched_arinc653.c 2010-03-19
09:12:32.105381300 -0400
@@ -0,0 +1,725 @@
+/*
+ * File: sched_arinc653.c
+ * Copyright (c) 2009, DornerWorks, Ltd.
<DornerWorks.com>
+ *
+ * Description:
+ * This file provides an ARINC653-compatible
scheduling algorithm
+ * for use in Xen.
+ *
+ * This program is free software; you can redistribute it
and/or modify it
+ * under the terms of the GNU General Public License as
published by the Free
+ * software Foundation; either version 2 of the License, or
(at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be
useful,
+ * but WITHOUT ANY WARRANTY; without even the implied
warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ */
+
+
+/**************************************************************************
+ *
Includes
*
+
*************************************************************************/
+#include <xen/lib.h>
+#include <xen/sched.h>
+#include <xen/sched-if.h>
+#include <xen/timer.h>
+#include <xen/softirq.h>
+#include <xen/time.h>
+#include <xen/errno.h>
+#include <xen/sched_arinc653.h>
+#include <xen/list.h>
+#include
<public/sched.h>
/* ARINC653_MAX_DOMAINS_PER_SCHEDULE */
+
+
+/**************************************************************************
+ * Private
Macros
*
+
*************************************************************************/
+
+/* Retrieve the idle VCPU for a given physical CPU */
+#define IDLETASK(cpu) ((struct vcpu *)
per_cpu(schedule_data, (cpu)).idle)
+
+/*
+ * Return a pointer to the ARINC653-specific scheduler data
information
+ * associated with the given VCPU (vc)
+ */
+#define AVCPU(vc) ((arinc653_vcpu_t *)(vc)->sched_priv)
+
+/**************************************************************************
+ * Private Type
Definitions
*
+ *************************************************************************/
+/*
+ * The sched_entry_t structure holds a single entry of the
+ * ARINC653 schedule.
+ */
+typedef struct sched_entry_s
+{
+ /* dom_handle holds the handle
("UUID") for the domain that this
+ * schedule entry refers to. */
+ xen_domain_handle_t dom_handle;
+ /* vcpu_id holds the VCPU number for the
VCPU that this schedule
+ * entry refers to. */
+
int
vcpu_id;
+ /* runtime holds the number of
nanoseconds that the VCPU for this
+ * schedule entry should be allowed
to run per major frame. */
+
s_time_t
runtime;
+} sched_entry_t;
+
+/*
+ * The arinc653_vcpu_t structure holds
ARINC653-scheduler-specific
+ * information for all non-idle VCPUs
+ */
+typedef struct arinc653_vcpu_s
+{
+ /* runtime stores the number of
nanoseconds that this VCPU is allowed
+ * to run per major frame. */
+
s_time_t
runtime;
+ /* time_left stores the number of
nanoseconds (its "credit") that this
+ * VCPU still has left in the
current major frame. */
+
s_time_t
time_left;
+ /* last_activation_time stores the time
that this VCPU was switched
+ * to for credit-accounting
purposes. */
+
s_time_t
last_activation_time;
+ /* list holds the linked list
information for whichever list this
+ * VCPU is stored on. */
+ struct list_head list;
+ /* vc points to Xen's struct vcpu so we
can get to it from an
+ * arinc653_vcpu_t pointer. */
+ struct vcpu
* vc;
+ /* The active flag tells whether this
VCPU is active in the current
+ * ARINC653 schedule or not. */
+
bool_t
active;
+} arinc653_vcpu_t;
+
+
+/**************************************************************************
+ * Global
Data
*
+
*************************************************************************/
+
+/*
+ * This array holds the active ARINC653 schedule.
+ * When the system tries to start a new VCPU, this schedule
is scanned
+ * to look for a matching (handle, VCPU #) pair. If both
the handle ("UUID")
+ * and VCPU number match, then the VCPU is allowed to run.
Its run time
+ * (per major frame) is given in the third entry of the
schedule.
+ */
+static sched_entry_t
arinc653_schedule[ARINC653_MAX_DOMAINS_PER_SCHEDULE] = {
+ { "", 0, MILLISECS(10) }
+};
+
+/*
+ * This variable holds the number of entries that are valid
in
+ * the arinc653_schedule table.
+ * This is not necessarily the same as the number of
domains in the
+ * schedule, since a domain with multiple VCPUs could have
a different
+ * schedule entry for each VCPU.
+ */
+static int num_schedule_entries = 1;
+
+/*
+ * arinc653_major_frame holds the major frame time for the
ARINC653 schedule.
+ */
+static s_time_t arinc653_major_frame = MILLISECS(10);
+
+/*
+ * next_major_frame holds the time that the next major
frame starts
+ */
+static s_time_t next_major_frame = 0;
+
+/* Linked list to store runnable domains in the current
schedule with
+ * time left this major frame */
+static LIST_HEAD(run_list);
+
+/* Linked list to store blocked domains in the current
schedule */
+static LIST_HEAD(blocked_list);
+
+/* Linked list to store runnable domains in the current
schedule
+ * that have no time left in this major frame */
+static LIST_HEAD(expired_list);
+
+/* Linked list to store runnable domains not in the current
schedule */
+static LIST_HEAD(deactivated_run_list);
+
+/* Linked list to store blocked domains not in the current
schedule */
+static LIST_HEAD(deactivated_blocked_list);
+
+
+/**************************************************************************
+ * Scheduler
functions
*
+
*************************************************************************/
+
+static int dom_handle_cmp(const xen_domain_handle_t h1,
+ const
xen_domain_handle_t h2)
+{
+ return memcmp(h1, h2,
sizeof(xen_domain_handle_t));
+}
+
+/*
+ * This function scans the current ARINC653 schedule and
looks
+ * for an entry that matches the VCPU v.
+ * If an entry is found, a pointer to it is returned.
+ * Otherwise, NULL is returned.
+ */
+static sched_entry_t * find_sched_entry(struct vcpu * v)
+{
+ sched_entry_t * sched_entry = NULL;
+ if (v != NULL)
+ {
+ for (int i = 0;
i < num_schedule_entries; i++)
+ {
+
if ( (v->vcpu_id == arinc653_schedule[i].vcpu_id)
+
&& (dom_handle_cmp(arinc653_schedule[i].dom_handle,
+
v->domain->handle) == 0))
+
{
+
sched_entry = &arinc653_schedule[i];
+
break;
+
}
+ }
+ }
+ return sched_entry;
+}
+
+/*
+ * This function is called by the hypervisor when a
privileged domain
+ * invokes the HYPERVISOR_sched_op hypercall with a command
of
+ * SCHEDOP_arinc653_sched_set.
+ * It returns 0 on success and nonzero upon error.
+ * This function is only called from do_sched_op(), defined
within
+ * xen/common/schedule.c. The parameter schedule is set to
be the
+ * address of a local variable from within do_sched_op(),
so it is
+ * guaranteed not to be NULL.
+ */
+int arinc653_sched_set(sched_arinc653_sched_set_t *
schedule)
+{
+ int ret = 0;
+ s_time_t total_runtime = 0;
+ int found_dom0 = 0;
+ const static xen_domain_handle_t
dom0_handle = {0};
+
+ /* check for valid major frame and
number of schedule entries */
+ if ( (schedule->major_frame <= 0)
+ ||
(schedule->num_sched_entries < 1)
+ ||
(schedule->num_sched_entries > ARINC653_MAX_DOMAINS_PER_SCHEDULE) )
+ {
+ ret = -EINVAL;
+ }
+ if (ret == 0)
+ {
+ for (int i = 0;
i < schedule->num_sched_entries; i++)
+ {
+
/*
+
* look for domain 0 handle - every schedule must contain
+
* some time for domain 0 to run
+
*/
+
if (dom_handle_cmp(schedule->sched_entries[i].dom_handle,
+ dom0_handle)
== 0)
+
{
+
found_dom0 = 1;
+
}
+
/* check for a valid VCPU id and runtime */
+
if ( (schedule->sched_entries[i].vcpu_id < 0)
+
|| (schedule->sched_entries[i].runtime <= 0) )
+
{
+
ret = -EINVAL;
+
}
+
else
+
{
+
total_runtime += schedule->sched_entries[i].runtime;
+
}
+ }
+ }
+ if (ret == 0)
+ {
+ /* error if the
schedule doesn't contain a slot for domain 0 */
+ if (found_dom0
== 0)
+ {
+
ret = -EINVAL;
+ }
+ }
+ if (ret == 0)
+ {
+ /* error if the
major frame is not large enough to run all entries */
+ if
(total_runtime > schedule->major_frame)
+ {
+
ret = -EINVAL;
+ }
+ }
+ if (ret == 0)
+ {
+ arinc653_vcpu_t
* avcpu;
+ arinc653_vcpu_t
* avcpu_tmp;
+
+ /* copy the new
schedule into place */
+
num_schedule_entries = schedule->num_sched_entries;
+
arinc653_major_frame = schedule->major_frame;
+ for (int i = 0;
i < schedule->num_sched_entries; i++)
+ {
+
memcpy(arinc653_schedule[i].dom_handle,
+
schedule->sched_entries[i].dom_handle,
+
sizeof(arinc653_schedule[i].dom_handle));
+
arinc653_schedule[i].vcpu_id = schedule->sched_entries[i].vcpu_id;
+
arinc653_schedule[i].runtime = schedule->sched_entries[i].runtime;
+ }
+
+ /*
+ * The
newly installed schedule takes effect immediately.
+ * We do
not even wait for the current major frame to expire.
+ * So, we
need to update all of our VCPU lists to reflect the
+ * new
schedule here.
+ */
+
+ /*
+ * There
should be nothing in the expired_list when we start the
+ * next
major frame for the new schedule, so move everything
+ *
currently there into the run_list.
+ */
+
list_splice_init(&expired_list, &run_list);
+
+ /*
+ * Process
entries on the run_list (this will now include
+ * entries
that just came from the expired list).
+ * If the
VCPU is in the current schedule, update its
+ * runtime
and mark it active.
+ * The
time_left parameter will be updated upon the next
+ *
invocation of the do_schedule callback function because a
+ * new
major frame will start.
+ * It is
just set to zero here "defensively."
+ * If the
VCPU is not in the new schedule, mark it inactive
+ * and move
it to the deactivated_run_list.
+ */
+
list_for_each_entry_safe(avcpu, avcpu_tmp, &run_list, list)
+ {
+
sched_entry_t * sched_entry = find_sched_entry(avcpu->vc);
+
if (sched_entry != NULL)
+
{
+
avcpu->active = 1;
+
avcpu->runtime = sched_entry->runtime;
+
avcpu->time_left = 0;
+
}
+
else
+
{
+
avcpu->active = 0;
+
list_move(&avcpu->list, &deactivated_run_list);
+
}
+ }
+
+ /*
+ * Process
entries on the blocked_list.
+ * If the
VCPU is in the current schedule, update its
+ * runtime
and mark it active.
+ * The
time_left parameter will be updated upon the next
+ *
invocation of the do_schedule callback function because a
+ * new
major frame will start.
+ * It is
just set to zero here "defensively."
+ * If the
VCPU is not in the new schedule, mark it inactive
+ * and move
it to the deactivated_blocked_list.
+ */
+
list_for_each_entry_safe(avcpu, avcpu_tmp, &blocked_list, list)
+ {
+
sched_entry_t * sched_entry = find_sched_entry(avcpu->vc);
+
if (sched_entry != NULL)
+
{
+
avcpu->active = 1;
+ avcpu->runtime
= sched_entry->runtime;
+
avcpu->time_left = 0;
+
}
+
else
+
{
+
avcpu->active = 0;
+
list_move(&avcpu->list, &deactivated_blocked_list);
+
}
+ }
+
+ /*
+ * Process
entries on the deactivated_run_list.
+ * If the
VCPU is now in the current schedule, update its
+ * runtime,
mark it active, and move it to the run_list.
+ * The
time_left parameter will be updated upon the next
+ *
invocation of the do_schedule callback function because a
+ * new
major frame will start.
+ * It is
just set to zero here "defensively."
+ * If the
VCPU is not in the new schedule, do nothing because
+ * it is
already in the correct place.
+ */
+
list_for_each_entry_safe(avcpu, avcpu_tmp, &deactivated_run_list, list)
+ {
+
sched_entry_t * sched_entry = find_sched_entry(avcpu->vc);
+
if (sched_entry != NULL)
+
{
+
avcpu->active = 1;
+
avcpu->runtime = sched_entry->runtime;
+
avcpu->time_left = 0;
+
list_move(&avcpu->list, &run_list);
+
}
+ }
+
+ /*
+ * Process
entries on the deactivated_blocked_list.
+ * If the
VCPU is now in the current schedule, update its
+ * runtime,
mark it active, and move it to the blocked_list.
+ * The
time_left parameter will be updated upon the next
+ *
invocation of the do_schedule callback function because a
+ * new
major frame will start.
+ * It is
just set to zero here "defensively."
+ * If the
VCPU is not in the new schedule, do nothing because
+ * it is
already in the correct place.
+ */
+
list_for_each_entry_safe(avcpu, avcpu_tmp,
+
&deactivated_blocked_list, list)
+ {
+
sched_entry_t * sched_entry = find_sched_entry(avcpu->vc);
+
if (sched_entry != NULL)
+
{
+
avcpu->active = 1;
+
avcpu->runtime = sched_entry->runtime;
+
avcpu->time_left = 0;
+
list_move(&avcpu->list, &blocked_list);
+
}
+ }
+
+ /*
+ * Signal a
new major frame to begin. The next major frame
+ * is set
up by the do_schedule callback function when it
+ * is next
invoked.
+ */
+ next_major_frame
= NOW();
+ }
+ return ret;
+}
+
+/*
+ * Xen scheduler callback function to initialize a virtual
CPU (VCPU)
+ *
+ * This function should return 0 if the VCPU is allowed to
run and
+ * nonzero if there is an error.
+ */
+static int arinc653_init_vcpu(struct vcpu * v)
+{
+ int ret = -1;
+
+ if (is_idle_vcpu(v))
+ {
+ /*
+ * The idle
VCPU is created by Xen to run when no domains
+ * are
runnable or require CPU time.
+ * It is
similar to an "idle task" or "halt loop" process
+ * in an
operating system.
+ * We do
not track any scheduler information for the idle VCPU.
+ */
+ v->sched_priv
= NULL;
+ ret = 0;
+ }
+ else
+ {
+ v->sched_priv
= xmalloc(arinc653_vcpu_t);
+ if (AVCPU(v) !=
NULL)
+ {
+ /*
+
* Initialize our ARINC653 scheduler-specific information
+
* for the VCPU.
+
* The VCPU starts out on the blocked list.
+
* When Xen is ready for the VCPU to run, it will call
+
* the vcpu_wake scheduler callback function and our
+
* scheduler will move the VCPU to the run_list.
+
*/
+
sched_entry_t * sched_entry = find_sched_entry(v);
+
AVCPU(v)->vc = v;
+
AVCPU(v)->last_activation_time = 0;
+
if (sched_entry != NULL)
+
{
+
/* the new VCPU is in the current schedule */
+
AVCPU(v)->active = 1;
+
AVCPU(v)->runtime = sched_entry->runtime;
+
AVCPU(v)->time_left = sched_entry->runtime;
+
list_add(&AVCPU(v)->list, &blocked_list);
+
}
+
else
+
{
+
/* the new VCPU is NOT in the current schedule */
+
AVCPU(v)->active = 0;
+
AVCPU(v)->runtime = 0;
+
AVCPU(v)->time_left = 0;
+
list_add(&AVCPU(v)->list, &deactivated_blocked_list);
+
}
+
ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Xen scheduler callback function to remove a VCPU
+ */
+static void arinc653_destroy_vcpu(struct vcpu * v)
+{
+ if (AVCPU(v) != NULL)
+ {
+ /* remove the
VCPU from whichever list it is on */
+
list_del(&AVCPU(v)->list);
+ /* free the arinc653_vcpu
structure */
+ xfree(AVCPU(v));
+ }
+}
+
+/*
+ * This function searches the run list to find the next
VCPU
+ * to run.
+ * If a VCPU other than domain 0 is runnable, it will be
returned.
+ * Otherwise, if domain 0 is runnable, it will be returned.
+ * Otherwise, the idle task will be returned.
+ */
+static struct vcpu * find_next_runnable_vcpu(void)
+{
+ arinc653_vcpu_t *
avcpu;
/* loop index variable */
+ struct vcpu * dom0_task = NULL;
+ struct vcpu * new_task = NULL;
+ int cpu = smp_processor_id();
+
+ BUG_ON(cpu !=
0); /* this
implementation only supports one CPU */
+
+ /* select a new task from the run_list
to run, choosing any runnable
+ * task other than domain 0 first
*/
+ list_for_each_entry(avcpu,
&run_list, list)
+ {
+ if
(avcpu->vc->domain->domain_id == 0)
+ {
+
dom0_task = avcpu->vc;
+ }
+ else if
(vcpu_runnable(avcpu->vc))
+ {
+
new_task = avcpu->vc;
+ }
+ }
+
+ if (new_task == NULL)
+ {
+ /* no non-dom0
runnable task was found */
+ if (dom0_task !=
NULL && vcpu_runnable(dom0_task))
+ {
+
/* if domain 0 has credit left and is runnable, run it */
+ new_task
= dom0_task;
+ }
+ else
+ {
+
/* otherwise just run the idle task */
+
new_task = IDLETASK(cpu);
+ }
+ }
+
+ return new_task;
+}
+
+/*
+ * Xen scheduler callback function to select a VCPU to run.
+ * This is the main scheduler routine.
+ */
+static struct task_slice arinc653_do_schedule(s_time_t t)
+{
+ arinc653_vcpu_t *
avcpu;
/* loop index variable */
+ struct task_slice
ret;
/* hold the chosen domain */
+ struct vcpu * new_task = NULL;
+
+ /* current_task holds a pointer to the
currently executing VCPU across
+ * do_schedule invocations for
credit accounting */
+ static struct vcpu * current_task =
NULL;
+
+ if (unlikely(next_major_frame ==
0)) /* first run of do_schedule */
+ {
+ next_major_frame
= t + arinc653_major_frame;
+ }
+ else if (t >=
next_major_frame)
/* entered a new major frame */
+ {
+ next_major_frame
= t + arinc653_major_frame;
+ /* move
everything that had expired last major frame to
+ * the run
list for this frame */
+
list_splice_init(&expired_list, &run_list);
+
list_for_each_entry(avcpu, &run_list, list)
+ {
+
/* restore domain credits for the new major frame */
+
avcpu->time_left = avcpu->runtime;
+
BUG_ON(avcpu->time_left <= 0);
+ }
+
list_for_each_entry(avcpu, &blocked_list, list)
+ {
+
/* restore domain credits for the new major frame */
+
avcpu->time_left = avcpu->runtime;
+
BUG_ON(avcpu->time_left <= 0);
+ }
+ }
+ else if (AVCPU(current_task) !=
NULL) /* not the idle task */
+ {
+ /*
+ * The
first time this function is called one of the previous if
+ *
statements will be true, and so current_task will get set
+ * to a
non-NULL value before this function is called again.
+ *
next_major_frame starts out as 0, so if it is not changed
+ * the
first if statement will be true.
+ * If
next_major_frame was changed from 0, it must have been
+ * changed
in the arinc653_sched_set() function, since that
+ * is the only
function other than this one that changes that
+ *
variable. In that case, it was set to the time at which
+ *
arinc653_sched_set() was called, and so when this function
+ * is
called the time will be greater than or equal to
+ *
next_major_frame, and so the second if statement will be true.
+ */
+
+ /* we did not
enter a new major frame, so decrease the
+ * credits
remaining for this domain for this frame */
+
AVCPU(current_task)->time_left -=
+
t - AVCPU(current_task)->last_activation_time;
+ if
(AVCPU(current_task)->time_left <= 0)
+ {
+
/* this domain has expended all of its time, so move it
+
* to the expired list */
+
AVCPU(current_task)->time_left = 0;
+
list_move(&AVCPU(current_task)->list, &expired_list);
+ }
+ }
+ else
+ {
+ /* only the idle
VCPU will get here, and we do not do any
+ *
"credit" accounting for it */
+ BUG_ON(!is_idle_vcpu(current_task));
+ }
+
+ new_task = find_next_runnable_vcpu();
+ BUG_ON(new_task == NULL);
+
+ /* if we are switching to a task that we
are tracking
+ * information for, set its last
activation time */
+ if (AVCPU(new_task) != NULL)
+ {
+
AVCPU(new_task)->last_activation_time = t;
+
BUG_ON(AVCPU(new_task)->time_left <= 0);
+ }
+
+ /* Check to make sure we did not miss a
major frame.
+ * This is a good test for robust
partitioning. */
+ BUG_ON(t >= next_major_frame);
+
+ ret.time = is_idle_vcpu(new_task)
+ ?
next_major_frame - t /* run idle task until next
major frame */
+ :
AVCPU(new_task)->time_left; /* run for entire slice this frame
*/
+ ret.task = new_task;
+ current_task = new_task;
+
+ BUG_ON(ret.time <= 0);
+
+ return ret;
+}
+
+/* Xen scheduler callback function to select a CPU for the
VCPU to run on */
+static int arinc653_pick_cpu(struct vcpu * v)
+{
+ /* this implementation only supports one
physical CPU */
+ return 0;
+}
+
+/*
+ * Xen scheduler callback function to wake up a VCPU
+ * Xen may call this scheduler callback function for VCPUs
that are
+ * not in the current ARINC653 run list. We keep track of
the fact
+ * that they have been "woken up" but we still do
not let them run.
+ *
+ * If the VCPU is not in the current schedule:
+ * Move it to the deactivated run list.
+ * Otherwise:
+ * If the VCPU still has credit left for this
major frame:
+ * Move the VCPU to the run list
+ * Otherwise:
+ * Move the VCPU to the expired
list
+ */
+static void arinc653_vcpu_wake(struct vcpu * vc)
+{
+ /* boolean flag to indicate first run */
+ static bool_t dont_raise_softirq = 0;
+
+ if (AVCPU(vc) != NULL) /* check
that this is a VCPU we are tracking */
+ {
+ if
(AVCPU(vc)->active)
+ {
+
/* the VCPU is in the current ARINC653 schedule */
+
if (AVCPU(vc)->time_left > 0)
+
{
+
/* the domain has credit remaining for this frame
+
* so put it on the run list */
+
list_move(&AVCPU(vc)->list, &run_list);
+
}
+
else
+
{
+
/* otherwise put it on the expired list for next frame */
+
list_move(&AVCPU(vc)->list, &expired_list);
+
}
+ }
+ else
+ {
+
/* VCPU is not allowed to run according to this schedule! */
+
list_move(&AVCPU(vc)->list, &deactivated_run_list);
+ }
+ }
+
+ /* the first time the vcpu_wake function
is called, we should raise
+ * a softirq to invoke the
do_scheduler callback */
+ if (!dont_raise_softirq)
+ {
+
cpu_raise_softirq(vc->processor, SCHEDULE_SOFTIRQ);
+ dont_raise_softirq
= 1;
+ }
+}
+
+/*
+ * Xen scheduler callback function to sleep a VCPU
+ * This function will remove the VCPU from the run list.
+ * If the VCPU is in the current schedule:
+ * Move it to the blocked list.
+ * Otherwise:
+ * Move it to the deactivated blocked list.
+ */
+static void arinc653_vcpu_sleep(struct vcpu * vc)
+{
+ if (AVCPU(vc) != NULL) /* check
that this is a VCPU we are tracking */
+ {
+ if
(AVCPU(vc)->active)
/* if in current schedule */
+ {
+
list_move(&AVCPU(vc)->list, &blocked_list);
+ }
+ else
+ {
+
list_move(&AVCPU(vc)->list, &deactivated_blocked_list);
+ }
+ }
+
+ /* if the VCPU being put to sleep is the
same one that is currently
+ * running, raise a softirq to
invoke the scheduler to switch domains */
+ if (per_cpu(schedule_data,
vc->processor).curr == vc)
+ {
+
cpu_raise_softirq(vc->processor, SCHEDULE_SOFTIRQ);
+ }
+}
+
+/*
+ * This structure defines our scheduler for Xen.
+ * The entries tell Xen where to find our
scheduler-specific
+ * callback functions.
+ * The symbol must be visible to the rest of Xen at link
time.
+ */
+struct scheduler sched_arinc653_def = {
+
.name = "ARINC
653 Scheduler",
+
.opt_name = "arinc653",
+
.sched_id = XEN_SCHEDULER_ARINC653,
+
+ .init_domain = NULL,
+ .destroy_domain = NULL,
+
+ .init_vcpu
= arinc653_init_vcpu,
+ .destroy_vcpu =
arinc653_destroy_vcpu,
+
+ .do_schedule =
arinc653_do_schedule,
+
.pick_cpu = arinc653_pick_cpu,
+ .dump_cpu_state = NULL,
+
.sleep =
arinc653_vcpu_sleep,
+
.wake =
arinc653_vcpu_wake,
+
.adjust = NULL,
+};
diff -rupN a/xen/common/schedule.c b/xen/common/schedule.c
--- a/xen/common/schedule.c 2009-08-06
09:57:27.000000000 -0400
+++ b/xen/common/schedule.c 2010-03-19
09:13:50.792881300 -0400
@@ -7,7 +7,8 @@
* File:
common/schedule.c
* Author: Rolf
Neugebauer & Keir Fraser
*
Updated for generic API by Mark Williamson
- *
+
*
ARINC653 scheduler added by DornerWorks
+ *
* Description: Generic CPU scheduling code
*
implements support functionality for the Xen scheduler API.
*
@@ -25,6 +26,7 @@
#include <xen/timer.h>
#include <xen/perfc.h>
#include <xen/sched-if.h>
+#include <xen/sched_arinc653.h>
#include <xen/softirq.h>
#include <xen/trace.h>
#include <xen/mm.h>
@@ -58,9 +60,11 @@ DEFINE_PER_CPU(struct schedule_data, sch
extern struct scheduler sched_sedf_def;
extern struct scheduler sched_credit_def;
+extern struct scheduler sched_arinc653_def;
static struct scheduler *schedulers[] = {
&sched_sedf_def,
&sched_credit_def,
+ &sched_arinc653_def,
NULL
};
@@ -657,6 +661,27 @@ ret_t do_sched_op(int cmd,
XEN_GUEST_HAN
break;
}
+ case SCHEDOP_arinc653_sched_set:
+ {
+
sched_arinc653_sched_set_t sched_set;
+
+ if
(!IS_PRIV(current->domain))
+ {
+
ret = -EPERM;
+
break;
+ }
+
+ if
(copy_from_guest(&sched_set, arg, 1) != 0)
+ {
+
ret = -EFAULT;
+
break;
+ }
+
+ ret = arinc653_sched_set(&sched_set);
+
+ break;
+ }
+
default:
ret =
-ENOSYS;
}
diff -rupN a/xen/include/public/domctl.h
b/xen/include/public/domctl.h
--- a/xen/include/public/domctl.h 2009-08-06
09:57:28.000000000 -0400
+++ b/xen/include/public/domctl.h 2010-03-19
09:15:27.229190500 -0400
@@ -23,6 +23,8 @@
*
* Copyright (c) 2002-2003, B Dragovic
* Copyright (c) 2002-2006, K Fraser
+ *
+ * ARINC653 Scheduler type added by DornerWorks
<DornerWorks.com>.
*/
#ifndef __XEN_PUBLIC_DOMCTL_H__
@@ -297,6 +299,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_max_v
/* Scheduler types. */
#define XEN_SCHEDULER_SEDF 4
#define XEN_SCHEDULER_CREDIT 5
+#define XEN_SCHEDULER_ARINC653 6
/* Set or get info? */
#define XEN_DOMCTL_SCHEDOP_putinfo 0
#define XEN_DOMCTL_SCHEDOP_getinfo 1
diff -rupN a/xen/include/public/sched.h
b/xen/include/public/sched.h
--- a/xen/include/public/sched.h 2009-08-06
09:57:28.000000000 -0400
+++ b/xen/include/public/sched.h 2010-03-19
09:15:17.682315500 -0400
@@ -22,6 +22,8 @@
* DEALINGS IN THE SOFTWARE.
*
* Copyright (c) 2005, Keir Fraser <keir@xxxxxxxxxxxxx>
+ *
+ * ARINC653 Schedule set added by DornerWorks
<DornerWorks.com>.
*/
#ifndef __XEN_PUBLIC_SCHED_H__
@@ -108,6 +110,40 @@ DEFINE_XEN_GUEST_HANDLE(sched_remote_shu
#define SHUTDOWN_suspend 2 /*
Clean up, save suspend info,
kill. */
#define SHUTDOWN_crash
3 /* Tell controller we've
crashed.
*/
+/*
+ * Set the ARINC653 schedule. The new schedule takes effect
immediately.
+ * The scheduler does not wait for the current major frame
to expire
+ * before switching to the new schedule.
+ */
+#define
SCHEDOP_arinc653_sched_set 5
+#define ARINC653_MAX_DOMAINS_PER_SCHEDULE 64
+/*
+ * This structure is used to pass a new ARINC653 schedule
from a
+ * privileged domain (ie dom0) to Xen.
+ */
+struct sched_arinc653_sched_set {
+ /* major_frame holds the time for the
new schedule's major frame
+ * in nanoseconds. */
+ int64_t
major_frame;
+ /* num_sched_entries holds how many of
the entries in the
+ * sched_entries[] array are valid.
*/
+ uint8_t
num_sched_entries;
+ /* The sched_entries array holds the
actual schedule entries. */
+ struct {
+ /* dom_handle
must match a domain's UUID */
+
xen_domain_handle_t dom_handle;
+ /* If a domain
has multiple VCPUs, vcpu_id specifies which one
+ * this
schedule entry applies to. It should be set to 0 if
+ * there is
only one VCPU for the domain. */
+
int
vcpu_id;
+ /* runtime
specifies the amount of time that should be allocated
+ * to this
VCPU per major frame. It is specified in nanoseconds */
+
int64_t runtime;
+ }
sched_entries[ARINC653_MAX_DOMAINS_PER_SCHEDULE];
+};
+typedef struct sched_arinc653_sched_set
sched_arinc653_sched_set_t;
+DEFINE_XEN_GUEST_HANDLE(sched_arinc653_sched_set_t);
+
#endif /* __XEN_PUBLIC_SCHED_H__ */
/*
diff -rupN a/xen/include/xen/sched_arinc653.h
b/xen/include/xen/sched_arinc653.h
--- a/xen/include/xen/sched_arinc653.h 1969-12-31
19:00:00.000000000 -0500
+++ b/xen/include/xen/sched_arinc653.h 2010-03-19
08:58:10.346112800 -0400
@@ -0,0 +1,44 @@
+/*
+ * File: sched_arinc653.h
+ * Copyright (c) 2009, DornerWorks, Ltd.
<DornerWorks.com>
+ *
+ * Description:
+ * This file prototypes global ARINC653
scheduler functions. Scheduler
+ * callback functions are static to the
sched_arinc653 module and do not
+ * need global prototypes here.
+ *
+ * This program is free software; you can redistribute it
and/or modify it
+ * under the terms of the GNU General Public License as
published by the Free
+ * software Foundation; either version 2 of the License, or
(at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be
useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for
+ * more details.
+ */
+
+#ifndef __SCHED_ARINC653_H__
+#define __SCHED_ARINC653_H__
+
+#include <public/sched.h>
+
+/*
+ * arinc653_sched_set() is used to put a new schedule in
place for the
+ * ARINC653 scheduler.
+ * Whereas the credit scheduler allows configuration by
changing weights
+ * and caps on a per-domain basis, the ARINC653 scheduler
allows configuring
+ * a complete list of domains that are allowed to run, and
the duration they
+ * are allowed to run.
+ * This is a global call instead of a domain-specific
setting, so a prototype
+ * is placed here for the rest of Xen to access. Currently
it is only
+ * called by the do_sched_op() function in
xen/common/schedule.c
+ * in response to a hypercall.
+ *
+ * Return values:
+ *
0 Success
+ * -EINVAL Invalid Parameter
+ */
+int arinc653_sched_set(sched_arinc653_sched_set_t *
schedule);
+
+#endif /* __SCHED_ARINC653_H__ */