What is ARINC653? Can you give a summary of what it is, why it's
important, and what this patch does?
Thanks,
-George
On Fri, Mar 19, 2010 at 4:23 PM, Anthony Boorsma
<Anthony.Boorsma@xxxxxxxxxxxxxxx> wrote:
> 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__ */
>
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@xxxxxxxxxxxxxxxxxxx
> http://lists.xensource.com/xen-devel
>
>
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|