/****************************************************************************** * 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 /*verbosity settings*/ #define SEDFLEVEL 0 #define PRINT(_f, _a...) \ do { \ if ( (_f) <= SEDFLEVEL ) \ printk(_a ); \ } while ( 0 ) #ifndef NDEBUG #define SEDF_STATS #define CHECK(_p) \ do { \ if ( !(_p) ) \ printk("Check '%s' failed, line %d, file %s\n", \ #_p , __LINE__, __FILE__); \ } while ( 0 ) #else #define CHECK(_p) ((void)0) #endif #define EXTRA_NONE (0) #define EXTRA_AWARE (1) #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 SEDF_ASLEEP (16) #define EXTRA_QUANTUM (MICROSECS(500)) #define WEIGHT_PERIOD (MILLISECS(100)) #define WEIGHT_SAFETY (MILLISECS(5)) #define PERIOD_MAX MILLISECS(10000) /* 10s */ #define PERIOD_MIN (MICROSECS(10)) /* 10us */ #define SLICE_MIN (MICROSECS(5)) /* 5us */ #define IMPLY(a, b) (!(a) || (b)) #define EQ(a, b) ((!!(a)) == (!!(b))) struct sedf_dom_info { struct domain *domain; }; struct sedf_vcpu_info { struct vcpu *vcpu; 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*/ /*Advaced Parameters*/ /*Latency Scaling*/ s_time_t period_orig; s_time_t slice_orig; s_time_t latency; /*status of domain*/ int status; /*weights for "Scheduling for beginners/ lazy/ etc." ;)*/ short weight; short extraweight; /*Bookkeeping*/ s_time_t deadl_abs; s_time_t sched_start_abs; s_time_t cputime; /* times the domain un-/blocked */ s_time_t block_abs; s_time_t unblock_abs; /*scores for {util, block penalty}-weighted extratime distribution*/ int score[2]; 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; 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 { struct list_head runnableq; struct list_head waitq; struct list_head extraq[2]; s_time_t current_slice_expires; }; #define EDOM_INFO(d) ((struct sedf_vcpu_info *)((d)->sched_priv)) #define CPU_INFO(cpu) \ ((struct sedf_cpu_info *)per_cpu(schedule_data, cpu).sched_priv) #define LIST(d) (&EDOM_INFO(d)->list) #define EXTRALIST(d,i) (&(EDOM_INFO(d)->extralist[i])) #define RUNQ(cpu) (&CPU_INFO(cpu)->runnableq) #define WAITQ(cpu) (&CPU_INFO(cpu)->waitq) #define EXTRAQ(cpu,i) (&(CPU_INFO(cpu)->extraq[i])) #define IDLETASK(cpu) ((struct vcpu *)per_cpu(schedule_data, cpu).idle) #define PERIOD_BEGIN(inf) ((inf)->deadl_abs - (inf)->period) #define MIN(x,y) (((x)<(y))?(x):(y)) #define DIV_UP(x,y) (((x) + (y) - 1) / y) #define extra_runs(inf) ((inf->status) & 6) #define extra_get_cur_q(inf) (((inf->status & 6) >> 1)-1) #define sedf_runnable(edom) (!(EDOM_INFO(edom)->status & SEDF_ASLEEP)) static void sedf_dump_cpu_state(int i); static inline int extraq_on(struct vcpu *d, int i) { return ((EXTRALIST(d,i)->next != NULL) && (EXTRALIST(d,i)->next != EXTRALIST(d,i))); } static inline void extraq_add_head(struct vcpu *d, int i) { list_add(EXTRALIST(d,i), EXTRAQ(d->processor,i)); ASSERT(extraq_on(d, i)); } static inline void extraq_add_tail(struct vcpu *d, int i) { list_add_tail(EXTRALIST(d,i), EXTRAQ(d->processor,i)); ASSERT(extraq_on(d, i)); } static inline void extraq_del(struct vcpu *d, int i) { struct list_head *list = EXTRALIST(d,i); ASSERT(extraq_on(d,i)); PRINT(3, "Removing domain %i.%i from L%i extraq\n", d->domain->domain_id, d->vcpu_id, i); list_del(list); list->next = NULL; ASSERT(!extraq_on(d, i)); } /* 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 vcpu *d, int i, int sub) { struct list_head *cur; struct sedf_vcpu_info *curinf; ASSERT(!extraq_on(d,i)); PRINT(3, "Adding domain %i.%i (score= %i, short_pen= %"PRIi64")" " to L%i extraq\n", d->domain->domain_id, d->vcpu_id, EDOM_INFO(d)->score[i], EDOM_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_vcpu_info,extralist[i]); curinf->score[i] -= sub; if ( EDOM_INFO(d)->score[i] < curinf->score[i] ) break; PRINT(4,"\tbehind domain %i.%i (score= %i)\n", curinf->vcpu->domain->domain_id, curinf->vcpu->vcpu_id, curinf->score[i]); } /* cur now contains the element, before which we'll enqueue. */ PRINT(3, "\tlist_add to %p\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_vcpu_info, extralist[i]); curinf->score[i] -= sub; PRINT(4, "\tupdating domain %i.%i (score= %u)\n", curinf->vcpu->domain->domain_id, curinf->vcpu->vcpu_id, curinf->score[i]); } } ASSERT(extraq_on(d,i)); } static inline void extraq_check(struct vcpu *d) { if ( extraq_on(d, EXTRA_UTIL_Q) ) { PRINT(2,"Dom %i.%i is on L1 extraQ\n", d->domain->domain_id, d->vcpu_id); if ( !(EDOM_INFO(d)->status & EXTRA_AWARE) && !extra_runs(EDOM_INFO(d)) ) { extraq_del(d, EXTRA_UTIL_Q); PRINT(2,"Removed dom %i.%i from L1 extraQ\n", d->domain->domain_id, d->vcpu_id); } } else { PRINT(2, "Dom %i.%i is NOT on L1 extraQ\n", d->domain->domain_id, d->vcpu_id); if ( (EDOM_INFO(d)->status & EXTRA_AWARE) && sedf_runnable(d) ) { extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); PRINT(2,"Added dom %i.%i to L1 extraQ\n", d->domain->domain_id, d->vcpu_id); } } } static inline void extraq_check_add_unblocked(struct vcpu *d, int priority) { struct sedf_vcpu_info *inf = EDOM_INFO(d); if ( inf->status & EXTRA_AWARE ) /* Put on the weighted extraq without updating any scores. */ extraq_add_sort_update(d, EXTRA_UTIL_Q, 0); } static inline int __task_on_queue(struct vcpu *d) { return (((LIST(d))->next != NULL) && (LIST(d)->next != LIST(d))); } static inline void __del_from_queue(struct vcpu *d) { struct list_head *list = LIST(d); ASSERT(__task_on_queue(d)); PRINT(3,"Removing domain %i.%i (bop= %"PRIu64") from runq/waitq\n", d->domain->domain_id, d->vcpu_id, PERIOD_BEGIN(EDOM_INFO(d))); list_del(list); list->next = NULL; ASSERT(!__task_on_queue(d)); } typedef int(*list_comparer)(struct list_head* el1, struct list_head* el2); static inline void list_insert_sort( struct list_head *list, struct list_head *element, list_comparer comp) { struct list_head *cur; /* Iterate through all elements to find our "hole". */ list_for_each( cur, list ) if ( comp(element, cur) < 0 ) break; /* cur now contains the element, before which we'll enqueue. */ PRINT(3,"\tlist_add to %p\n",cur->prev); list_add(element, cur->prev); } #define DOMAIN_COMPARER(name, field, comp1, comp2) \ int name##_comp(struct list_head* el1, struct list_head* el2) \ { \ struct sedf_vcpu_info *d1, *d2; \ d1 = list_entry(el1,struct sedf_vcpu_info, field); \ d2 = list_entry(el2,struct sedf_vcpu_info, field); \ if ( (comp1) == (comp2) ) \ return 0; \ if ( (comp1) < (comp2) ) \ return -1; \ else \ return 1; \ } /* 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 */ DOMAIN_COMPARER(waitq, list, PERIOD_BEGIN(d1), PERIOD_BEGIN(d2)); static inline void __add_to_waitqueue_sort(struct vcpu *v) { ASSERT(!__task_on_queue(v)); PRINT(3,"Adding domain %i.%i (bop= %"PRIu64") to waitq\n", v->domain->domain_id, v->vcpu_id, PERIOD_BEGIN(EDOM_INFO(v))); list_insert_sort(WAITQ(v->processor), LIST(v), waitq_comp); ASSERT(__task_on_queue(v)); } /* 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 o
/*
    ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

#include "ch.h"
#include "hal.h"
#include "ch_test.h"
#include "shell.h"
#include "chprintf.h"

#define SHELL_WA_SIZE       THD_WORKING_AREA_SIZE(4096)
#define CONSOLE_WA_SIZE     THD_WORKING_AREA_SIZE(4096)
#define TEST_WA_SIZE        THD_WORKING_AREA_SIZE(4096)

#define cputs(msg) chMsgSend(cdtp, (msg_t)msg)

static thread_t *cdtp;
static thread_t *shelltp1;
static thread_t *shelltp2;

static const ShellCommand commands[] = {
  {NULL, NULL}
};

static const ShellConfig shell_cfg1 = {
  (BaseSequentialStream *)&SD1,
  commands
};

static const ShellConfig shell_cfg2 = {
  (BaseSequentialStream *)&SD2,
  commands
};

/*
 * Console print server done using synchronous messages. This makes the access
 * to the C printf() thread safe and the print operation atomic among threads.
 * In this example the message is the zero terminated string itself.
 */
