本文是基于linux kernel 5.15.41

QoS(Quality Of Service),最大限度地满足多个子系统的需求。具体的数据结构和函数声明在/include/linux/pm_qos.h,主要分成两类,系统级(PM QoS classes framework,/kernel/power/qos.c)和设备级(per-device PM QoS framework,/drivers/base/power/qos.c),这两者有共同的实现流程,都是将需求进行分类计算极值,并向执行者提供需求查询接口,但是作用的场合不同。

  • 系统级主要是限制cpu&dma latency、cpu frequecy;
  • 设备级主要是限制resume_latency、active状态的latency_tolerance和QoS flag(如:no power off(不允许设备断电)/remote wakeup(设备应具备唤醒功能));

PM QoS classes framework

PM Qos( /kernel/power/qos.c)底层原理是维护一个双向的优先级链表plist,优先级从大到小排序,即prio值从小到大,prio值相同的节点按先插入在前的顺序。

需求方:提出需求,如进程、driver等。

框架层:汇总所有需求方的需求,计算出极值。

执行方:需要满足所有的需求方的需求,如cpuidle、cpufreq。

Freq Qos

多个进程或者driver需要设置max frequency,如果是直接设置scaling_max_freq(/sys/devices/system/cpu/cpufreq/policy*/),会存在覆盖的行为,无法满足所有需求,因此采用qos的方式。

在freq qos中,需求方的frequency需求作为prio值,把需求做为链表节点插入进去。

  • 获取最大max frequency,执行方为满足所有的需求方,需要取极大值(PM_QOS_MAX),直接取链表尾即可。
  • 获取最大min frequency,执行方为满足所有的需求方,需要取极小值(PM_QOS_MIN),直接取链表头即可。

这个plist是由prio_list和node_list组成

  • prio_list:只链接优先级不同的node,相同优先级的node先进先出。
  • node_list:链接所有的node。

plist.drawio.png

freq qos公开API函数:

int freq_qos_add_request(struct freq_constraints *qos,
             struct freq_qos_request *req,
             enum freq_qos_req_type type, s32 value);

int freq_qos_update_request(struct freq_qos_request *req, s32 new_value);

int freq_qos_remove_request(struct freq_qos_request *req)

int freq_qos_add_notifier(struct freq_constraints *qos,
              enum freq_qos_req_type type,
              struct notifier_block *notifier);

int freq_qos_remove_notifier(struct freq_constraints *qos,
                 enum freq_qos_req_type type,
                 struct notifier_block *notifier);

1. freq_constraints_init

// 初始化freq_constraints
void freq_constraints_init(struct freq_constraints *qos)
{
    struct pm_qos_constraints *c;

    c = &qos->min_freq;
    plist_head_init(&c->list);
    c->target_value = FREQ_QOS_MIN_DEFAULT_VALUE;
    c->default_value = FREQ_QOS_MIN_DEFAULT_VALUE;
    c->no_constraint_value = FREQ_QOS_MIN_DEFAULT_VALUE;
    //min_freq的初始化类型是PM_QOS_MAX
    c->type = PM_QOS_MAX;
    c->notifiers = &qos->min_freq_notifiers;
    BLOCKING_INIT_NOTIFIER_HEAD(c->notifiers);

    c = &qos->max_freq;
    plist_head_init(&c->list);
    c->target_value = FREQ_QOS_MAX_DEFAULT_VALUE;
    c->default_value = FREQ_QOS_MAX_DEFAULT_VALUE;
    c->no_constraint_value = FREQ_QOS_MAX_DEFAULT_VALUE;
    //max_freq的初始化类型是PM_QOS_MIN
    c->type = PM_QOS_MIN;
    c->notifiers = &qos->max_freq_notifiers;
    BLOCKING_INIT_NOTIFIER_HEAD(c->notifiers);
}

2. freq_qos_add_request

// 添加frequency的需求节点,插入优先级双向链表
freq_qos_add_request(struct freq_constraints *qos,struct freq_qos_request *req,enum freq_qos_req_type type, s32 value)
    |-freq_qos_apply(req, PM_QOS_ADD_REQ, value);
        |-case FREQ_QOS_MIN://如果req->type是FREQ_QOS_MIN,省略FREQ_QOS_MAX
            |-pm_qos_update_target(&req->qos->min_freq, &req->pnode, action, value);
                |-case PM_QOS_ADD_REQ:
                    |-plist_node_init(node, new_value);//创建初始化node,prio值为new_value
                    |-plist_add(node, &c->list);//插入优先级双向链表,优先级从大到小排序,高优先级(prio值小)在前面
                |-curr_value = pm_qos_get_value(c);//获取当前值
                |-pm_qos_set_value(c, curr_value);//设置c->target_value值
// 添加frequency的需求节点,插入优先级双向链表
int freq_qos_add_request(struct freq_constraints *qos,
             struct freq_qos_request *req,
             enum freq_qos_req_type type, s32 value)
{
    int ret;

    if (IS_ERR_OR_NULL(qos) || !req)
        return -EINVAL;

    if (WARN(freq_qos_request_active(req),
         "%s() called for active request\n", __func__))
        return -EINVAL;

    req->qos = qos;
    req->type = type;
    ret = freq_qos_apply(req, PM_QOS_ADD_REQ, value);
    if (ret < 0) {
        req->qos = NULL;
        req->type = 0;
    }

    return ret;
}
EXPORT_SYMBOL_GPL(freq_qos_add_request);
// 添加/修改/删除频率 QoS 请求
int freq_qos_apply(struct freq_qos_request *req,
              enum pm_qos_req_action action, s32 value)
{
    int ret;

