if ( is_idle_domain(d) )
{
v->runstate.state = RUNSTATE_running;
+ v->new_state = RUNSTATE_running;
}
else
{
if ( !((new_task != NULL)
&& (AUNIT(new_task) != NULL)
&& AUNIT(new_task)->awake
- && unit_runnable(new_task)) )
+ && unit_runnable_state(new_task)) )
new_task = IDLETASK(cpu);
BUG_ON(new_task == NULL);
if ( !test_bit(CSCHED_FLAG_UNIT_YIELD, &scurr->flags)
&& !tasklet_work_scheduled
&& prv->ratelimit
- && unit_runnable(unit)
+ && unit_runnable_state(unit)
&& !is_idle_unit(unit)
&& runtime < prv->ratelimit )
{
dec_nr_runnable(sched_cpu);
}
- snext = __runq_elem(runq->next);
-
- /* Tasklet work (which runs in idle UNIT context) overrides all else. */
- if ( tasklet_work_scheduled )
- {
- TRACE_0D(TRC_CSCHED_SCHED_TASKLET);
- snext = CSCHED_UNIT(sched_idle_unit(sched_cpu));
- snext->pri = CSCHED_PRI_TS_BOOST;
- }
-
/*
* Clear YIELD flag before scheduling out
*/
clear_bit(CSCHED_FLAG_UNIT_YIELD, &scurr->flags);
- /*
- * SMP Load balance:
- *
- * If the next highest priority local runnable UNIT has already eaten
- * through its credits, look on other PCPUs to see if we have more
- * urgent work... If not, csched_load_balance() will return snext, but
- * already removed from the runq.
- */
- if ( snext->pri > CSCHED_PRI_TS_OVER )
- __runq_remove(snext);
- else
- snext = csched_load_balance(prv, sched_cpu, snext, &migrated);
+ do {
+ snext = __runq_elem(runq->next);
+
+ /* Tasklet work (which runs in idle UNIT context) overrides all else. */
+ if ( tasklet_work_scheduled )
+ {
+ TRACE_0D(TRC_CSCHED_SCHED_TASKLET);
+ snext = CSCHED_UNIT(sched_idle_unit(sched_cpu));
+ snext->pri = CSCHED_PRI_TS_BOOST;
+ }
+
+ /*
+ * SMP Load balance:
+ *
+ * If the next highest priority local runnable UNIT has already eaten
+ * through its credits, look on other PCPUs to see if we have more
+ * urgent work... If not, csched_load_balance() will return snext, but
+ * already removed from the runq.
+ */
+ if ( snext->pri > CSCHED_PRI_TS_OVER )
+ __runq_remove(snext);
+ else
+ snext = csched_load_balance(prv, sched_cpu, snext, &migrated);
+
+ } while ( !unit_runnable_state(snext->unit) );
/*
* Update idlers mask if necessary. When we're idling, other CPUs
* In fact, it may be the case that scurr is about to spin, and there's
* no point forcing it to do so until rate limiting expires.
*/
- if ( !yield && prv->ratelimit_us && unit_runnable(scurr->unit) &&
+ if ( !yield && prv->ratelimit_us && unit_runnable_state(scurr->unit) &&
(now - scurr->unit->state_entry_time) < MICROSECS(prv->ratelimit_us) )
{
if ( unlikely(tb_init_done) )
*
* Of course, we also default to idle also if scurr is not runnable.
*/
- if ( unit_runnable(scurr->unit) && !soft_aff_preempt )
+ if ( unit_runnable_state(scurr->unit) && !soft_aff_preempt )
snext = scurr;
else
snext = csched2_unit(sched_idle_unit(cpu));
* some budget, then choose it.
*/
if ( (yield || svc->credit > snext->credit) &&
- (!has_cap(svc) || unit_grab_budget(svc)) )
+ (!has_cap(svc) || unit_grab_budget(svc)) &&
+ unit_runnable_state(svc->unit) )
snext = svc;
/* In any case, if we got this far, break. */
cpumask_set_cpu(sched_cpu, &prv->cpus_free);
}
- if ( unlikely(prev->next_task == NULL || !unit_runnable(prev->next_task)) )
+ if ( unlikely(prev->next_task == NULL ||
+ !unit_runnable_state(prev->next_task)) )
prev->next_task = sched_idle_unit(sched_cpu);
NULL_UNIT_CHECK(prev->next_task);
else
{
snext = runq_pick(ops, cpumask_of(sched_cpu));
+
if ( snext == NULL )
snext = rt_unit(sched_idle_unit(sched_cpu));
+ else if ( !unit_runnable_state(snext->unit) )
+ {
+ q_remove(snext);
+ snext = rt_unit(sched_idle_unit(sched_cpu));
+ }
/* if scurr has higher priority and budget, still pick scurr */
if ( !is_idle_unit(currunit) &&
- unit_runnable(currunit) &&
+ unit_runnable_state(currunit) &&
scurr->cur_budget > 0 &&
( is_idle_unit(snext->unit) ||
compare_unit_priority(scurr, snext) > 0 ) )
for_each_sched_unit_vcpu ( unit, v )
{
if ( running )
- vcpu_runstate_change(v, RUNSTATE_running, new_entry_time);
+ vcpu_runstate_change(v, v->new_state, new_entry_time);
else
vcpu_runstate_change(v,
((v->pause_flags & VPF_blocked) ? RUNSTATE_blocked :
return false;
}
+/*
+ * Returns whether a sched_unit is runnable and sets new_state for each of its
+ * vcpus. It is mandatory to determine the new runstate for all vcpus of a unit
+ * without dropping the schedule lock (which happens when synchronizing the
+ * context switch of the vcpus of a unit) in order to avoid races with e.g.
+ * vcpu_sleep().
+ */
+static inline bool unit_runnable_state(const struct sched_unit *unit)
+{
+ struct vcpu *v;
+ bool runnable, ret = false;
+
+ if ( is_idle_unit(unit) )
+ return true;
+
+ for_each_sched_unit_vcpu ( unit, v )
+ {
+ runnable = vcpu_runnable(v);
+
+ v->new_state = runnable ? RUNSTATE_running
+ : (v->pause_flags & VPF_blocked)
+ ? RUNSTATE_blocked : RUNSTATE_offline;
+
+ if ( runnable )
+ ret = true;
+ }
+
+ return ret;
+}
+
static inline void sched_set_res(struct sched_unit *unit,
struct sched_resource *res)
{
XEN_GUEST_HANDLE(vcpu_runstate_info_compat_t) compat;
} runstate_guest; /* guest address */
#endif
+ unsigned int new_state;
/* Has the FPU been initialised? */
bool fpu_initialised;