|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH] credi2-ratelimit: Implement rate limit for credit2 scheduler
On Wed, Jul 6, 2016 at 6:33 PM, Makkar anshul.makkar@xxxxxxxxxx <Anshul> wrote:
> From: Anshul Makkar <anshul.makkar@xxxxxxxxxx>
>
> Rate limit assures that a vcpu will execute for a minimum amount of time
> before
> being put at the back of a queue or being preempted by higher priority thread.
>
> It introduces a minimum amount of latency to enable a VM to batch its work and
> it also ensures that system is not spending most of its time in
> VMEXIT/VMENTRY because of VM that is waking/sleeping at high rate.
>
> ratelimit can be disabled by setting it to 0.
>
> Signed-off-by: Anshul Makkar <anshul.makkar@xxxxxxxxxx>
Hey Anshul! Thanks for submitting this. A few comments.
> ---
> ---
> xen/common/sched_credit2.c | 115
> ++++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 98 insertions(+), 17 deletions(-)
>
> diff --git a/xen/common/sched_credit2.c b/xen/common/sched_credit2.c
> index 1933ff1..6718574 100644
> --- a/xen/common/sched_credit2.c
> +++ b/xen/common/sched_credit2.c
> @@ -171,6 +171,11 @@ integer_param("sched_credit2_migrate_resist",
> opt_migrate_resist);
> #define c2r(_ops, _cpu) (CSCHED2_PRIV(_ops)->runq_map[(_cpu)])
> /* CPU to runqueue struct macro */
> #define RQD(_ops, _cpu) (&CSCHED2_PRIV(_ops)->rqd[c2r(_ops, _cpu)])
> +/* Find the max of time slice */
> +#define MAX_TSLICE(t1, t2) \
> + ({ typeof (t1) _t1 = (t1); \
> + typeof (t1) _t2 = (t2); \
> + _t1 > _t2 ? _t1 < 0 ? 0 : _t1 : _t2 < 0 ? 0 : _t2; })
Hiding the zero-checking behind this seems a bit strange to me. I
think if the algorithm is properly laid out, we probably don't need
this at all (see my proposed alternate below).
>
> /*
> * Shifts for load average.
> @@ -280,6 +285,7 @@ struct csched2_private {
> struct csched2_runqueue_data rqd[NR_CPUS];
>
> unsigned int load_window_shift;
> + unsigned ratelimit_us; /* each cpupool can have its onw ratelimit */
> };
>
> /*
> @@ -1588,6 +1594,34 @@ csched2_dom_cntl(
> return rc;
> }
>
> +static int csched2_sys_cntl(const struct scheduler *ops,
> + struct xen_sysctl_scheduler_op *sc)
> +{
> + int rc = -EINVAL;
> + xen_sysctl_credit_schedule_t *params = &sc->u.sched_credit;
> + struct csched2_private *prv = CSCHED2_PRIV(ops);
> + unsigned long flags;
> +
> + switch (sc->cmd )
> + {
> + case XEN_SYSCTL_SCHEDOP_putinfo:
> + if ( params->ratelimit_us &&
> + ( params->ratelimit_us < CSCHED2_MIN_TIMER ||
> + params->ratelimit_us > MICROSECS(CSCHED2_MAX_TIMER) ))
> + return rc;
> + spin_lock_irqsave(&prv->lock, flags);
> + prv->ratelimit_us = params->ratelimit_us;
> + spin_unlock_irqrestore(&prv->lock, flags);
> + break;
> +
> + case XEN_SYSCTL_SCHEDOP_getinfo:
> + params->ratelimit_us = prv->ratelimit_us;
> + rc = 0;
> + break;
> + }
> + return rc;
> +}
> +
> static void *
> csched2_alloc_domdata(const struct scheduler *ops, struct domain *dom)
> {
> @@ -1657,12 +1691,15 @@ csched2_dom_destroy(const struct scheduler *ops,
> struct domain *dom)
>
> /* How long should we let this vcpu run for? */
> static s_time_t
> -csched2_runtime(const struct scheduler *ops, int cpu, struct csched2_vcpu
> *snext)
> +csched2_runtime(const struct scheduler *ops, int cpu,
> + struct csched2_vcpu *snext, s_time_t now)
> {
> - s_time_t time;
> + s_time_t time;
> int rt_credit; /* Proposed runtime measured in credits */
> struct csched2_runqueue_data *rqd = RQD(ops, cpu);
> struct list_head *runq = &rqd->runq;
> + s_time_t runtime = 0;
> + struct csched2_private *prv = CSCHED2_PRIV(ops);
>
> /*
> * If we're idle, just stay so. Others (or external events)
> @@ -1680,6 +1717,14 @@ csched2_runtime(const struct scheduler *ops, int cpu,
> struct csched2_vcpu *snext
>
> /* 1) Basic time: Run until credit is 0. */
> rt_credit = snext->credit;
> + if (snext->vcpu->is_running)
> + runtime = now - snext->vcpu->runstate.state_entry_time;
> + if ( runtime < 0 )
> + {
> + runtime = 0;
> + d2printk("%s: Time went backwards? now %"PRI_stime" state_entry_time
> %"PRI_stime"\n",
> + _func__, now, snext->runstate.state_entry_time);
> + }
>
> /* 2) If there's someone waiting whose credit is positive,
> * run until your credit ~= his */
> @@ -1695,11 +1740,24 @@ csched2_runtime(const struct scheduler *ops, int cpu,
> struct csched2_vcpu *snext
> }
>
> /* The next guy may actually have a higher credit, if we've tried to
> - * avoid migrating him from a different cpu. DTRT. */
> + * avoid migrating him from a different cpu. DTRT.
> + * Even if the next guy has higher credit and current vcpu has executed
> + * for less amount of time than rate limit, allow it to run for minimum
> + * amount of time.
> + */
> if ( rt_credit <= 0 )
> {
> - time = CSCHED2_MIN_TIMER;
> - SCHED_STAT_CRANK(runtime_min_timer);
> + if ( snext->vcpu->is_running && prv->ratelimit_us)
> + /* implies the current one has executed for time < ratelimit and
> thus
> + * it has neen selcted int runq_candidate to run next.
> + * No need to check for this condition again.
> + */
> + time = MAX_TSLICE(CSCHED2_MIN_TIMER,
> + MICROSECS(prv->ratelimit_us) - runtime);
> + else
> + time = MAX_TSLICE(CSCHED2_MIN_TIMER,
> MICROSECS(prv->ratelimit_us));
> +
> + SCHED_STAT_CRANK(time);
> }
> else
> {
> @@ -1709,17 +1767,22 @@ csched2_runtime(const struct scheduler *ops, int cpu,
> struct csched2_vcpu *snext
> * at a different rate. */
> time = c2t(rqd, rt_credit, snext);
>
> - /* Check limits */
> - if ( time < CSCHED2_MIN_TIMER )
> - {
> - time = CSCHED2_MIN_TIMER;
> - SCHED_STAT_CRANK(runtime_min_timer);
> - }
> - else if ( time > CSCHED2_MAX_TIMER )
> + /* Check limits.
> + * time > ratelimit --> time > MIN.
> + */
> + if ( time > CSCHED2_MAX_TIMER )
> {
> +
> time = CSCHED2_MAX_TIMER;
> SCHED_STAT_CRANK(runtime_max_timer);
> }
> + else
> + {
> + time = MAX_TSLICE(MAX_TSLICE(CSCHED2_MIN_TIMER,
> + MICROSECS(prv->ratelimit_us)),
> time);
> + SCHED_STAT_CRANK(time);
> + }
> +
> }
This is all really confusing. What about something like this (also
attached)? The basic idea is to calculate min_time, then go through
the normal algorithm, then clip it once at the end.
@@ -1657,12 +1691,14 @@ csched2_dom_destroy(const struct scheduler
*ops, struct domain *dom)
/* How long should we let this vcpu run for? */
static s_time_t
-csched2_runtime(const struct scheduler *ops, int cpu, struct
csched2_vcpu *snext)
+csched2_runtime(const struct scheduler *ops, int cpu,
+ struct csched2_vcpu *snext, s_time_t now)
{
- s_time_t time;
+ s_time_t time, min_time;
int rt_credit; /* Proposed runtime measured in credits */
struct csched2_runqueue_data *rqd = RQD(ops, cpu);
struct list_head *runq = &rqd->runq;
+ struct csched2_private *prv = CSCHED2_PRIV(ops);
/*
* If we're idle, just stay so. Others (or external events)
@@ -1675,9 +1711,19 @@ csched2_runtime(const struct scheduler *ops,
int cpu, struct csched2_vcpu *snext
* 1) Run until snext's credit will be 0
* 2) But if someone is waiting, run until snext's credit is equal
* to his
- * 3) But never run longer than MAX_TIMER or shorter than MIN_TIMER.
+ * 3) But never run longer than MAX_TIMER or shorter than MIN_TIMER
+ * or your the ratelimit time.
*/
+ /* Calculate mintime */
+ min_time = CSCHED2_MIN_TIMER;
+ if ( prv->ratelimit_us ) {
+ s_time_t ratelimit_min = snext->vcpu->runstate.state_entry_time +
+ MICROSECS(prv->ratelimit_us) - now;
+ if ( ratelimit_min > min_time )
+ min_time = ratelimit_min;
+ }
+
/* 1) Basic time: Run until credit is 0. */
rt_credit = snext->credit;
@@ -1694,32 +1740,33 @@ csched2_runtime(const struct scheduler *ops,
int cpu, struct csched2_vcpu *snext
}
}
- /* The next guy may actually have a higher credit, if we've tried to
- * avoid migrating him from a different cpu. DTRT. */
- if ( rt_credit <= 0 )
+ /*
+ * The next guy on the runqueue may actually have a higher credit,
+ * if we've tried to avoid migrating him from a different cpu.
+ * Setting time=0 will ensure the minimum timeslice is chosen.
+ *
+ * FIXME: See if we can eliminate this conversion if we know time
+ * will be outside (MIN,MAX). Probably requires pre-calculating
+ * credit values of MIN,MAX per vcpu, since each vcpu burns credit
+ * at a different rate.
+ */
+ if ( rt_credit > 0 )
+ time = c2t(rqd, rt_credit, snext);
+ else
+ time = 0;
+
+
+ /* 3) But never run longer than MAX_TIMER or shorter than MIN_TIMER
+ * or your ratelimit time */
+ if ( time < min_time )
{
- time = CSCHED2_MIN_TIMER;
+ time = min_time;
SCHED_STAT_CRANK(runtime_min_timer);
}
- else
+ else if ( time > CSCHED2_MAX_TIMER )
{
- /* FIXME: See if we can eliminate this conversion if we know time
- * will be outside (MIN,MAX). Probably requires pre-calculating
- * credit values of MIN,MAX per vcpu, since each vcpu burns credit
- * at a different rate. */
- time = c2t(rqd, rt_credit, snext);
-
- /* Check limits */
- if ( time < CSCHED2_MIN_TIMER )
- {
- time = CSCHED2_MIN_TIMER;
- SCHED_STAT_CRANK(runtime_min_timer);
- }
- else if ( time > CSCHED2_MAX_TIMER )
- {
- time = CSCHED2_MAX_TIMER;
- SCHED_STAT_CRANK(runtime_max_timer);
- }
+ time = CSCHED2_MAX_TIMER;
+ SCHED_STAT_CRANK(runtime_max_timer);
}
return time;
>
> return time;
> @@ -1733,7 +1796,7 @@ void __dump_execstate(void *unused);
> static struct csched2_vcpu *
> runq_candidate(struct csched2_runqueue_data *rqd,
> struct csched2_vcpu *scurr,
> - int cpu, s_time_t now)
> + int cpu, s_time_t now, struct csched2_private *prv)
Since we have the cpu, I think we can get ops this way, without
cluttering things up with the extra argument:
struct csched_private *prv = CSCHED_PRIV(per_cpu(scheduler, cpu));
> {
> struct list_head *iter;
> struct csched2_vcpu *snext = NULL;
> @@ -1744,6 +1807,16 @@ runq_candidate(struct csched2_runqueue_data *rqd,
> else
> snext = CSCHED2_VCPU(idle_vcpu[cpu]);
>
> + /* return the current vcpu if it has executed for less than ratelimit.
> + * Adjuststment for the selected vcpu's credit and decision
> + * for how long it will run will be taken in csched2_runtime.
> + */
> + if ( prv->ratelimit_us && !is_idle_vcpu(scurr->vcpu) &&
> + vcpu_runnable(scurr->vcpu) &&
> + (now - scurr->vcpu->runstate.state_entry_time) <
> + MICROSECS(prv->ratelimit_us) )
> + return scurr;
> +
This looks OK. The comment should probably follow the general hypervisor style:
/*
* blah blah blah
*/
I realize most of the file isn't like this, but that was probably a mistake. :-)
> list_for_each( iter, &rqd->runq )
> {
> struct csched2_vcpu * svc = list_entry(iter, struct csched2_vcpu,
> runq_elem);
> @@ -1762,9 +1835,13 @@ runq_candidate(struct csched2_runqueue_data *rqd,
> }
>
> /* If the next one on the list has more credit than current
> - * (or idle, if current is not runnable), choose it. */
> + * (or idle, if current is not runnable) and current one has already
> + * executed for more than ratelimit. choose it.
> + * Control has reached here means that current vcpu has executed >
> + * ratelimit_us or ratelimit is off, so chose the next one.
> + */
> if ( svc->credit > snext->credit )
> - snext = svc;
> + snext = svc;
I agree with Dario; this comment change isn't very helpful.
>
> /* In any case, if we got this far, break. */
> break;
> @@ -1787,6 +1864,7 @@ csched2_schedule(
> struct csched2_vcpu * const scurr = CSCHED2_VCPU(current);
> struct csched2_vcpu *snext = NULL;
> struct task_slice ret;
> + struct csched2_private *prv = CSCHED2_PRIV(ops);
>
> SCHED_STAT_CRANK(schedule);
> CSCHED2_VCPU_CHECK(current);
> @@ -1857,7 +1935,7 @@ csched2_schedule(
> snext = CSCHED2_VCPU(idle_vcpu[cpu]);
> }
> else
> - snext=runq_candidate(rqd, scurr, cpu, now);
> + snext=runq_candidate(rqd, scurr, cpu, now, prv);
>
> /* If switching from a non-idle runnable vcpu, put it
> * back on the runqueue. */
> @@ -1921,7 +1999,7 @@ csched2_schedule(
> /*
> * Return task to run next...
> */
> - ret.time = csched2_runtime(ops, cpu, snext);
> + ret.time = csched2_runtime(ops, cpu, snext, now);
> ret.task = snext->vcpu;
>
> CSCHED2_VCPU_CHECK(ret.task);
> @@ -2353,6 +2431,8 @@ csched2_init(struct scheduler *ops)
> prv->runq_map[i] = -1;
> prv->rqd[i].id = -1;
> }
> + /* initialize ratelimit */
> + prv->ratelimit_us = 1000 * CSCHED2_MIN_TIMER;
Is there any reason this isn't using sched_ratelimit_us (the
hypervisor command-line option, which the credit scheduler is using)?
Thanks again!
-George
Attachment:
example.diff _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |