cpufreq 代码分析

基础知识:

1.cpufreq 的五种模式

2.cpufreq 的框架

cpufreq 代码分析

从 drivers\cpufreq\Makefile 开始,(注:我的是linux-4.14.63)

# SPDX-License-Identifier: GPL-2.0
# CPUfreq core
obj-$(CONFIG_CPU_FREQ)          += cpufreq.o freq_table.o          # cpufreq core 代码# CPUfreq stats
obj-$(CONFIG_CPU_FREQ_STAT)             += cpufreq_stats.o          # cpufreq stats  # CPUfreq governors
obj-$(CONFIG_CPU_FREQ_GOV_PERFORMANCE)  += cpufreq_performance.o    # 对应于cpufreq 五种模式
obj-$(CONFIG_CPU_FREQ_GOV_POWERSAVE)    += cpufreq_powersave.o
obj-$(CONFIG_CPU_FREQ_GOV_USERSPACE)    += cpufreq_userspace.o
obj-$(CONFIG_CPU_FREQ_GOV_ONDEMAND) += cpufreq_ondemand.o
obj-$(CONFIG_CPU_FREQ_GOV_CONSERVATIVE) += cpufreq_conservative.o
obj-$(CONFIG_CPU_FREQ_GOV_COMMON)       += cpufreq_governor.o
obj-$(CONFIG_CPU_FREQ_GOV_ATTR_SET) += cpufreq_governor_attr_set.oobj-$(CONFIG_CPUFREQ_DT)        += cpufreq-dt.o                    # cpufreq_driver dt 表示设备树版
obj-$(CONFIG_CPUFREQ_DT_PLATDEV)    += cpufreq-dt-platdev.o
//....

驱动入口:

cpufreq-dt.c:

static struct platform_driver dt_cpufreq_platdrv = {.driver = {.name   = "cpufreq-dt",},.probe      = dt_cpufreq_probe,.remove     = dt_cpufreq_remove,
};
module_platform_driver(dt_cpufreq_platdrv);

可以看出这是个平台驱动,与之对应的平台设备的添加在cpufreq-dt-platdev.c:

static int __init cpufreq_dt_platdev_init(void)
{create_pdev:of_node_put(np);return PTR_ERR_OR_ZERO(platform_device_register_data(NULL, "cpufreq-dt",-1, data,sizeof(struct cpufreq_dt_platform_data)));
}
device_initcall(cpufreq_dt_platdev_init);

device_initcall(cpufreq_dt_platdev_init); 表示该函数在内核启动过程中会被调用。

继续看 probe 函数,dt_cpufreq_probe:

 ret = cpufreq_register_driver(&dt_cpufreq_driver);

dt_cpufreq_driver 的定义

static struct cpufreq_driver dt_cpufreq_driver = {.flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,.verify = cpufreq_generic_frequency_table_verify,.target_index = set_target,.get = cpufreq_generic_get,.init = cpufreq_init,.exit = cpufreq_exit,.ready = cpufreq_ready,.name = "cpufreq-dt",.attr = cpufreq_dt_attr,.suspend = cpufreq_generic_suspend,c
};

继续跟踪 cpufreq_register_driver

 int cpufreq_register_driver(struct cpufreq_driver *driver_data){    cpufreq_driver = driver_data;    // 1.给 cpufreq_driver 赋值  ret = subsys_interface_register(&cpufreq_interface);  // 2.注册 cpufreq_interface    // ...
}

cpufreq_interface 的定义

static struct subsys_interface cpufreq_interface = {.name       = "cpufreq",.subsys      = &cpu_subsys,.add_dev = cpufreq_add_dev,.remove_dev  = cpufreq_remove_dev,
};

subsys_interface_register 函数会调用 sif->add_dev 函数 ,即 cpufreq_add_dev

int subsys_interface_register(struct subsys_interface *sif)
{       if (sif->add_dev) {      sif->add_dev(dev, sif);      }
}

cpufreq_add_dev:

