RT调度1—cpupri

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进行运行。

原文地址:https://www.cnblogs.com/hellokitty2/p/15294465.html