博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CPU Temp hardware monitor
阅读量:4095 次
发布时间:2019-05-25

本文共 7934 字,大约阅读时间需要 26 分钟。

在内核中,为了对硬件设备进行监视,内核提供了hwmon(hardware monitor)方法,来方便对硬件设备的监视。

比如:为了对处理器的温度进行监控,可以利用该方法来完成。

static int __init xxx_hwmon_init(void){
int ret; pr_info("xxx Hwmon Enter...\n"); if (cpu_has_csr()) csr_temp_enable = csr_readl(XXX_CSR_FEATURE) & XXX_CSRF_TEMP; cpu_hwmon_dev = hwmon_device_register(NULL); //该函数用来注册并返回cpu_hwmon_dev if (IS_ERR(cpu_hwmon_dev)) {
ret = PTR_ERR(cpu_hwmon_dev); pr_err("hwmon_device_register fail!\n"); goto fail_hwmon_device_register; } ...}

关于cpu_has_csr,该函数与处理器的结构相关,这里对其进行解析:

static inline bool cpu_has_csr(void){
if (cpu_has_cfg()) //检查处理中是否存在相关的配置寄存器 return (read_cpucfg(XXX_CFG2) & XXX_CFG2_LCSRP); //读取处理器中的相关寄存器的配置 return false;}static inline bool cpu_has_cfg(void){
return ((read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_XXX_64G);}#define read_c0_prid() __read_const_32bit_c0_register($15, 0)#define __read_const_32bit_c0_register(source, sel) \ ___read_32bit_c0_register(source, sel,)#define ___read_32bit_c0_register(source, sel, vol) \({ unsigned int __res; \ if (sel == 0) \ __asm__ vol( \ "mfc0\t%0, " #source "\n\t" \ : "=r" (__res)); \ else \ __asm__ vol( \ ".set\tpush\n\t" \ ".set\tmips32\n\t" \ "mfc0\t%0, " #source ", " #sel "\n\t" \ ".set\tpop\n\t" \ : "=r" (__res)); \ __res; \})

通过上述代码可以看出,在sel == 0的情况下,通过指令mfc将$15(即15号寄存器,CP0)中的内容写入res变量中,随后将改变量与PRID_IMP_MASK宏进行匹配,判断res变量中是否包含PRID_IMP_XXX_64G。这里,分析的处理器架构为MIPS架构。

CP0寄存器的结构如下:

±-----------------------±-----------------±-----------------±-----------+
| Company Options | Company ID | Processor ID | Revision |
±-----------------------±-----------------±-----------------±-----------+
31 24 23 16 15 8 7 0

#define PRID_IMP_MASK 0Xff00#define PRID_IMP_XXX_64G 0xc000

所以,cpu_has_cfg实际用来判断是否存在Company Options。

关于另一个与处理器结构相关的read_cpucfg函数,其原型如下:

static inline u32 read_cpucfg(u32 reg){
u32 __res; __asm__ __volatile__( "parse_r __res,%0\n\t" "parse_r reg,%1\n\t" ".insn \n\t" ".word (0xc8080118 | (reg << 21) | (__res << 11))\n\t" :"=r"(__res) :"r"(reg) : ); return __res;}#define XXX_CFG2 0x2#define XXX_CFG2_LCSRP BIT(27)#define BIT(nr) (UL(1) << (nr))

综上分析,read_cpucfg()函数的执行结果为1。所以,csr_temp_enable为1。

hwmon_device_register()函数实际的执行者为__hwmon_device_register()。

static struct device *__hwmon_device_register(struct device *dev, const char *name, void *drvdata,			const struct hwmon_chip_info *chip,			const struct attribute_group **groups){
//在实际的代码中,这里传入的参数全部为NULL。因此,关于部分分支条件可以暂且忽略。 struct hwmon_device *hwdev; struct device *hdev; int i, j, err, id; //检查name变量的长度以及是否包含字符“-”。 if (name && (!strlen(name) || strpbrk(name, "-* \t\n"))) dev_warn(dev, "hwmon: '%s' is not a valid name attribute, please fix\n", name); //获取id编号 id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL); if (id < 0) return ERR_PTR(id); //申请hwmon_device结构体,并将其内容清零。 hwdev = kzalloc(sizeof(*hwdev), GFP_KERNEL); if (hwdev == NULL) {
err = -ENOMEM; goto ida_remove; } //对hwdev以及hdev结构体对象进行初始化赋值。 hdev = &hwdev->dev; if (chip) {
... } else {
hdev->groups = groups; } ... hwdev->name = name; hdev->class = &hwmon_class; hdev->parent = dev; hdev->of_node = dev ? dev->of_node : NULL; hwdev->chip = chip; //hdev->driver_data = drvdata; dev_set_drvdata(hdev, drvdata); //sprintf(hdev->kobj->name, ”hwmon%d“,id); dev_set_name(hdev, HWMON_ID_FORMAT, id); //注册hdev设备。该函数首先对device结构体对象hdev进行初始化(device_initialize(dev)),随后添加该结构体(device_add(dev))。 err = device_register(hdev); if (err) goto free_hwmon; return hdev;free_hwmon: hwmon_dev_release(hdev);ida_remove: ida_simple_remove(&hwmon_ida, id); return ERR_PTR(err);}

