使用 thinkfan 管理 T420s 风扇
小黑在升级到 Kubuntu 18.04 后出现了一个比较诡异的问题:挂起唤醒之后有比较大概率会出现主屏分辨率错误,只能在重启后重新设置主副屏才会恢复正常。由于暂时未找到问题的原因,只能让小黑保持24小时待机的方式来规避问题。
24小时待机又引出了另一个问题,T420s 的风扇管理基本等于没有,在待机状态下仍然会保持4000转速,风扇噪音在晚上格外刺耳。于是只能祭出 thinkfan,手动设置风扇的转速。
安装 thinkfan
$ sudo apt install thinkfan lm-sensors
$ sensors
coretemp-isa-0000
Adapter: ISA adapter
Package id 0: +49.0°C (high = +86.0°C, crit = +100.0°C)
Core 0: +48.0°C (high = +86.0°C, crit = +100.0°C)
Core 1: +49.0°C (high = +86.0°C, crit = +100.0°C)
查找 sensors 文件
$ find /sys -type f -name "temp*_input"
/sys/devices/platform/coretemp.0/hwmon/hwmon2/temp3_input
/sys/devices/platform/coretemp.0/hwmon/hwmon2/temp1_input
/sys/devices/platform/coretemp.0/hwmon/hwmon2/temp2_input
/sys/devices/virtual/hwmon/hwmon0/temp1_input
列表里 coretemp.0 目录下的 input 文件对应了上面 sensors 输出的 CPU 以及各个核心的温度来源,也是 thinkfan 需要监控的目标。
配置 thinkfan
$ cat /etc/thinkfan.conf
tp_fan /proc/acpi/ibm/fan
hwmon /sys/devices/platform/coretemp.0/hwmon/hwmon2/temp1_input
(0, 0, 42)
(1, 40, 47)
(2, 45, 52)
(3, 50, 57)
(4, 55, 62)
(5, 60, 77)
(7, 73, 32767)
我在设置里选择了 thinkfan 的简单模式,tp_fan
参数是需要控制的风扇,hwmon
则是需要监控的温度,(0, 0, 42)
表示 (风扇转速等级, 最低温度, 最高温度)
。假如当前温度为55度,那么 thinkfan 会将风扇等级设置为 3,温度下降到 41度后,thinkfan 会将风扇等级自动设置为 1。
如果需要为多个 sensor 设置不同的监控参数,可以选择复杂模式:
tp_fan /proc/acpi/ibm/fan
hwmon /sys/devices/platform/coretemp.0/hwmon/hwmon2/temp1_input
hwmon /sys/devices/platform/coretemp.0/hwmon/hwmon2/temp2_input
hwmon /sys/devices/platform/coretemp.0/hwmon/hwmon2/temp3_input
{ "level 0" # the fan level
# 1 2 3
# =======
(0 0 0 ) # LOWER limit
(43 42 42) # UPPER limit
}
{ "level 1" # the fan level
# 1 2 3
# =======
(40 40 40) # LOWER limit
(48 47 46) # UPPER limit
}
完整的复杂模式设置方法可以参考:thinkfan.conf.complex。
设置 thinkpad_acpi modprobe
$ sudo echo "options thinkpad_acpi fan_control=1" | sudo tee /etc/modprobe.d/thinkfan.conf
重启后开启 thinkfan 服务
$ systemctl start thinkfan.service
到这里小黑的风扇终于消停,在待机状态下基本听不到噪音了。
简单模式和复杂模式的区别
首先要明确一点,不论简单模式还是复杂模式,thinkfan 都只能控制一个风扇的转速,如果需要控制多个风扇,需要启动不同的进程并设置对应的配置文件。
可以通过查看源码中了解 thinkfan 在调速时的基本逻辑:
//@see https://github.com/vmatare/thinkfan/blob/master/src/thinkfan.cpp#L114
while (likely(!interrupted)) {
std::this_thread::sleep_for(sleeptime);
temp_state.restart();
// 读取所有 sensors 的当前温度
for (const SensorDriver *sensor : config.sensors())
sensor->read_temps();
if (unlikely(!temp_state.complete()))
throw SystemError(MSG_SENSOR_LOST);
// 当前转速等级不是最高且温度超过当前等级温度上限(UPPER limit),增加转速等级
if (unlikely(cur_lvl != --config.levels().end() && (*cur_lvl)->up())) {
while (cur_lvl != --config.levels().end() && (*cur_lvl)->up())
cur_lvl++;
log(TF_INF) << temp_state << " -> " <<
(*cur_lvl)->str() << flush;
config.fan()->set_speed(*cur_lvl);
}
// 当前转速等级不是最低且温度低于当前等级温度下限(LOWER limit),降低转速等级
else if (unlikely(cur_lvl != config.levels().begin() && (*cur_lvl)->down())) {
while (cur_lvl != config.levels().begin() && (*cur_lvl)->down())
cur_lvl--;
log(TF_INF) << temp_state << " -> " <<
(*cur_lvl)->str() << flush;
config.fan()->set_speed(*cur_lvl);
tmp_sleeptime = sleeptime;
}
else {
config.fan()->ping_watchdog_and_depulse(*cur_lvl);
#ifdef DEBUG
log(TF_DBG) << temp_state << flush;
#endif
}
}
简单模式和复杂模式的最大区别就在于 (*cur_lvl)->up()
和 (*cur_lvl)->down()
这两个温度上下限判断逻辑:
//@see https://github.com/vmatare/thinkfan/blob/master/src/config.cpp#L282
bool SimpleLevel::up() const
{ return *temp_state.tmax >= upper_limit().front(); }
bool SimpleLevel::down() const
{ return *temp_state.tmax < lower_limit().front(); }
//@see https://github.com/vmatare/thinkfan/blob/master/src/config.cpp#L300
bool ComplexLevel::up() const
{
std::vector<int>::const_iterator temp_it = temp_state.biased_temps().begin();
const int *upper_idx = upper_limit().data();
while (temp_it != temp_state.biased_temps().end())
if (*temp_it++ >= *upper_idx++) return true;
return false;
}
bool ComplexLevel::down() const
{
std::vector<int>::const_iterator temp_it = temp_state.biased_temps().begin();
const int *lower_idx = lower_limit().data();
while (temp_it != temp_state.biased_temps().end() && *temp_it < *lower_idx) {
temp_it++;
lower_idx++;
}
return temp_it == temp_state.biased_temps().end();
}
复杂模式下和上下限温度相比较的是每个 sensor 的 biased_temp_
,而非 tmax
。只要 temp_state.biased_temps()
中任意一个值高于其对应 UPPER limit
都会调高风扇转速,对应的,需要 temp_state.biased_temps()
的所有值都低于其对应 LOWER limit
时才会降低转速。