static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
{if (cpu_online(cpu)) {ret = cpufreq_online(cpu);      }
}

cpufreq_online:

static int cpufreq_online(unsigned int cpu)
{/* call driver. From then on the cpufreq must be able* to accept all calls to ->verify and ->setpolicy for this CPU*/ret = cpufreq_driver->init(policy);  // 1.调用 cpufreq_driver->init 函数 初始化policy   down_write(&policy->rwsem);  if (new_policy) {ret = cpufreq_add_dev_interface(policy);  // 创建 sys 节点 cpufreq/scaling_cur_freq  等if (ret)goto out_exit_policy;cpufreq_stats_create_table(policy);    // 创建 cpufreq/sys  stats 节点write_lock_irqsave(&cpufreq_driver_lock, flags);list_add(&policy->policy_list, &cpufreq_policy_list);  //write_unlock_irqrestore(&cpufreq_driver_lock, flags);}ret = cpufreq_init_policy(policy);   //   初始化policy 的 governorup_write(&policy->rwsem);kobject_uevent(&policy->kobj, KOBJ_ADD);/* Callback for handling stuff after policy is ready */if (cpufreq_driver->ready)cpufreq_driver->ready(policy);    // cpufreq_driver->ready , 创建了cpufreq_cooling_dev,// 与 thermal 系统关联pr_debug("initialization complete\n");return 0;}

cpufreq_driver->init -----------------> cpufreq_init ,主要是填充policy结构体中的 freq_table

static int cpufreq_init(struct cpufreq_policy *policy)
{   dev_pm_opp_of_cpumask_add_table(policy->cpus);  // 从dts 中读取 opp cpufreq tablepriv = kzalloc(sizeof(*priv), GFP_KERNEL);if (!priv) {ret = -ENOMEM;goto out_free_opp;}priv->reg_name = name;priv->opp_table = opp_table;ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);  //取出 freq_tableif (ret) {dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);goto out_free_priv;}priv->cpu_dev = cpu_dev;policy->driver_data = priv;policy->clk = cpu_clk;policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;ret = cpufreq_table_validate_and_show(policy, freq_table);  // freq_table 填入 policytransition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);if (!transition_latency)transition_latency = CPUFREQ_ETERNAL;policy->cpuinfo.transition_latency = transition_latency;policy->dvfs_possible_from_any_cpu = true;return 0;}

小结:cpufreq_driver 注册时序图:

接上面:cpufre_online -> cpufreq_init_policy , 继续跟踪

cpufreq_init_policy:

static int cpufreq_init_policy(struct cpufreq_policy *policy)
{new_policy.governor = gov;    /* set default policy */return cpufreq_set_policy(policy, &new_policy);  // cpufreq_set_policy
}

cpufreq_set_policy:

static int cpufreq_set_policy(struct cpufreq_policy *policy,struct cpufreq_policy *new_policy)
{   /* start new governor */policy->governor = new_policy->governor;ret = cpufreq_init_governor(policy);if (!ret) {ret = cpufreq_start_governor(policy);     // start_governor     }   return ret;
}

cpufreq_start_governor:

static int cpufreq_start_governor(struct cpufreq_policy *policy)
{int ret;if (cpufreq_suspended)return 0;if (!policy->governor)return -EINVAL;pr_debug("%s: for CPU %u\n", __func__, policy->cpu);if (cpufreq_driver->get && !cpufreq_driver->setpolicy)cpufreq_update_current_freq(policy);if (policy->governor->start) {ret = policy->governor->start(policy);    // governor->start  if (ret)return ret;}if (policy->governor->limits)policy->governor->limits(policy);        // governor->limitsreturn 0;
}

governor->start , 以 ondemand 为例

static struct dbs_governor od_dbs_gov = {.gov = CPUFREQ_DBS_GOVERNOR_INITIALIZER("ondemand"),.kobj_type = { .default_attrs = od_attributes },.gov_dbs_update = od_dbs_update,.alloc = od_alloc,.free = od_free,.init = od_init,.exit = od_exit,.start = od_start,
};
#define CPUFREQ_DBS_GOVERNOR_INITIALIZER(_name_)         \{                              \.name = _name_,                       \.dynamic_switching = true,                \.owner = THIS_MODULE,                 \.init = cpufreq_dbs_governor_init,            \.exit = cpufreq_dbs_governor_exit,            \.start = cpufreq_dbs_governor_start,          \.stop = cpufreq_dbs_governor_stop,            \.limits = cpufreq_dbs_governor_limits,            \}

governor->start 就对应于cpufreq_dbs_governor_start

int cpufreq_dbs_governor_start(struct cpufreq_policy *policy)
{gov->start(policy);    // gov->start  ----- od_startgov_set_update_util(policy_dbs, sampling_rate);return 0;
}
static void gov_set_update_util(struct policy_dbs_info *policy_dbs,unsigned int delay_us)
{for_each_cpu(cpu, policy->cpus) {struct cpu_dbs_info *cdbs = &per_cpu(cpu_dbs, cpu);cpufreq_add_update_util_hook(cpu, &cdbs->update_util,dbs_update_util_handler);    // dbs_update_util_handler}
}

dbs_update_util_handler

static void dbs_update_util_handler(struct update_util_data *data, u64 time,unsigned int flags)
{irq_work_queue(&policy_dbs->irq_work);   //  irq_work
}
 init_irq_work(&policy_dbs->irq_work, dbs_irq_work);INIT_WORK(&policy_dbs->work, dbs_work_handler);
static void dbs_irq_work(struct irq_work *irq_work)
{struct policy_dbs_info *policy_dbs;policy_dbs = container_of(irq_work, struct policy_dbs_info, irq_work);schedule_work_on(smp_processor_id(), &policy_dbs->work);  //---> dbs_work_handler
}
static void dbs_work_handler(struct work_struct *work)
{   gov_update_sample_delay(policy_dbs, gov->gov_dbs_update(policy));  //---> od_dbs_update
}
static unsigned int od_dbs_update(struct cpufreq_policy *policy)
{od_update(policy);  // od_updatereturn dbs_data->sampling_rate * policy_dbs->rate_mult;
}

od_update

/** Every sampling_rate, we check, if current idle time is less than 20%* (default), then we try to increase frequency. Else, we adjust the frequency* proportional to load.*/
static void od_update(struct cpufreq_policy *policy)
{/* Check for frequency increase */if (load > dbs_data->up_threshold) {     // 根据负载来动态调整cpu频率/* If switching to max speed, apply sampling_down_factor */if (policy->cur < policy->max)policy_dbs->rate_mult = dbs_data->sampling_down_factor;dbs_freq_increase(policy, policy->max);} else {/* Calculate the next frequency proportional to load */unsigned int freq_next, min_f, max_f;min_f = policy->cpuinfo.min_freq;max_f = policy->cpuinfo.max_freq;freq_next = min_f + load * (max_f - min_f) / 100;/* No longer fully busy, reset rate_mult */policy_dbs->rate_mult = 1;if (od_tuners->powersave_bias)freq_next = od_ops.powersave_bias_target(policy,freq_next,CPUFREQ_RELATION_L);__cpufreq_driver_target(policy, freq_next, CPUFREQ_RELATION_C);}
}

小结:cpu_policy 时序图

cpufreq 代码分析相关推荐

  1. 20145236《网络攻防》Exp4 恶意代码分析

    20145236<网络攻防>Exp4 恶意代码分析 一.基础问题回答 如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所有想监控下系统一天天的到底在干些什么.请设计下你想监控的操作有哪些 ...

  2. C#中类的继承 override virtual new的作用以及代码分析

    继承中override virtual new的作用 virtual 父类中需要注明允许重写的方法: override 子类中必须显示声明该方法是重写的父类中的方法: new 子类中忽略父类的已存在的 ...

  3. 2017.4.18 静态代码分析工具sonarqube+sonar-runner的安装配置及使用

    配置成功后的代码分析页面: 可以看到对复杂度.语法使用.重复度等等都做了分析,具体到了每一个方法和每一句代码. 四种使用方式: sonarqube + sonar-runner sonarqube + ...

  4. lighttpd1.4.18代码分析

    lighttpd1.4.18代码分析(八)--状态机(2)CON_STATE_READ状态 posted @ 2008-09-24 10:50 那谁 阅读(2225) | 评论 (1)  编辑 lig ...

  5. Device Tree(三):代码分析

    2019独角兽企业重金招聘Python工程师标准>>> 一.前言 Device Tree总共有三篇,分别是: 1.为何要引入Device Tree,这个机制是用来解决什么问题的?(请 ...

  6. 使用Hadoop和ELK进行业务代码分析!分分钟捉到Bug!

    大数据是计算领域的新高地,它有望提供一种方法来应对二十一世纪不断增长的数据生成.越来越多的大数据爱好者正在涌现,越来越多的公司正在采用各种大数据平台,并希望提出以客户为中心的解决方案,帮助他们在竞争激 ...

  7. 20145328 《网络对抗技术》恶意代码分析

    20145328 <网络对抗技术>恶意代码分析 ------看到这句话说明还没写完-------- 实践内容: 使用schtasks指令监控系统运行 使用sysmon工具监控系统运行 使用 ...

  8. starGAN原理代码分析

    下载: git clone https://github.com/yunjey/StarGAN.git 1 cd StarGAN/ 1 下载celebA训练数据: bash download.sh 1 ...

  9. tensorflow笔记:多层CNN代码分析

    tensorflow笔记系列:  (一) tensorflow笔记:流程,概念和简单代码注释  (二) tensorflow笔记:多层CNN代码分析  (三) tensorflow笔记:多层LSTM代 ...

  10. ARM裸机篇---启动代码分析

    ARM裸机篇---启动代码分析 先搞清楚启动代码和Bootloader的区别,启动代码是指CPU复位后到进入C语言的main函数之前需要执行的那段汇编代码. 下面的代码先暂且这样吧,没啥注释的,时间关 ...

最新文章

  1. 微信小程序Java登录流程(ssm实现具体功能和加解密隐私信息问题解决方案)
  2. VTK:PolyData之EmbedPointsIntoVolume
  3. AR与大数据 珠联璧合带来的无限想象空间
  4. 不属于前后端分离的Vue+Django的例子
  5. 需求奇葩不可怕,可怕的是变幻无常
  6. 正则化、归一化含义解析(一)
  7. iPhone降价后销量惊人 库克本周将再度访华
  8. Vagrant:将装在C盘的虚拟机移动到别的目录
  9. IMEI,手机号,身份证号格式有效性校验
  10. 华为OD(外包)社招技术二面,总结复盘
  11. FTP 通过cmd命令上传下载文件
  12. 厦门大学计算机系夏令营考什么,2018年厦门大学全校33个学院保研夏令营信息汇总,读研不止一条路...
  13. 《暗时间》读后感(一)——确实需要学习一些心理学有关的内容
  14. 2021:Check it again:Progressive Visual Question Answering via Visual Entailment通过视觉暗示进行渐进式视觉问答
  15. # Linux学习笔记
  16. python找素数程序_求素数python
  17. 快手视频艾特实操教学分享,什么是艾特脚本,评论区艾特引流脚本讲解!
  18. XSL中特殊符号详解
  19. java endpoint原理_Endpoint
  20. 大学内的云计算解决方案

热门文章

  1. 我们一起追逐过的大肥猫——tomcat部署
  2. Error Client wants topic A to have B, but our version has C. Dropping connection.
  3. linux:为xamp添加mysql的环境变量
  4. c#语言小括号里面的逗号是什么意思
  5. gin框架下参数的自定义验证小示例
  6. 对数似然比LLR公式的问题
  7. MySql安装 Staring the server出错
  8. 苹果公司的iPhone产品以及其历史
  9. requestLayout() improperly called by
  10. nuxt实现服务端渲染查看源代码显示动态接口数据