static THD_FUNCTION(console_thread, arg) {

  (void)arg;
  while (!chThdShouldTerminateX()) {
    thread_t *tp = chMsgWait();
    puts((char *)chMsgGet(tp));
    fflush(stdout);
    chMsgRelease(tp, MSG_OK);
  }
}

/**
 * @brief Shell termination handler.
 *
 * @param[in] id event id.
 */
static void termination_handler(eventid_t id) {

  (void)id;
  if (shelltp1 && chThdTerminatedX(shelltp1)) {
    chThdWait(shelltp1);
    shelltp1 = NULL;
    chThdSleepMilliseconds(10);
    cputs("Init: shell on SD1 terminated");
    chSysLock();
    oqResetI(&SD1.oqueue);
    chSchRescheduleS();
    chSysUnlock();
  }
  if (shelltp2 && chThdTerminatedX(shelltp2)) {
    chThdWait(shelltp2);
    shelltp2 = NULL;
    chThdSleepMilliseconds(10);
    cputs("Init: shell on SD2 terminated");
    chSysLock();
    oqResetI(&SD2.oqueue);
    chSchRescheduleS();
    chSysUnlock();
  }
}

static event_listener_t sd1fel, sd2fel;

/**
 * @brief SD1 status change handler.
 *
 * @param[in] id event id.
 */
