1. 相关结构
struct cpupri_vec { atomic_t count; //记录了此mask成员中被set的CPU的个数,见cpupri_set() cpumask_var_t mask; //设置了哪些CPU }; struct cpupri { struct cpupri_vec pri_to_cpu[CPUPRI_NR_PRIORITIES]; //102个元素,见convert_prio(),invalid:-1,idle:0,cfs:1,rt:101-prio(0-99) /*rq_offline_rt()中设置为 CPUPRI_INVALID */ int *cpu_to_pri; //每个CPU一个的数组,存放cpupri_set()的参数3,保存的是最新设置的 new_pri 值,也是此cpu上此时最高的优先级 };
2. cpupri_set
(1) 调用路径
rq_offline_rt //rq_offline回调,设置 CPUPRI_INVALID 这个无效优先级到cpu_to_pri rq_online_rt //rq_online回调,设置 rt_rq->highest_prio.curr这个优先级 dequeue_rt_entity enqueue_rt_entity dequeue_rt_stack __dequeue_rt_entity dec_rt_tasks dec_rt_prio //最高优先级的dequeue了,设置第二大的优先级 dec_rt_prio_smp(struct rt_rq *rt_rq, int prio, int prev_prio) //prev_prio 取设置之前的rt_rq->highest_prio.curr enqueue_rt_entity dequeue_rt_entity __enqueue_rt_entity inc_rt_tasks inc_rt_prio //新任务优先级比rt_rq->highest_prio.curr高才设置 inc_rt_prio_smp(struct rt_rq *rt_rq, int prio, int prev_prio) //prev_prio 取设置之前的rt_rq->highest_prio.curr cpupri_set
(2) 函数解析
void cpupri_set(struct cpupri *cp, int cpu, int newpri) { int *currpri = &cp->cpu_to_pri[cpu]; //per-cpu的 int oldpri = *currpri; //上次调用这个函数设置的newpri if (newpri == oldpri) return; newpri = convert_prio(newpri); //对优先级转换一下,cpupri = 101-newpri,(RT的)优先级越高,这个数值越大 if (likely(newpri != CPUPRI_INVALID)) { //将此 cpu id 设置到 newpri 对应的 vec 中,并计数加1 struct cpupri_vec *vec = &cp->pri_to_cpu[newpri]; cpumask_set_cpu(cpu, vec->mask); atomic_inc(&(vec)->count); } if (likely(oldpri != CPUPRI_INVALID)) { //将oldpri对应的vec中记录的cpu清除并计数减1 struct cpupri_vec *vec = &cp->pri_to_cpu[oldpri]; atomic_dec(&(vec)->count); cpumask_clear_cpu(cpu, vec->mask); } *currpri = newpri; }
设置新的时,同时会把旧的给删除掉。比如设置cpu1上运行的新任务的优先级为10,第一次设置后,cp->cpu_to_pri[1]=10,cp->pri_to_cpu[10].mask记录了cpu1,然后cpu1上切换到一个优先级为20的任务在运行了,再次设置后,cp->cpu_to_pri[1]=20,cp->pri_to_cpu[20].mask记录了cpu1,并且会将之前在 cp->pri_to_cpu[10].mask 中设置的cpu1给清理掉。
虽然每次调用 cpupri_set 都会对 cp->cpu_to_pri[cpu] 赋值为新的值,但是其保存的是始终是最高优先级,因为在所有调用 cpupri_set 之前都进行了判断,只有newpri的优先级比之前设置的高,才会调用 cpupri_set 进行设置。
(3) rt_rq->highest_prio 中有两个成员,curr 和 next,主要在 rt.c中进行赋值
curr 的赋值逻辑为:
inc_rt_prio 中,若 prio 比 curr 成员记录的优先级还高,就将 curr 成员设置为prio;
dec_rt_prio 中,若 rq 中还有rt线程运行,赋值为 sched_find_first_bit(array->bitmap),否则赋值为 MAX_RT_PRIO
next 的赋值逻辑为:
enqueue_pushable_task 时,若是新的线程的优先级比next高,则next赋值为新线程的优先级
dequeue_pushable_task 时,赋值为 rq->rt.pushable_tasks 链表上的第一个线程的优先级
使用逻辑为:
sched_rt_rq_enqueue 中,若发现 rt_rq->highest_prio.curr 比 curr->prio 的优先级还高,就触发重新调度
dec_rt_prio_smp 中,将 rt_rq->highest_prio.curr 设置到 cpu_to_pri 中
select_task_rq_rt 中,新线程的优先级要比选中的cpu的 rt_rq->highest_prio.curr 成员记录的优先级还高才能选中此cpu.
find_lock_lowest_rq 中,给新线程选cpu,若选出的rq的 rt_rq->highest_prio.curr 比新线程的优先级还高,就不选择此cpu
pull_rt_task 中,若src cpu 的 src_rq->rt.highest_prio.next 比当前cpu的 this_rq->rt.highest_prio.curr 记录的优先级低,就不从这个src cpu上拉任务过来
rq_online_rt 中,将 rq->rt.highest_prio.curr 设置到 cpu_to_pri 上
prio_changed_rt 中,若 rq->rt.highest_prio.curr 记录的优先级比正在运行的任务的优先级还高,触发重新调度
rq->rt.highest_prio.curr:表示 rt 的优先级链表中任务的最高的优先级
rq->rt.highest_prio.next:主要是在push/pull任务迁移中使用
3. __cpupri_find
(1) 调用路径
push_rt_task find_lock_lowest_rq select_task_rq_rt //RT的select_task_rq回调 find_lowest_rq //rt.c check_preempt_curr_rt //RT的check_preempt_curr回调 check_preempt_equal_prio cpupri_find_fitness cpupri_find //cpupri.c 传参fitness_fn=NULL cpupri_find_fitness for (idx = 0; idx < task_pri; idx++) { __cpupri_find }
(2) 函数解析
//lowest_mask既是输入参数,也是输出参数 static inline int __cpupri_find(struct cpupri *cp, struct task_struct *p, struct cpumask *lowest_mask, int idx,bool drop_nopreempts) //cpupri.c { struct cpupri_vec *vec = &cp->pri_to_cpu[idx]; int skip = 0; /*此pri没有对应的CPU*/ if (!atomic_read(&(vec)->count)) return 0; //按位与的结果的第一个,只是一个test,若与的结果为空,则返回大于nr_cpu_ids的值 if (cpumask_any_and(p->cpus_ptr, vec->mask) >= nr_cpu_ids) return 0; if (lowest_mask) { //尊重task_struct的cpu亲和性的设置 cpumask_and(lowest_mask, p->cpus_ptr, vec->mask); if (drop_nopreempts) { //去除掉不能抢占的cpu,主要是判断是有软中断在运行就不抢占 drop_nopreempt_cpus(lowest_mask); } //去除掉isolated的cpu cpumask_andnot(lowest_mask, lowest_mask, cpu_isolated_mask); if (cpumask_empty(lowest_mask)) return 0; } return 1; }
按优先级由小到大遍历 cp->pri_to_cpu[] 数组,直到找到合适的CPU后返回,目标CPU放在 lowest_mask 中。因此,更容易运行在最低优先级任务运行的cpu上。
3. cpupri_check_rt 函数
__do_softirq
softirq_deferred_for_rt
cpupri_check_rt
bool cpupri_check_rt(void) { int cpu = raw_smp_processor_id(); //当前cpu的rt优先级链表上是否有RT线程等待运行,cpu_to_pri总是保存最新一次设置的值 return cpu_rq(cpu)->rd->cpupri.cpu_to_pri[cpu] > CPUPRI_NORMAL; }
4. 总结
每个 cpu 的 rq 的 root_domain 中都有一个 cpupri 结构,用于RT调度类的选核、迁移的优先级参考依据,支撑高优先级任务选择低优先级任务运行的cpu进行运行。