Linux RT带宽控制
Linux RT带宽控制是通过在/proc/sys/kernel的以下节点控制的
sched_rt_period_us sched_rt_runtime_us
root@ubuntu:/proc/sys/kernel# cat sched_rt_period_us
1000000
root@ubuntu:/proc/sys/kernel# cat sched_rt_runtime_us
950000
这表明,在1000ms中,RT 任务最多可执行950ms
我写了一个while 1 并设置rt 调度,在ftrace中发现,其最长的运行时间是950ms, 这和设置吻合。
Linux RT带宽控制实现
rt 的调度逻辑都在rt.c中。
.update_curr = update_curr_rt,
每个tick 系统会调用 update_curr_rt 查看rt task的运行情况。
static void update_curr_rt(struct rq *rq)
{
struct task_struct *curr = rq->curr;
struct sched_rt_entity *rt_se = &curr->rt;
u64 delta_exec;
u64 now;
if (curr->sched_class != &rt_sched_class)
return;
now = rq_clock_task(rq);
delta_exec = now - curr->se.exec_start;
if (unlikely((s64)delta_exec <= 0))
return;
schedstat_set(curr->se.statistics.exec_max,
max(curr->se.statistics.exec_max, delta_exec));
curr->se.sum_exec_runtime += delta_exec;
account_group_exec_runtime(curr, delta_exec);
curr->se.exec_start = now;
cgroup_account_cputime(curr, delta_exec);
if (!rt_bandwidth_enabled())
return;
for_each_sched_rt_entity(rt_se)
{
struct rt_rq *rt_rq = rt_rq_of_se(rt_se);
int exceeded;
if (sched_rt_runtime(rt_rq) != RUNTIME_INF) {
raw_spin_lock(&rt_rq->rt_runtime_lock);
rt_rq->rt_time += delta_exec; //这里进行了执行时间的更新,注意是更新到rt_rq中,不是任务结构,这表明所有rt任务在1s中,只能执行950ms。,
exceeded = sched_rt_runtime_exceeded(rt_rq);
if (exceeded) {
resched_curr(rq); //如果已经超过时间,重新调度。
}
raw_spin_unlock(&rt_rq->rt_runtime_lock);
if (exceeded)
do_start_rt_bandwidth(
sched_rt_bandwidth(rt_rq));
}
}
}
如何判断是否超过时间,在sched_rt_runtime_exceeded中进行。
static int sched_rt_runtime_exceeded(struct rt_rq *rt_rq)
{
u64 runtime = sched_rt_runtime(rt_rq);
if (rt_rq->rt_throttled)
return rt_rq_throttled(rt_rq);
if (runtime >= sched_rt_period(rt_rq))
return 0;
balance_runtime(rt_rq);
runtime = sched_rt_runtime(rt_rq);
if (runtime == RUNTIME_INF)
return 0;
if (rt_rq->rt_time > runtime) {
struct rt_bandwidth *rt_b = sched_rt_bandwidth(rt_rq);
/*
* Don't actually throttle groups that have no runtime assigned
* but accrue some time due to boosting.
*/
if (likely(rt_b->rt_runtime)) {
rt_rq->rt_throttled = 1;
printk_deferred_once(
"sched: RT throttling activated\n");
} else {
/*
* In case we did anyway, make it go away,
* replenishment is a joke, since it will replenish us
* with exactly 0 ns.
*/
rt_rq->rt_time = 0;
}
if (rt_rq_throttled(rt_rq)) {
sched_rt_rq_dequeue(rt_rq); //设置 rq->rt.rt_queued = 1
return 1;
}
}
return 0;
}
让rt调度暂停,不在选择任务。
static struct task_struct *pick_next_task_rt(struct rq *rq)
{
struct task_struct *p;
if (!sched_rt_runnable(rq)) //如果rt已经执行到了limit 暂停选择rt任务
return NULL;
/*
static inline bool sched_rt_runnable(struct rq *rq)
{
return rq->rt.rt_queued > 0;
}
*/
p = _pick_next_task_rt(rq);
set_next_task_rt(rq, p, true);
return p;
}
rt 任务初始化是,会启动一个hr timer。 timer 的周期为sched_rt_period_us
void init_rt_bandwidth(struct rt_bandwidth *rt_b, u64 period, u64 runtime)
{
rt_b->rt_period = ns_to_ktime(period);
rt_b->rt_runtime = runtime;
raw_spin_lock_init(&rt_b->rt_runtime_lock);
hrtimer_init(&rt_b->rt_period_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_HARD);
rt_b->rt_period_timer.function = sched_rt_period_timer;
}
但这个timer响应时,如果发现rt 任务已经暂停。会调用sched_rt_rq_enqueue(rt_rq);重启rt 调度。
如果启动两个rt - rr的任务,他们在1s中的执行时间也时950ms