static void sd1_handler(eventid_t id) {
  eventflags_t flags;

  (void)id;
  flags = chEvtGetAndClearFlags(&sd1fel);
  if ((flags & CHN_CONNECTED) && (shelltp1 == NULL)) {
    cputs("Init: connection on SD1");
    shelltp1 = chThdCreateFromHeap(NULL, SHELL_WA_SIZE,
                                   "shell1", NORMALPRIO + 10,
                                   shellThread, (void *)&shell_cfg1);
  }
  if (flags & CHN_DISCONNECTED) {
    cputs("Init: disconnection on SD1");
    chSysLock();
    iqResetI(&SD1.iqueue);
    chSchRescheduleS();
    chSysUnlock();
  }
}

/**
 * @brief SD2 status change handler.
 *
 * @param[in] id event id.
 */
static void sd2_handler(eventid_t id) {
  eventflags_t flags;

  (void)id;
  flags = chEvtGetAndClearFlags(&sd2fel);
  if ((flags & CHN_CONNECTED) && (shelltp2 == NULL)) {
    cputs("Init: connection on SD2");
    shelltp2 = chThdCreateFromHeap(NULL, SHELL_WA_SIZE,
                                   "shell2", NORMALPRIO + 10,
                                   shellThread, (void *)&shell_cfg2);
  }
  if (flags & CHN_DISCONNECTED) {
    cputs("Init: disconnection on SD2");
    chSysLock();
    iqResetI(&SD2.iqueue);
    chSchRescheduleS();
    chSysUnlock();
  }
}

static evhandler_t fhandlers[] = {
  termination_handler,
  sd1_handler,
  sd2_handler
};

/*------------------------------------------------------------------------*
 * Simulator main.                                                        *
 *------------------------------------------------------------------------*/