    switch(req->type) {
    case FREQ_QOS_MIN:
        ret = pm_qos_update_target(&req->qos->min_freq, &req->pnode,
                       action, value);
        break;
    case FREQ_QOS_MAX:
        ret = pm_qos_update_target(&req->qos->max_freq, &req->pnode,
                       action, value);
        break;
    default:
        ret = -EINVAL;
    }

    return ret;
}
//更新PM QoS 约束请求链表
int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node,
             enum pm_qos_req_action action, int value)
{
    int prev_value, curr_value, new_value;
    unsigned long flags;

    spin_lock_irqsave(&pm_qos_lock, flags);
    
    //获取上一个value
    prev_value = pm_qos_get_value(c);
    if (value == PM_QOS_DEFAULT_VALUE)
        new_value = c->default_value;
    else
        new_value = value;

    switch (action) {
    case PM_QOS_REMOVE_REQ:
        plist_del(node, &c->list);//在链表中删除node节点
        break;
    case PM_QOS_UPDATE_REQ:
        /*
         * To change the list, atomically remove, reinit with new value
         * and add, then see if the aggregate has changed.
         */
        plist_del(node, &c->list);//在链表中删除node节点
        fallthrough;//在C17中,没有break,会warning,消除warning
    case PM_QOS_ADD_REQ:
        plist_node_init(node, new_value);//创建初始化node,prio值为new_value
        plist_add(node, &c->list);//插入优先级双向链表,优先级从大到小排序,优先级越高,prio值小。
        break;
    default:
        /* no action */
        ;
    }
    //获取当前值
    curr_value = pm_qos_get_value(c);
    //设置c->target_value值
    pm_qos_set_value(c, curr_value);

    spin_unlock_irqrestore(&pm_qos_lock, flags);

    trace_pm_qos_update_target(action, prev_value, curr_value);

    if (prev_value == curr_value)
        return 0;

    //当value发生改变时,将当前的值通知所有节点
    if (c->notifiers)
        blocking_notifier_call_chain(c->notifiers, curr_value, NULL);

    return 1;
}
static int pm_qos_get_value(struct pm_qos_constraints *c)
{
    if (plist_head_empty(&c->list))
        return c->no_constraint_value;

    switch (c->type) {
    case PM_QOS_MIN:
        return plist_first(&c->list)->prio;//获取链表头,即prio最小

    case PM_QOS_MAX:
        return plist_last(&c->list)->prio;//获取链表尾,即prio最大

    default:
        WARN(1, "Unknown PM QoS type in %s\n", __func__);
        return PM_QOS_DEFAULT_VALUE;
    }
}

3. freq_qos_update_request

//更新现有freq QoS需求
int freq_qos_update_request(struct freq_qos_request *req, s32 new_value)
{
    if (!req)
        return -EINVAL;

    if (WARN(!freq_qos_request_active(req),
         "%s() called for unknown object\n", __func__))
        return -EINVAL;

    // 更新prio值
    if (req->pnode.prio == new_value)
        return 0;

    //先删除plist_node,再添加新的node
    return freq_qos_apply(req, PM_QOS_UPDATE_REQ, new_value);
}
EXPORT_SYMBOL_GPL(freq_qos_update_request);

4. freq_qos_remove_request

//从plist中删除freq QoS请求
int freq_qos_remove_request(struct freq_qos_request *req)
{
    int ret;

    if (!req)
        return -EINVAL;

    if (WARN(!freq_qos_request_active(req),
         "%s() called for unknown object\n", __func__))
        return -EINVAL;

    ret = freq_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
    req->qos = NULL;
    req->type = 0;

    return ret;
}
EXPORT_SYMBOL_GPL(freq_qos_remove_request);

5. freq_qos_add_notifier

//注册freq QoS更改通知
int freq_qos_add_notifier(struct freq_constraints *qos,
              enum freq_qos_req_type type,
              struct notifier_block *notifier)
{
    int ret;

    if (IS_ERR_OR_NULL(qos) || !notifier)
        return -EINVAL;

    switch (type) {
    case FREQ_QOS_MIN:
        // 内核通知链机制,订阅/发布
        ret = blocking_notifier_chain_register(qos->min_freq.notifiers,
                               notifier);
        break;
    case FREQ_QOS_MAX:
        ret = blocking_notifier_chain_register(qos->max_freq.notifiers,
                               notifier);
        break;
    default:
        WARN_ON(1);
        ret = -EINVAL;
    }

    return ret;
}
EXPORT_SYMBOL_GPL(freq_qos_add_notifier);

6. freq_qos_remove_notifier

//删除freq QoS更改通知
int freq_qos_remove_notifier(struct freq_constraints *qos,
                 enum freq_qos_req_type type,
                 struct notifier_block *notifier)
{
    int ret;

    if (IS_ERR_OR_NULL(qos) || !notifier)
        return -EINVAL;

    switch (type) {
    case FREQ_QOS_MIN:
        ret = blocking_notifier_chain_unregister(qos->min_freq.notifiers,
                             notifier);
        break;
    case FREQ_QOS_MAX:
        ret = blocking_notifier_chain_unregister(qos->max_freq.notifiers,
                             notifier);
        break;
    default:
        WARN_ON(1);
        ret = -EINVAL;
    }

    return ret;
}
EXPORT_SYMBOL_GPL(freq_qos_remove_notifier);
文章目录