Soc统一调频插件及服务:源码解析及配置说明
仓库主页及介绍:
https://gitee.com/openharmony/resourceschedule\_resource\_schedule\_service
注:本文所分析源码为OpenHarmony3.2Release版本
SOC统一调频插件
简介
SOC统一调频插件(SocPerfPluing
)位于资源调度子系统(ResourceScheduleService, RSS
)内。其通过接收其他场景向RSS报告或通过RSS中智能分组模块转发的应用状态、焦点状态、后台任务状态等系统和应用事件,并将这些事件再分发给SOC统一调频服务(SocPerf
),从而进行相关的调频仲裁,最终使用内核接口设置配置文件中设置的CPU/GPU/DDR/NPU频率策略。
配置说明
配置文件 |
说明 |
socperf_resource_config.xml |
定义产品可支持的资源配置,例如CPU(大小核)/GPU/DDR/NPU等 |
socperf_boost_config.xml |
用于性能提频的配置文件 |
各个xml配置文件都需要按产品定制,不同产品的配置不相同。
对于指定的某产品,所有可支持配置的资源都定义在socperf_resource_config.xml
内,支持单路径/多路径配置,任何资源都有唯一的resID。socperf_boost_config.xml
使用的cmdID不能重复。
默认配置如下:
- socperf_resource_config.xml:
其中的
path
可以根据产品进行多路径配置,比如当产品CPU为多核时。
<Configs>
<Resource>
<res id="1000" name="lit_cpu_min_freq" pair="1001">
<default>408000</default>
<path>/sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq</path>
<node>408000 600000 816000 1104000 1416000 1608000 1800000 1992000</node>
</res>
<res id="1001" name="lit_cpu_max_freq" pair="1000" mode="1">
<default>1800000</default>
<path>/sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq</path>
<node>408000 600000 816000 1104000 1416000 1608000 1800000 1992000</node>
</res>
<res id="2000" name="gpu_min_freq" pair="2001">
<default>200000000</default>
<path>/sys/class/devfreq/fde60000.gpu/min_freq</path>
<node>200000000 300000000 400000000 600000000 700000000 800000000</node>
</res>
<res id="2001" name="gpu_max_freq" pair="2000" mode="1">
<default>700000000</default>
<path>/sys/class/devfreq/fde60000.gpu/max_freq</path>
<node>200000000 300000000 400000000 600000000 700000000 800000000</node>
</res>
</Resource>
</Configs>
- socperf_boost_config.xml:
<Configs>
<cmd id="10000" name="app_start">
<Action>
<duration>1500</duration>
<lit_cpu_min_freq>1992000</lit_cpu_min_freq>
</Action>
</cmd>
<cmd id="10001" name="warm_start">
<Action>
<duration>400</duration>
<lit_cpu_min_freq>1800000</lit_cpu_min_freq>
</Action>
</cmd>
<cmd id="10002" name="window_switch">
<Action>
<duration>400</duration>
<lit_cpu_min_freq>1608000</lit_cpu_min_freq>
</Action>
</cmd>
<cmd id="10006" name="click_normal">
<Action>
<duration>250</duration>
<lit_cpu_min_freq>1992000</lit_cpu_min_freq>
</Action>
</cmd>
<cmd id="10007" name="push_page_start">
<Action>
<duration>300</duration>
<lit_cpu_min_freq>1608000</lit_cpu_min_freq>
</Action>
</cmd>
<cmd id="10008" name="list_fling">
<Action>
<duration>5000</duration>
<lit_cpu_min_freq>1992000</lit_cpu_min_freq>
</Action>
</cmd>
<cmd id="10009" name="slide_normal">
<Action>
<duration>5000</duration>
<lit_cpu_min_freq>1992000</lit_cpu_min_freq>
</Action>
</cmd>
<cmd id="10010" name="touch_down_up">
<Action>
<duration>100</duration>
<lit_cpu_min_freq>1608000</lit_cpu_min_freq>
</Action>
</cmd>
<cmd id="10011" name="push_page_complete">
<Action>
<duration>200</duration>
<lit_cpu_min_freq>1416000</lit_cpu_min_freq>
</Action>
</cmd>
<cmd id="10012" name="web_gesture">
<Action>
<duration>800</duration>
<lit_cpu_min_freq>1992000</lit_cpu_min_freq>
<gpu_min_freq>800000000</gpu_min_freq>
</Action>
</cmd>
<cmd id="10016" name="pop_page">
<Action>
<duration>400</duration>
<lit_cpu_min_freq>1608000</lit_cpu_min_freq>
</Action>
</cmd>
</Configs>
name
:需要和ressched_report.cpp
中具体事件对应的string
相同。
duration
:表示提频的持续时间,在PerfRequest
接口生效。
CPU节点配置
以RK3568为参考,其CPU/GPU/DDR/NPU的配置频率均可在./kernel/linux/rk3588-5.10/arch/arm64/boot/dts/rockchip/rk3568.dtsi
中找到。