int main(void) {
  event_listener_t tel;

  /*
   * System initializations.
   * - HAL initialization, this also initializes the configured device drivers
   *   and performs the board-specific initializations.
   * - Kernel initialization, the main() function becomes a thread and the
   *   RTOS is active.
   */
  halInit();
  chSysInit();

  /*
   * Serial ports (simulated) initialization.
   */
  sdStart(&SD1, NULL);
  sdStart(&SD2, NULL);

  /*
   * Shell manager initialization.
   */
  shellInit();
  chEvtRegister(&shell_terminated, &tel, 0);

  /*
   * Console thread started.
   */
  cdtp = chThdCreateFromHeap(NULL, CONSOLE_WA_SIZE, "console",
                             NORMALPRIO + 1, console_thread, NULL);

  /*
   * Initializing connection/disconnection events.
   */
  cputs("Shell service started on SD1, SD2");
  cputs("  - Listening for connections on SD1");
  chEvtRegister(chnGetEventSource(&SD1), &sd1fel, 1);
  cputs("  - Listening for connections on SD2");
  chEvtRegister(chnGetEventSource(&SD2), &sd2fel, 2);

  /*
   * Events servicing loop.
   */
  while (!chThdShouldTerminateX())
    chEvtDispatch(fhandlers, chEvtWaitOne(ALL_EVENTS));

  /*
   * Clean simulator exit.
   */
  chEvtUnregister(chnGetEventSource(&SD1), &sd1fel);
  chEvtUnregister(chnGetEventSource(&SD2), &sd2fel);
  return 0;
}
\n",d->domain->domain_id, d->vcpu_id); if ( unlikely(is_idle_vcpu(d)) ) return; if ( unlikely(__task_on_queue(d)) ) { PRINT(3,"\tdomain %i.%i is already in some queue\n", d->domain->domain_id, d->vcpu_id); return; } ASSERT(!sedf_runnable(d)); inf->status &= ~SEDF_ASLEEP; ASSERT(!extraq_on(d, EXTRA_UTIL_Q)); ASSERT(!extraq_on(d, EXTRA_PEN_Q)); if ( unlikely(inf->deadl_abs == 0) ) { /*initial setup of the deadline*/ inf->deadl_abs = now + inf->slice; } PRINT(3, "waking up domain %i.%i (deadl= %"PRIu64" period= %"PRIu64 "now= %"PRIu64")\n", d->domain->domain_id, d->vcpu_id, inf->deadl_abs, inf->period, now); #ifdef SEDF_STATS inf->block_tot++; #endif if ( unlikely(now < PERIOD_BEGIN(inf)) ) { PRINT(4,"extratime unblock\n"); /* unblocking in extra-time! */ if ( inf->status & 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); } extraq_check_add_unblocked(d, 0); } else { if ( now < inf->deadl_abs ) { PRINT(4,"short unblocking\n"); /*short blocking*/ #ifdef SEDF_STATS inf->short_block_tot++; #endif unblock_short_extra_support(inf, now); extraq_check_add_unblocked(d, 1); } else { PRINT(4,"long unblocking\n"); /*long unblocking*/ #ifdef SEDF_STATS inf->long_block_tot++; #endif unblock_long_cons_b(inf, now); extraq_check_add_unblocked(d, 1); } } PRINT(3, "woke up domain %i.%i (deadl= %"PRIu64" period= %"PRIu64 "now= %"PRIu64")\n", d->domain->domain_id, d->vcpu_id, inf->deadl_abs, inf->period, now); if ( PERIOD_BEGIN(inf) > now ) { __add_to_waitqueue_sort(d); PRINT(3,"added to waitq\n"); } else { __add_to_runqueue_sort(d); PRINT(3,"added to runq\n"); } #ifdef SEDF_STATS /*do some statistics here...*/ if ( inf->block_abs != 0 ) { inf->block_time_tot += now - inf->block_abs; inf->penalty_time_tot += PERIOD_BEGIN(inf) + inf->cputime - inf->block_abs; } #endif /*sanity check: make sure each extra-aware domain IS on the util-q!*/ ASSERT(IMPLY(inf->status & EXTRA_AWARE, extraq_on(d, EXTRA_UTIL_Q))); ASSERT(__task_on_queue(d)); /*check whether the awakened task needs to invoke the do_schedule routine. Try to avoid unnecessary runs but: Save approximation: Always switch to scheduler!*/ ASSERT(d->processor >= 0); ASSERT(d->processor < NR_CPUS); ASSERT(per_cpu(schedule_data, d->processor).curr); if ( should_switch(per_cpu(schedule_data, d->processor).curr, d, now) ) cpu_raise_softirq(d->processor, SCHEDULE_SOFTIRQ); } /* Print a lot of useful information about a domains in the system */ static void sedf_dump_domain(struct vcpu *d) { printk("%i.%i has=%c ", d->domain->domain_id, d->vcpu_id, test_bit(_VCPUF_running, &d->vcpu_flags) ? 'T':'F'); printk("p=%"PRIu64" sl=%"PRIu64" ddl=%"PRIu64" w=%hu" " sc=%i xtr(%s)=%"PRIu64" ew=%hu", EDOM_INFO(d)->period, EDOM_INFO(d)->slice, EDOM_INFO(d)->deadl_abs, EDOM_INFO(d)->weight, EDOM_INFO(d)->score[EXTRA_UTIL_Q], (EDOM_INFO(d)->status & EXTRA_AWARE) ? "yes" : "no", EDOM_INFO(d)->extra_time_tot, EDOM_INFO(d)->extraweight); #ifdef SEDF_STATS if ( EDOM_INFO(d)->block_time_tot != 0 ) printk(" pen=%"PRIu64"%%", (EDOM_INFO(d)->penalty_time_tot * 100) / EDOM_INFO(d)->block_time_tot); if ( EDOM_INFO(d)->block_tot != 0 ) printk("\n blks=%u sh=%u (%u%%) (shc=%u (%u%%) shex=%i "\ "shexsl=%i) l=%u (%u%%) avg: b=%"PRIu64" p=%"PRIu64"", EDOM_INFO(d)->block_tot, EDOM_INFO(d)->short_block_tot, (EDOM_INFO(d)->short_block_tot * 100) / EDOM_INFO(d)->block_tot, EDOM_INFO(d)->short_cont, (EDOM_INFO(d)->short_cont * 100) / EDOM_INFO(d)->block_tot, EDOM_INFO(d)->pen_extra_blocks, EDOM_INFO(d)->pen_extra_slices, EDOM_INFO(d)->long_block_tot, (EDOM_INFO(d)->long_block_tot * 100) / EDOM_INFO(d)->block_tot, (EDOM_INFO(d)->block_time_tot) / EDOM_INFO(d)->block_tot, (EDOM_INFO(d)->penalty_time_tot) / EDOM_INFO(d)->block_tot); #endif printk("\n"); } /* dumps all domains on hte specified cpu */ static void sedf_dump_cpu_state(int i) { struct list_head *list, *queue, *tmp; struct sedf_vcpu_info *d_inf; struct domain *d; struct vcpu *ed; int loop = 0; printk("now=%"PRIu64"\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_vcpu_info, list); sedf_dump_domain(d_inf->vcpu); } 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_vcpu_info, list); sedf_dump_domain(d_inf->vcpu); } 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_vcpu_info, extralist[EXTRA_PEN_Q]); printk("%3d: ",loop++); sedf_dump_domain(d_inf->vcpu); } 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_vcpu_info, extralist[EXTRA_UTIL_Q]); printk("%3d: ",loop++); sedf_dump_domain(d_inf->vcpu); } loop = 0; printk("\nnot on Q\n"); for_each_domain ( d ) { for_each_vcpu(d, ed) { if ( !__task_on_queue(ed) && (ed->processor == i) ) { printk("%3d: ",loop++); sedf_dump_domain(ed); } } } } /* Adjusts periods and slices of the domains accordingly to their weights. */ static int sedf_adjust_weights(struct xen_domctl_scheduler_op *cmd) { struct vcpu *p; struct domain *d; int sumw[NR_CPUS] = { 0 }; s_time_t sumt[NR_CPUS] = { 0 }; /* Sum across all weights. */ for_each_domain( d ) { for_each_vcpu( d, p ) { if ( EDOM_INFO(p)->weight ) { sumw[p->processor] += EDOM_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*/ /*check for overflows*/ ASSERT((WEIGHT_PERIOD < ULONG_MAX) && (EDOM_INFO(p)->slice_orig < ULONG_MAX)); sumt[p->processor] += (WEIGHT_PERIOD * EDOM_INFO(p)->slice_orig) / EDOM_INFO(p)->period_orig; } } } /* Adjust all slices (and periods) to the new weight. */ for_each_domain( d ) { for_each_vcpu ( d, p ) { if ( EDOM_INFO(p)->weight ) { EDOM_INFO(p)->period_orig = EDOM_INFO(p)->period = WEIGHT_PERIOD; EDOM_INFO(p)->slice_orig = EDOM_INFO(p)->slice = (EDOM_INFO(p)->weight * (WEIGHT_PERIOD - WEIGHT_SAFETY - sumt[p->processor])) / sumw[p->processor]; } } } return 0; } /* set or fetch domain scheduling parameters */ static int sedf_adjust(struct domain *p, struct xen_domctl_scheduler_op *op) { struct vcpu *v; PRINT(2,"sedf_adjust was called, domain-id %i new period %"PRIu64" " "new slice %"PRIu64"\nlatency %"PRIu64" extra:%s\n", p->domain_id, op->u.sedf.period, op->u.sedf.slice, op->u.sedf.latency, (op->u.sedf.extratime)?"yes":"no"); if ( op->cmd == XEN_DOMCTL_SCHEDOP_putinfo ) { /* Check for sane parameters. */ if ( !op->u.sedf.period && !op->u.sedf.weight ) return -EINVAL; if ( op->u.sedf.weight ) { if ( (op->u.sedf.extratime & EXTRA_AWARE) && (!op->u.sedf.period) ) { /* Weight-driven domains with extratime only. */ for_each_vcpu ( p, v ) { EDOM_INFO(v)->extraweight = op->u.sedf.weight; EDOM_INFO(v)->weight = 0; EDOM_INFO(v)->slice = 0; EDOM_INFO(v)->period = WEIGHT_PERIOD; } } else { /* Weight-driven domains with real-time execution. */ for_each_vcpu ( p, v ) EDOM_INFO(v)->weight = op->u.sedf.weight; } } else { /* Time-driven domains. */ for_each_vcpu ( p, v ) { /* * Sanity checking: note that disabling extra weight requires * that we set a non-zero slice. */ if ( (op->u.sedf.period > PERIOD_MAX) || (op->u.sedf.period < PERIOD_MIN) || (op->u.sedf.slice > op->u.sedf.period) || (op->u.sedf.slice < SLICE_MIN) ) return -EINVAL; EDOM_INFO(v)->weight = 0; EDOM_INFO(v)->extraweight = 0; EDOM_INFO(v)->period_orig = EDOM_INFO(v)->period = op->u.sedf.period; EDOM_INFO(v)->slice_orig = EDOM_INFO(v)->slice = op->u.sedf.slice; } } if ( sedf_adjust_weights(op) ) return -EINVAL; for_each_vcpu ( p, v ) { EDOM_INFO(v)->status = (EDOM_INFO(v)->status & ~EXTRA_AWARE) | (op->u.sedf.extratime & EXTRA_AWARE); EDOM_INFO(v)->latency = op->u.sedf.latency; extraq_check(v); } } else if ( op->cmd == XEN_DOMCTL_SCHEDOP_getinfo ) { if ( p->vcpu[0] == NULL ) return -EINVAL; op->u.sedf.period = EDOM_INFO(p->vcpu[0])->period; op->u.sedf.slice = EDOM_INFO(p->vcpu[0])->slice; op->u.sedf.extratime = EDOM_INFO(p->vcpu[0])->status & EXTRA_AWARE; op->u.sedf.latency = EDOM_INFO(p->vcpu[0])->latency; op->u.sedf.weight = EDOM_INFO(p->vcpu[0])->weight; } PRINT(2,"sedf_adjust_finished\n"); return 0; } struct scheduler sched_sedf_def = { .name = "Simple EDF Scheduler", .opt_name = "sedf", .sched_id = XEN_SCHEDULER_SEDF, .init_domain = sedf_init_domain, .destroy_domain = sedf_destroy_domain, .init_vcpu = sedf_init_vcpu, .destroy_vcpu = sedf_destroy_vcpu, .do_schedule = sedf_do_schedule, .pick_cpu = sedf_pick_cpu, .dump_cpu_state = sedf_dump_cpu_state, .sleep = sedf_sleep, .wake = sedf_wake, .adjust = sedf_adjust, }; /* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */