所谓带宽控制,指的是能够控制任务长时间占用CPU的能力。Deadline调度器、RT调度器、CFS调度器均支持这种能力。这篇笔记记录了RT调度器的带宽控制相关实现。
RT调度器的带宽控制核心思想是:限制CPU运行队列上的任务在检测周期内占用的CPU时长不能超过限定时长,检测周期和限定时长正是RT调度器带宽控制的两个可配置参数。带宽控制可以在CPU级别(即CPU运行队列上)实现;支持组调度后,带宽控制还可以扩展到在分组级别(即任务组的运行队列上)。
可配置参数
带宽控制的可配置参数也分为CPU和任务组两个级别。
CPU级别配置参数
在CPU级别上,由下面两个全局变量表示检测周期和限定时长两个带宽控制参数。默认的周期为1s,RT任务可运行时长为0.95s,即最大可以占用95%的CPU。这两个参数对应的用户态配置节点为/proc/sys/kernel/sched_rt_period_us和/proc/sys/kernelsched_rt_runtime_us。
/*
* period over which we measure -rt task cpu usage in us.
* default: 1s
*/
unsigned int sysctl_sched_rt_period = 1000000;
/*
* part of the period that we allow rt tasks to run in us.
* default: 0.95s
*/
int sysctl_sched_rt_runtime = 950000;
RT调度器就是通过判断sysctl_sched_rt_runtime变量来确定系统是否开启带宽控制的。
static inline int rt_bandwidth_enabled(void)
{
return sysctl_sched_rt_runtime >= 0;
}
任务组级别配置参数
支持组调度后,用户态的每个控制组的目录下增加了rt_period_us和rt_runtime_us,分别表示该任务组的检测周期和运行时长。
static struct cftype cpu_files[] = {
...
#ifdef CONFIG_RT_GROUP_SCHED
{
.name = "rt_runtime_us",
.read_s64 = cpu_rt_runtime_read,
.write_s64 = cpu_rt_runtime_write,
},
{
.name = "rt_period_us",
.read_u64 = cpu_rt_period_read_uint,
.write_u64 = cpu_rt_period_write_uint,
},
#endif
{ } /* terminate */
};
数据结构
RT带宽控制: rt_bandwidth
调度过程中,RT调度器设计了rt_bandwidth结构来保存带宽控制信息。对于任务组,在task_group中包含了该结构;对于CPU级别的控制,则设计了一个全局变量def_rt_bandwidth。
struct rt_bandwidth {
raw_spinlock_t rt_runtime_lock;
ktime_t rt_period;
// 检查周期参数,单位为纳秒
u64 rt_runtime;
// 限定时长参数,单位为纳秒。值为0表示不限制
struct hrtimer rt_period_timer; // 用于解除限制的定时器
};
struct task_group {
...
#ifdef CONFIG_RT_GROUP_SCH