CPU节点配置以RK3588为例,GPU/DDR/NPU均与其配置过程类似。具体的配置过程如下:
- 明确产品的CPU型号及其节点目录:
RK3588 CPU:四核A76(大核:CPU4、CPU6)+四核A55(小核:CPU0),最高主频2.4G;
powershell
# CPU0:(对应4个A55:CPU0-3)
/sys/devices/system/cpu/cpu0/cpufreq/policy0
# CPU4: (对应2个A76:CPU4-5)
/sys/devices/system/cpu/cpu4/cpufreq/
# CPU6: (对应2个A76:CPU6-7)
/sys/devices/system/cpu/cpu6/cpufreq/
- 根据dtsi文件查询其支持的CPU频率参数:
cd KaihongOS/kernel/linux/rk3588-5.10/arch/arm64/boot/dts/rockchip
gedit rk3588s.dtsi # 或其他命令
在文件中搜索cluster1_opp_table
,参数根据opp-xxxx
进行配置。
3. 对应RK3588的三个CPU大小核,按照其上路径,根据其官方文档提供的频率范围设定频率。
<res id="1000" name="lit_cpu0_min_freq" pair="1001">
<default>408000</default>
<path>/sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq</path>
<node>408000 600000 816000 1008000 1200000 1416000 1608000 1800000 1992000</node>
</res>
<res id="1001" name="lit_cpu0_max_freq" pair="1000" mode="1">
<default>1800000</default>
<path>/sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq</path>
<node>408000 600000 816000 1008000 1200000 1416000 1608000 1800000 1992000</node>
</res>
<res id="1002" name="lit_cpu4_min_freq" pair="1003">
<default>408000</default>
<path>/sys/devices/system/cpu/cpufreq/policy4/scaling_min_freq</path>
<node>408000 600000 816000 1008000 1200000 1416000 1608000 1800000 2016000 2208000 2304000</node>
</res>
<res id="1003" name="lit_cpu4_max_freq" pair="1002" mode="1">
<default>2304000</default>
<path>/sys/devices/system/cpu/cpufreq/policy4/scaling_max_freq</path>
<node>408000 600000 816000 1008000 1200000 1416000 1608000 1800000 2016000 2208000 2304000</node>
</res>
<res id="1004" name="lit_cpu6_min_freq" pair="1005">
<default>408000</default>
<path>/sys/devices/system/cpu/cpufreq/policy6/scaling_min_freq</path>
<node>408000 600000 816000 1008000 1200000 1416000 1608000 1800000 2016000 2208000 2352000</node>
</res>
<res id="1005" name="lit_cpu6_max_freq" pair="1004" mode="1">
<default>2352000</default>
<path>/sys/devices/system/cpu/cpufreq/policy6/scaling_max_freq</path>
<node>408000 600000 816000 1008000 1200000 1416000 1608000 1800000 2016000 2208000 2352000</node>
</res>
源码解析
Soc统一调频插件及服务的源码解析,分为初始化、插件分发、Soc统一调频服务三个部分来进行说明。
初始化
在初始化阶段,在文件res_sched_service_ability.cpp
中调用宏RES_SCHED_SYS_ABILITY_ID
注册系统能力ResSchedServiceAbility
。
namespace ResourceSchedule {
const bool REGISTER_RESULT =
SystemAbility::MakeAndRegisterAbility(DelayedSingleton<ResSchedServiceAbility>::GetInstance().get());
ResSchedServiceAbility::ResSchedServiceAbility() : SystemAbility(RES_SCHED_SYS_ABILITY_ID, true)
{
}
ResSchedServiceAbility
被拉起来后,就会解析两个xml配置文件并初始化所有插件。
void PluginMgr::Init()
{
...
// 读取res_sched_plugin_switch.xml:从etc/ressched/res_sched_plugin_switch.xml中加载插件
if (!pluginSwitch_) {
pluginSwitch_ = make_unique<PluginSwitch>();
std::string realPath = GetRealConfigPath(PLUGIN_SWITCH_FILE_NAME);
if (realPath.empty() || !pluginSwitch_->LoadFromConfigFile(realPath)) {
RESSCHED_LOGW("%{public}s, PluginMgr load switch config file failed!", __func__);
}
}
// 读取res_sched_config.xml:从etc/ressched/res_sched_config.xml中加载插件配置
if (!configReader_) {
configReader_ = make_unique<ConfigReader>();
std::string realPath = GetRealConfigPath(CONFIG_FILE_NAME);
if (realPath.empty() || !configReader_->LoadFromCustConfigFile(realPath)) {
RESSCHED_LOGW("%{public}s, PluginMgr load config file failed!", __func__);
}
}
// 通过dlopen打开so动态库,调用onPluginInitFunc初始化插件
LoadPlugin();
RESSCHED_LOGI("PluginMgr::Init success!");
}
插件分发
RSS的所有操作都是通过业务场景访问对外暴露的外部接口ReportData
来进行的。
其他业务场景事件对SOC统一调频插件的调用:
业务场景 |
Event |
Status |
ace |
RES_TYPE_SLIDE_RECOGNIZE // ace滑动事件 |
SLIDE_OFF_EVENT = 0SLIDE_ON_EVENT = 1 |
|
RES_TYPE_WEB_GESTURE // web手势识别事件 |
|
|
RES_TYPE_CLICK_RECOGNIZE // ace手势点击识别事件 |
INVALID_EVENT = 0TOUCH_EVENT = 1CLICK_EVENT = 2 |
|
RES_TYPE_PUSH_PAGE // 页面加载 |
PUSH_PAGE_START_EVENT = 0PUSH_PAGE_COMPLETE_EVENT = 1 |
|
RES_TYPE_POP_PAGE // 页面退出 |
POP_PAGE_EVENT = 0 |
RemoteAnimation |
RES_TYPE_SHOW_REMOTE_ANIMATION // 应用过渡动画事件 |
REMOTE_ANIMATION_BEGIN = 0REMOTE_ANIMATION_END = 1 |
WindowImpl |
RES_TYPE_RESIZE_WINDOW // 调整窗口大小事件 |
WINDOW_RESIZING = 0WINDOW_RESIZE_STOP = 1 |
|
RES_TYPE_MOVE_WINDOW // 移动窗口事件 |
WINDOW_MOVING = 0WINDOW_MOVE_STOP = 1 |
AbilityManagerService |
RES_TYPE_APP_ABILITY_START // startAblity事件 |
APP_COLD_START = 0APP_WARM_START = 1 |
Cgroup转发 |
RES_TYPE_WINDOW_FOCUS // 窗口聚焦事件 |
WINDOW_FOCUS = 0WINDOW_UNFOCUS = 1 |
RSS中的所有事件见:
foundation/resourceschedule/resource_schedule_service/ressched/interfaces/innerkits/ressched_client/include/res_type.h
进入资源调度管理ResSchedMgr
之后,从ReportData
兵分两路,一路去Cgroup分组,一路去分发插件。
void ResSchedMgr::ReportData(uint32_t resType, int64_t value, const nlohmann::json& payload)
{
...
// dispatch resource async
...
mainHandler_->PostTask([this, resType, value, payload] {
// Cgroup分组
DispatchResourceInner(resType, value, payload);
// 分发插件
PluginMgr::GetInstance().DispatchResource(std::make_shared<ResData>(resType, value, payload));
});
...
}
从ResSchedMgr
DispatchResource到PluginMgr
后,会在其中调用deliverResourceToPlugin
去分发插件。
void PluginMgr::DispatchResource(const std::shared_ptr<ResData>& resData)
{
RemoveDisablePluginHandler();
...
for (const auto& libPath : iter->second) {
if (!dispatcherHandlerMap_[libPath]) {
RESSCHED_LOGE("%{public}s, failed, %{public}s dispatcher handler is stopped.", __func__,
libPath.c_str());
continue;
}
dispatcherHandlerMap_[libPath]->PostTask(
[libPath, resData, this] { deliverResourceToPlugin(libPath, resData); });
}
}
在deliverResourceToPlugin
中通过pluginDispatchFunc(resData)
来打开SOC统一调频插件的so库:libsocperf_plugin.z.so
调用插件。
void PluginMgr::deliverResourceToPlugin(const std::string& pluginLib, const std::shared_ptr<ResData>& resData)
{
...
OnDispatchResourceFunc pluginDispatchFunc = libInfo.onDispatchResourceFunc_;
if (!pluginDispatchFunc) {
RESSCHED_LOGE("%{public}s, no DispatchResourceFun !", __func__);
return;
}
auto beginTime = Clock::now();
// 打开动态链接库
pluginDispatchFunc(resData);
auto endTime = Clock::now();
int32_t costTime = (endTime - beginTime) / std::chrono::milliseconds(1);
if (costTime > DISPATCH_TIME_OUT) {
// dispatch resource use too long time, unload it
...
RepairPlugin(endTime, pluginLib, libInfo);
} else if (costTime > DISPATCH_WARNING_TIME) {
// log
}
}
在这步,代码对插件进行了一个约束:
插件的事件处理,必须快速完成。超过10ms(DISPATCH_WARNING_TIME
),框架会打印日志报告;超过50ms(DISPATCH_TIME_OUT
),框架会认为插件用时太久异常,就会调用RepairPlugin()
,在其中会disable该插件并重新enable;如果在一分钟内有三次调用该插件时的处理时间超过50ms,就会永远disable此插件。
之后,SOC统一调频插件将事件分发到SOC统一调频服务中,由其进行调频事件的处理和仲裁。
extern "C" void OnDispatchResource(const std::shared_ptr<ResData>& data)
{
SocPerfPlugin::GetInstance().DispatchResource(data);
}
SOC统一调频服务
SOC统一调频服务(SOCPERF
)主要负责cpu/gpu频率的修改,其中有两个关键类:
-
SocPerf
:解析两个xml配置文件,接收触发提频的RES_TYPE
事件,再根据事件类型向SocPerfHandler
发送对应的INNER_EVENT
事件。
-
SocPerfHandler
:接收SocPerf
发送的INNER_EVENT
,根据事件类型调用对应接口,进行调频事件处理和调频仲裁。
首先介绍其中的重要接口。
接口 |
说明 |
PerfRequest(int32_t cmdId, const std::string& msg) |
用于性能提频使用 |
PerfRequestEx(int32_t cmdId, bool onOffTag, const std::string& msg) |
用于性能提频使用且支持ON/OFF事件 |
PowerLimitBoost(bool onOffTag, const std::string& msg) |
用于限制boost无法突破功耗限频 |
ThermalLimitBoost(bool onOffTag, const std::string& msg) |
用于限制boost无法突破热限频 |
LimitRequest(int32_t clientId, const std::vector& tags, const std::vector& configs, const std::string& msg) |
用于热或功耗模块的限频且支持多项值一同设置 |
如表所示,提频接口都以cmdID为核心,将调频场景和调频参数互相关联,实现提频或者限频的功能。
带onOffTag参数的接口表示该接口支持ON/OFF的开关调频模式,一般用于生效时间不固定的长期调频事件,需要调用者手动开启或者关闭(测试),或者根据事件的状态进行触发。
msg参数为拓展字符串信息,可承载例如调用客户端pid/tid等信息。
目前由于后三个接口无实际使用场景,因此只介绍前两个提频接口。
当SOCPERF
被拉起后,插件会根据触发的事件调用对应的事件处理函数。
void SocPerfPlugin::DispatchResource(const std::shared_ptr<ResData>& data)
{
auto funcIter = functionMap.find(data->resType);
if (funcIter != functionMap.end()) {
auto function = funcIter->second;
if (function) {
// 调用函数进行对应事件处理
function(data);
}
}
}
void SocPerfPlugin::Init()
{
functionMap = {
{ RES_TYPE_WINDOW_FOCUS,
[this](const std::shared_ptr<ResData>& data) { HandleWindowFocus(data); } },
{ RES_TYPE_CLICK_RECOGNIZE,
[this](const std::shared_ptr<ResData>& data) { HandleEventClick(data); } },
{ RES_TYPE_PUSH_PAGE,
[this](const std::shared_ptr<ResData>& data) { HandlePushPage(data); } },
{ RES_TYPE_SLIDE_RECOGNIZE,
[this](const std::shared_ptr<ResData>& data) { HandleEventSlide(data); } },
{ RES_TYPE_WEB_GESTURE,
[this](const std::shared_ptr<ResData>& data) { HandleEventWebGesture(data); } },
{ RES_TYPE_POP_PAGE,
[this](const std::shared_ptr<ResData>& data) { HandlePopPage(data); } },
{ RES_TYPE_APP_ABILITY_START,
[this](const std::shared_ptr<ResData>& data) { HandleAppAbilityStart(data); } },
{ RES_TYPE_RESIZE_WINDOW,
[this](const std::shared_ptr<ResData>& data) { HandleResizeWindow(data); } },
{ RES_TYPE_MOVE_WINDOW,
[this](const std::shared_ptr<ResData>& data) { HandleMoveWindow(data); } },
{ RES_TYPE_SLIDE_NORMAL,
[this](const std::shared_ptr<ResData>& data) { HandleSlideNormal(data); } },
};
resTypes = {
RES_TYPE_WINDOW_FOCUS,
RES_TYPE_CLICK_RECOGNIZE,
RES_TYPE_PUSH_PAGE,
RES_TYPE_SLIDE_RECOGNIZE,
RES_TYPE_WEB_GESTURE,
RES_TYPE_POP_PAGE,
RES_TYPE_APP_ABILITY_START,
RES_TYPE_RESIZE_WINDOW,
RES_TYPE_MOVE_WINDOW,
RES_TYPE_SLIDE_NORMAL,
};
for (auto resType : resTypes) {
PluginMgr::GetInstance().SubscribeResource(LIB_NAME, resType);
}
RESSCHED_LOGI("SocPerfPlugin::Init success");
}
以事件RES_TYPE_WINDOW_FOCUS
为例。
首先需要在上面业务场景中提到的WindowImpl类中,在WINDOW_FOCUS
对应的回调下调用RSS提供的接口ReportData
向SOC统一调频插件分发事件,从而实现提频请求。
即,要实现窗口聚焦事件的提频,分为窗口聚焦事件发生、事件上报给RSS、框架给SOC统一调频插件分发事件、生效调频服务四个步骤。
过程如下:
调用处理窗口聚焦事件的函数后,会发送提频请求:
void SocPerfPlugin::HandleWindowFocus(const std::shared_ptr<ResData>& data)
{
if (data->value == WindowFocusStatus::WINDOW_FOCUS) {
RESSCHED_LOGI("SocPerfPlugin: socperf->WINDOW_SWITCH");
OHOS::SOCPERF::SocPerfClient::GetInstance().PerfRequest(PERF_REQUEST_CMD_ID_WINDOW_SWITCH, "");
}
}
之后调用SocPerf::PerfRequest
进行提频操作:
void SocPerf::PerfRequest(int32_t cmdId, const std::string& msg)
{
...
DoFreqActions(perfActionsInfo[cmdId], EVENT_INVALID, ACTION_TYPE_PERF);
...
}
调用SocPerf::DoFreqActions
,向SocPerfHandler
发送事件,进行调频事件处理和调频仲裁。
void SocPerf::DoFreqActions(std::shared_ptr<Actions> actions, int32_t onOff, int32_t actionType)
{
for (auto iter = actions->actionList.begin(); iter != actions->actionList.end(); iter++) {
std::shared_ptr<Action> action = *iter;
for (int32_t i = 0; i < (int32_t)action->variable.size() - 1; i += RES_ID_AND_VALUE_PAIR) {
auto handler = GetHandlerByResId(action->variable[i]);
if (!handler) {
continue;
}
auto resAction = std::make_shared<ResAction>(action->variable[i + 1], action->duration, actionType, onOff);
// 向SocPerfHandler发送对应的内部时间,进行调频处理
auto event = AppExecFwk::InnerEvent::Get(INNER_EVENT_ID_DO_FREQ_ACTION, resAction, action->variable[i]);
handler->SendEvent(event);
}
}
}
SocPerfHandler::ProcessEvent
根据传入的事件IDINNER_EVENT_ID_DO_FREQ_ACTION
进行调频事件的处理。
void SocPerfHandler::ProcessEvent(const AppExecFwk::InnerEvent::Pointer &event)
{
if (event == nullptr) {
return;
}
switch (event->GetInnerEventId()) {
...
case INNER_EVENT_ID_DO_FREQ_ACTION: {
int32_t resId = event->GetParam();
if (!IsValidResId(resId)) {
return;
}
std::shared_ptr<ResAction> resAction = event->GetSharedObject<ResAction>();
if (resAction != nullptr) {
UpdateResActionList(resId, resAction, false);
}
break;
}
...
default: {
break;
}
}
}
最终调用WriteNode()
,向设备中写入解析的xml文件中的resNode
信息。(如RK3568事件触发提频时向:/sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq
等等路径写入频率信息)
[/i][/i]