CPU Temp hardware monitor

在内核中,为了对硬件设备进行监视,内核提供了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温度监控的分析。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: TrafficMonitor是一款用于监控计算机网络流量和系统状态的软件。而CPU温度是CPU的工作温度,对于计算机硬件的稳定运行非常重要。 TrafficMonitor可以通过检测网络流量来监控计算机的网络状态,例如实时显示上传和下载速度、网络连接数等信息。它还可以监控每个进程的网络使用情况,帮助用户了解哪些进程占用了大部分的网络带宽。 而CPU温度是指CPU芯片的工作温度。CPU的高温度会导致计算机性能下降,甚至带来严重的硬件故障。因此,及时监控CPU温度非常重要。 对于TrafficMonitor来说,它并没有直接监控CPU温度的功能,它主要用于监控网络流量和进程的网络使用情况。要获取CPU温度的信息,我们可以使用其他工具或软件,例如CPU温度监控软件、硬件监控软件等。 使用这些软件,我们可以即时获得CPU的温度信息,并监控CPU的工作温度。如果CPU温度过高,可能是由于散热系统故障、过度运算等原因导致的,我们可以及时采取措施,例如清洁散热器、改善散热环境、调整CPU高负载操作等。 综上所述,TrafficMonitor是一款用于监控网络流量和系统状态的软件,无法直接获得CPU温度信息。要监控CPU温度,需要使用其他专门的软件或工具。 ### 回答2: TrafficMonitor是一种用于监控和显示计算机系统中各个硬件组件的工具,其中包括CPU温度。CPU温度监控是非常重要的,它能够帮助用户监测CPU的运行状态和性能,并及时发现任何可能的故障或问题。 TrafficMonitor可以通过读取计算机内部传感器提供的数据来获取CPU的温度信息。CPU温度是指中央处理器的温度,它是CPU运行时产生的热量的直接反映。高温可能会导致CPU的过热,从而降低系统的性能甚至损坏硬件。 通过TrafficMonitor,用户可以实时监测CPU温度并进行必要的调整,例如增加风扇的转速或降低处理器的负载。在TrafficMonitor的界面上,通常会显示CPU温度的实时数值以及温度的折线图,以便用户可以清楚地了解CPU的工作状态。 为保证计算机系统的稳定和可靠性,我们应该始终关注CPU温度,并在温度过高时采取相应的措施。TrafficMonitor作为一个监控工具,能够提供及时准确的CPU温度数据,有助于我们保持计算机的健康运行。 ### 回答3: trafficmonitor是一款用于监测网络流量的工具,它并不直接与CPU温度相关。 CPU温度是指中央处理器的温度,主要由CPU的工作负载、散热设计和附加散热装置等因素决定。 trafficmonitor通过监测网络流量提供了诸如带宽使用率、数据传输速度、连接数等信息,但它并不包含对硬件温度的监测和记录功能。 要检测和监控CPU的温度,可以使用专门的硬件监测软件,如CPUID HWMonitor、Core Temp等。这些软件可以实时显示CPU的温度、频率、功耗等参数,并提供警告功能来避免过热导致的系统故障。 对于长时间高负荷工作的计算机,特别是游戏主机或服务器,监测CPU温度是非常重要的。过高的温度可能会导致性能下降、系统稳定性问题甚至硬件损坏。因此,定期检查CPU温度并采取必要的散热措施至关重要,例如清洁散热器、改善通风条件、使用散热胶等。 总之,trafficmonitor不提供CPU温度监测功能,但我们可以使用其他专门的软件来监测和管理CPU温度,以确保计算机的正常运行和系统的稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值