关于device_register函数,其内部实现的内容较多且复杂,这里只对设备的属性来进行分析,即device_add_attrs()函数。

//添加与设备相关的属性文件。static int device_add_attrs(struct device *dev)//这里所传入的参数实际为上边代码中的hdev{
//对hdev的初始化过程中,hdev->class = &hwmon_class。 struct class *class = dev->class; ... if (class) {
//参数实际分别为dev=》hdev, class->dev_groups=》hwmon_dev_attr_groups。 //该函数接下来会遍历hwmon_dev_attr_groups来执行internal_create_group()函数。 error = device_add_groups(dev, class->dev_groups); if (error) return error; }}//hwmon_dev_attr_groups的定义如下:static const struct attribute_group *hwmon_dev_attr_groups[] = {
&hwmon_dev_attr_group, NULL};static const struct attribute_group hwmon_dev_attr_group = {
.attrs = hwmon_dev_attrs, .is_visible = hwmon_dev_name_is_visible,};static struct attribute *hwmon_dev_attrs[] = {
&dev_attr_name.attr, NULL};static int internal_create_groups(struct kobject *kobj, int update, const struct attribute_group **groups){
int error = 0; int i; if (!groups) return 0; for (i = 0; groups[i]; i++) {
//通过上述结构体的声明,可以知道该循环中只执行一次,kobj=》hdev->kobj,update=》0, groups[i]=》hwmon_dev_attr_group //该函数执行create_files()函数来创建相关文件。 error = internal_create_group(kobj, update, groups[i]); if (error) {
while (--i >= 0) sysfs_remove_group(kobj, groups[i]); break; } } return error;}static int create_files(struct kernfs_node *parent, struct kobject *kobj, kuid_t uid, kgid_t gid, const struct attribute_group *grp, int update){
struct attribute *const *attr; struct bin_attribute *const *bin_attr; int error = 0, i; //由上述结构体可以知道grp->attrs为真,因此进入该条件分支。 if (grp->attrs) {
//由前边可知,ARRAY_SIZE(grp->attrs)=1,所以该循环只遍历一次。 for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) {
umode_t mode = (*attr)->mode; //update此时为0,所以不执行该分支 if (update) kernfs_remove_by_name(parent, (*attr)->name); //此时grp->is_visible为真,执行该分支。 if (grp->is_visible) {
//该函数通过kobj找到hdev,随后通过hdev找hwdev,并判断hwdev的name属性是否有值,如果无值则继续循环。 //如果有值,则继续向下执行。 mode = grp->is_visible(kobj, *attr, i); if (!mode) continue; } ... //添加文件,该函数根据传入的第三个参数来选择执行相对应的条件分支。 error = sysfs_add_file_mode_ns(parent, *attr, false, mode, uid, gid, NULL); if (unlikely(error)) break; } if (error) {
remove_files(parent, grp); goto exit; } } if (grp->bin_attrs) {
... }exit: return error;}int sysfs_add_file_mode_ns(struct kernfs_node *parent, const struct attribute *attr, bool is_bin, umode_t mode, kuid_t uid, kgid_t gid, const void *ns){
struct lock_class_key *key = NULL; const struct kernfs_ops *ops; struct kernfs_node *kn; loff_t size; //对sysfs_ops结构体进行赋值。 if (!is_bin) {
struct kobject *kobj = parent->priv; const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops; if (WARN(!sysfs_ops, KERN_ERR "missing sysfs attribute operations for kobject: %s\n", kobject_name(kobj))) return -EINVAL; if (sysfs_ops->show && sysfs_ops->store) {
if (mode & SYSFS_PREALLOC) ops = &sysfs_prealloc_kfops_rw; else ops = &sysfs_file_kfops_rw; } else if (sysfs_ops->show) {
if (mode & SYSFS_PREALLOC) ops = &sysfs_prealloc_kfops_ro; else ops = &sysfs_file_kfops_ro; } else if (sysfs_ops->store) {
if (mode & SYSFS_PREALLOC) ops = &sysfs_prealloc_kfops_wo; else ops = &sysfs_file_kfops_wo; } else ops = &sysfs_file_kfops_empty; size = PAGE_SIZE; } else {
... }#ifdef CONFIG_DEBUG_LOCK_ALLOC if (!attr->ignore_lockdep) key = attr->key ?: (struct lock_class_key *)&attr->skey;#endif //创建文件。 kn = __kernfs_create_file(parent, attr->name, mode & 0777, uid, gid, size, ops, (void *)attr, ns, key); if (IS_ERR(kn)) {
if (PTR_ERR(kn) == -EEXIST) sysfs_warn_dup(parent, attr->name); return PTR_ERR(kn); } return 0;}

