/** * This function updates the pointer to the Xen VCPU structure for each entry * in the ARINC 653 schedule. * * @param ops Pointer to this instance of the scheduler structure * @return <None> */ static void update_schedule_vcpus(const struct scheduler *ops) { unsigned int i, n_entries = SCHED_PRIV(ops)->num_schedule_entries; for ( i = 0; i < n_entries; i++ ) SCHED_PRIV(ops)->schedule[i].vc = find_vcpu(ops, SCHED_PRIV(ops)->schedule[i].dom_handle, SCHED_PRIV(ops)->schedule[i].vcpu_id); }
/** * This function allocates scheduler-specific data for a VCPU * * @param ops Pointer to this instance of the scheduler structure * * @return Pointer to the allocated data */ static void * a653sched_alloc_vdata(const struct scheduler *ops, struct vcpu *vc, void *dd) { a653sched_priv_t *sched_priv = SCHED_PRIV(ops); arinc653_vcpu_t *svc; unsigned int entry; unsigned long flags; /* * Allocate memory for the ARINC 653-specific scheduler data information * associated with the given VCPU (vc). */ svc = xmalloc(arinc653_vcpu_t); if ( svc == NULL ) return NULL; spin_lock_irqsave(&sched_priv->lock, flags); /* * Add every one of dom0's vcpus to the schedule, as long as there are * slots available. */ if ( vc->domain->domain_id == 0 ) { entry = sched_priv->num_schedule_entries; if ( entry < ARINC653_MAX_DOMAINS_PER_SCHEDULE ) { sched_priv->schedule[entry].dom_handle[0] = '\0'; sched_priv->schedule[entry].vcpu_id = vc->vcpu_id; sched_priv->schedule[entry].runtime = DEFAULT_TIMESLICE; sched_priv->schedule[entry].vc = vc; sched_priv->major_frame += DEFAULT_TIMESLICE; ++sched_priv->num_schedule_entries; } } /* * Initialize our ARINC 653 scheduler-specific information for the VCPU. * The VCPU starts "asleep." When Xen is ready for the VCPU to run, it * will call the vcpu_wake scheduler callback function and our scheduler * will mark the VCPU awake. */ svc->vc = vc; svc->awake = 0; if ( !is_idle_vcpu(vc) ) list_add(&svc->list, &SCHED_PRIV(ops)->vcpu_list); update_schedule_vcpus(ops); spin_unlock_irqrestore(&sched_priv->lock, flags); return svc; }
/** * This function is called by the adjust_global scheduler hook to read the * current ARINC 653 schedule * * @param ops Pointer to this instance of the scheduler structure * @return <ul> * <li> 0 = success * <li> !0 = error * </ul> */ static int arinc653_sched_get( const struct scheduler *ops, struct xen_sysctl_arinc653_schedule *schedule) { a653sched_priv_t *sched_priv = SCHED_PRIV(ops); unsigned int i; unsigned long flags; spin_lock_irqsave(&sched_priv->lock, flags); schedule->num_sched_entries = sched_priv->num_schedule_entries; schedule->major_frame = sched_priv->major_frame; for ( i = 0; i < sched_priv->num_schedule_entries; i++ ) { memcpy(schedule->sched_entries[i].dom_handle, sched_priv->schedule[i].dom_handle, sizeof(sched_priv->schedule[i].dom_handle)); schedule->sched_entries[i].vcpu_id = sched_priv->schedule[i].vcpu_id; schedule->sched_entries[i].runtime = sched_priv->schedule[i].runtime; } spin_unlock_irqrestore(&sched_priv->lock, flags); return 0; }
/** * This function searches the vcpu list to find a VCPU that matches * the domain handle and VCPU ID specified. * * @param ops Pointer to this instance of the scheduler structure * @param handle Pointer to handler * @param vcpu_id VCPU ID * * @return <ul> * <li> Pointer to the matching VCPU if one is found * <li> NULL otherwise * </ul> */ static struct vcpu *find_vcpu( const struct scheduler *ops, xen_domain_handle_t handle, int vcpu_id) { arinc653_vcpu_t *avcpu; /* loop through the vcpu_list looking for the specified VCPU */ list_for_each_entry ( avcpu, &SCHED_PRIV(ops)->vcpu_list, list ) if ( (dom_handle_cmp(avcpu->vc->domain->handle, handle) == 0) && (vcpu_id == avcpu->vc->vcpu_id) ) return avcpu->vc; return NULL; }
/** * Xen scheduler callback function to select a VCPU to run. * This is the main scheduler routine. * * @param ops Pointer to this instance of the scheduler structure * @param now Current time * * @return Address of the VCPU structure scheduled to be run next * Amount of time to execute the returned VCPU * Flag for whether the VCPU was migrated */ static struct task_slice a653sched_do_schedule( const struct scheduler *ops, s_time_t now, bool_t tasklet_work_scheduled) { struct task_slice ret; /* hold the chosen domain */ struct vcpu * new_task = NULL; static unsigned int sched_index = 0; static s_time_t next_switch_time; a653sched_priv_t *sched_priv = SCHED_PRIV(ops); const unsigned int cpu = smp_processor_id(); unsigned long flags; spin_lock_irqsave(&sched_priv->lock, flags); if ( sched_priv->num_schedule_entries < 1 ) sched_priv->next_major_frame = now + DEFAULT_TIMESLICE; else if ( now >= sched_priv->next_major_frame ) { /* time to enter a new major frame * the first time this function is called, this will be true */ /* start with the first domain in the schedule */ sched_index = 0; sched_priv->next_major_frame = now + sched_priv->major_frame; next_switch_time = now + sched_priv->schedule[0].runtime; } else { while ( (now >= next_switch_time) && (sched_index < sched_priv->num_schedule_entries) ) { /* time to switch to the next domain in this major frame */ sched_index++; next_switch_time += sched_priv->schedule[sched_index].runtime; } } /* * If we exhausted the domains in the schedule and still have time left * in the major frame then switch next at the next major frame. */ if ( sched_index >= sched_priv->num_schedule_entries ) next_switch_time = sched_priv->next_major_frame; /* * If there are more domains to run in the current major frame, set * new_task equal to the address of next domain's VCPU structure. * Otherwise, set new_task equal to the address of the idle task's VCPU * structure. */ new_task = (sched_index < sched_priv->num_schedule_entries) ? sched_priv->schedule[sched_index].vc : IDLETASK(cpu); /* Check to see if the new task can be run (awake & runnable). */ if ( !((new_task != NULL) && (AVCPU(new_task) != NULL) && AVCPU(new_task)->awake && vcpu_runnable(new_task)) ) new_task = IDLETASK(cpu); BUG_ON(new_task == NULL); /* * Check to make sure we did not miss a major frame. * This is a good test for robust partitioning. */ BUG_ON(now >= sched_priv->next_major_frame); spin_unlock_irqrestore(&sched_priv->lock, flags); /* Tasklet work (which runs in idle VCPU context) overrides all else. */ if ( tasklet_work_scheduled ) new_task = IDLETASK(cpu); /* Running this task would result in a migration */ if ( !is_idle_vcpu(new_task) && (new_task->processor != cpu) ) new_task = IDLETASK(cpu); /* * Return the amount of time the next domain has to run and the address * of the selected task's VCPU structure. */ ret.time = next_switch_time - now; ret.task = new_task; ret.migrated = 0; BUG_ON(ret.time <= 0); return ret; }
/** * This function allocates scheduler-specific data for a domain * * We do not actually make use of any per-domain data but the hypervisor * expects a non-NULL return value * * @param ops Pointer to this instance of the scheduler structure * * @return Pointer to the allocated data */ static void * a653sched_alloc_domdata(const struct scheduler *ops, struct domain *dom) { /* return a non-NULL value to keep schedule.c happy */ return SCHED_PRIV(ops); }
/** * This function allocates scheduler-specific data for a physical CPU * * We do not actually make use of any per-CPU data but the hypervisor expects * a non-NULL return value * * @param ops Pointer to this instance of the scheduler structure * * @return Pointer to the allocated data */ static void * a653sched_alloc_pdata(const struct scheduler *ops, int cpu) { /* return a non-NULL value to keep schedule.c happy */ return SCHED_PRIV(ops); }
/** * This function performs deinitialization for an instance of the scheduler * * @param ops Pointer to this instance of the scheduler structure */ static void a653sched_deinit(const struct scheduler *ops) { xfree(SCHED_PRIV(ops)); }
/** * This function is called by the adjust_global scheduler hook to put * in place a new ARINC653 schedule. * * @param ops Pointer to this instance of the scheduler structure * * @return <ul> * <li> 0 = success * <li> !0 = error * </ul> */ static int arinc653_sched_set( const struct scheduler *ops, struct xen_sysctl_arinc653_schedule *schedule) { a653sched_priv_t *sched_priv = SCHED_PRIV(ops); s_time_t total_runtime = 0; unsigned int i; unsigned long flags; int rc = -EINVAL; spin_lock_irqsave(&sched_priv->lock, flags); /* 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) ) goto fail; for ( i = 0; i < schedule->num_sched_entries; i++ ) { /* Check for a valid run time. */ if ( schedule->sched_entries[i].runtime <= 0 ) goto fail; /* Add this entry's run time to total run time. */ total_runtime += schedule->sched_entries[i].runtime; } /* * Error if the major frame is not large enough to run all entries as * indicated by comparing the total run time to the major frame length. */ if ( total_runtime > schedule->major_frame ) goto fail; /* Copy the new schedule into place. */ sched_priv->num_schedule_entries = schedule->num_sched_entries; sched_priv->major_frame = schedule->major_frame; for ( i = 0; i < schedule->num_sched_entries; i++ ) { memcpy(sched_priv->schedule[i].dom_handle, schedule->sched_entries[i].dom_handle, sizeof(sched_priv->schedule[i].dom_handle)); sched_priv->schedule[i].vcpu_id = schedule->sched_entries[i].vcpu_id; sched_priv->schedule[i].runtime = schedule->sched_entries[i].runtime; } update_schedule_vcpus(ops); /* * The newly-installed schedule takes effect immediately. We do not even * wait for the current major frame to expire. * * 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. */ sched_priv->next_major_frame = NOW(); rc = 0; fail: spin_unlock_irqrestore(&sched_priv->lock, flags); return rc; }
/** * This function performs deinitialization for an instance of the scheduler * * @param ops Pointer to this instance of the scheduler structure */ static void a653sched_deinit(struct scheduler *ops) { xfree(SCHED_PRIV(ops)); ops->sched_data = NULL; }