综上,便是处理器温度监控的必要的初始动作,而关于真正的温度监控动作,其执行如下:

static int __init loongson_hwmon_init(void){
... INIT_DEFERRABLE_WORK(&thermal_work, do_thermal_timer); schedule_delayed_work(&thermal_work, msecs_to_jiffies(20000)); ...}static void do_thermal_timer(struct work_struct *work){
int i, value, temp_max = 0; for (i = 0; i < nr_packages; i++) {
value = xxx_cpu_temp(i); //读取cpu的温度 if (value > temp_max) temp_max = value; } if (temp_max <= CPU_THERMAL_THRESHOLD) schedule_delayed_work(&thermale_work, msecs_to_jiffies(5000)); else orderly_poweroff(true);}

以上,便是对cpu温度监控的分析。

转载地址:http://zpxii.baihongyu.com/

你可能感兴趣的文章
Spring Boot构建简单的微博应用
查看>>
Spring处理表单提交
查看>>
Spring MVC异常处理
查看>>
Leetcode 1180. Count Substrings with Only One Distinct Letter [Python]
查看>>
PHP 7 的五大新特性
查看>>
php使用 memcache 来存储 session
查看>>
php实现socket(转)
查看>>
PHP底层的运行机制与原理
查看>>
深入了解php底层机制
查看>>
PHP中的stdClass 【转】
查看>>
XHProf-php轻量级的性能分析工具
查看>>
PHP7新特性 What will be in PHP 7/PHPNG
查看>>
比较strtr, str_replace和preg_replace三个函数的效率
查看>>
ubuntu 下编译PHP5.5.7问题:configure: error: freetype.h not found.
查看>>
PHP编译configure时常见错误 debian centos
查看>>
configure: error: Please reinstall the BZip2 distribution
查看>>
OpenCV gpu模块样例注释:video_reader.cpp
查看>>
【增强学习在无人驾驶中的应用】
查看>>
《python+opencv实践》四、图像特征提取与描述——29理解图像特征
查看>>
《python+opencv实践》四、图像特征提取与描述——30Harris 角点检测
查看>>