2
* thd_engine.cpp: thermal engine class implementation
4
* Copyright (C) 2012 Intel Corporation. All rights reserved.
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License version
8
* 2 or later as published by the Free Software Foundation.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21
* Author Name <Srinivas.Pandruvada@linux.intel.com>
25
/* This class acts as the parent class of all thermal engines. Main functions are:
27
* - Read cooling devices and thermal zones(sensors), which can be overridden in child
28
* - Starts a poll loop, All the thermal processing happens in this thread's context
29
* - Message processing loop
30
* - If either a poll interval is expired or notified via netlink, it schedules a
31
* a change notification on the associated cthd_zone to read and process.
36
#include <sys/types.h>
39
#include "thd_engine.h"
40
#include "thd_cdev_therm_sys_fs.h"
41
#include "thd_zone_therm_sys_fs.h"
43
static void *cthd_engine_thread(void *arg);
45
cthd_engine::cthd_engine() :
46
cdev_cnt(0), zone_count(0), sensor_count(0), parse_thermal_zone_success(
47
false), parse_thermal_cdev_success(false), poll_timeout_msec(
48
-1), wakeup_fd(0), control_mode(COMPLEMENTRY), write_pipe_fd(0), preference(
49
0), status(true), thz_last_time(0), terminate(false), genuine_intel(
50
0), has_invariant_tsc(0), has_aperf(0), proc_list_matched(
51
false), poll_interval_sec(0), poll_sensor_mask(0) {
52
thd_engine = pthread_t();
53
thd_attr = pthread_attr_t();
54
thd_cond_var = pthread_cond_t();
55
thd_cond_mutex = pthread_mutex_t();
58
cthd_engine::~cthd_engine() {
61
for (i = 0; i < sensors.size(); ++i) {
65
for (i = 0; i < zones.size(); ++i) {
69
for (i = 0; i < cdevs.size(); ++i) {
75
void cthd_engine::thd_engine_thread() {
79
thd_log_info("thd_engine_thread begin\n");
83
n = poll(poll_fds, THD_NUM_OF_POLL_FDS, poll_timeout_msec);
84
thd_log_debug("poll exit %d \n", n);
86
thd_log_warn("Write to pipe failed \n");
91
thd_log_warn("Thermal Daemon is disabled \n");
94
// Polling mode enabled. Trigger a temp change message
95
for (i = 0; i < zones.size(); ++i) {
96
cthd_zone *zone = zones[i];
97
zone->zone_temperature_notification(0, 0);
100
if (poll_fds[0].revents & POLLIN) {
102
if (kobj_uevent.check_for_event()) {
106
thd_log_debug("kobj uevent for thermal\n");
107
if ((tm - thz_last_time) >= thz_notify_debounce_interval) {
108
for (i = 0; i < zones.size(); ++i) {
109
cthd_zone *zone = zones[i];
110
zone->zone_temperature_notification(0, 0);
113
thd_log_debug("IGNORE THZ kevent\n");
118
if (poll_fds[wakeup_fd].revents & POLLIN) {
119
message_capsul_t msg;
121
thd_log_debug("wakeup fd event\n");
122
int result = read(poll_fds[wakeup_fd].fd, &msg,
123
sizeof(message_capsul_t));
125
thd_log_warn("read on wakeup fd failed\n");
126
poll_fds[wakeup_fd].revents = 0;
129
if (proc_message(&msg) < 0) {
130
thd_log_debug("Terminating thread..\n");
134
thd_log_debug("thd_engine_thread_end\n");
137
bool cthd_engine::set_preference(const int pref) {
141
int cthd_engine::thd_engine_start(bool ignore_cpuid_check) {
145
if (ignore_cpuid_check) {
146
thd_log_debug("Ignore CPU ID check for MSRs \n");
147
proc_list_matched = true;
151
// Pipe is used for communication between two processes
152
ret = pipe(wake_fds);
154
thd_log_error("Thermal sysfs: pipe creation failed %d:\n", ret);
155
return THD_FATAL_ERROR;
157
fcntl(wake_fds[0], F_SETFL, O_NONBLOCK);
158
fcntl(wake_fds[1], F_SETFL, O_NONBLOCK);
159
write_pipe_fd = wake_fds[1];
160
wakeup_fd = THD_NUM_OF_POLL_FDS - 1;
162
memset(poll_fds, 0, sizeof(poll_fds));
163
poll_fds[wakeup_fd].fd = wake_fds[0];
164
poll_fds[wakeup_fd].events = POLLIN;
165
poll_fds[wakeup_fd].revents = 0;
167
poll_timeout_msec = -1;
168
if (poll_interval_sec) {
169
thd_log_warn("Polling mode is enabled: %d\n", poll_interval_sec);
170
poll_timeout_msec = poll_interval_sec * 1000;
173
ret = read_thermal_sensors();
174
if (ret != THD_SUCCESS) {
175
thd_log_error("Thermal sysfs Error in reading sensors\n");
176
// This is a fatal error and daemon will exit
177
return THD_FATAL_ERROR;
180
ret = read_cooling_devices();
181
if (ret != THD_SUCCESS) {
182
thd_log_error("Thermal sysfs Error in reading cooling devs\n");
183
// This is a fatal error and daemon will exit
184
return THD_FATAL_ERROR;
187
ret = read_thermal_zones();
188
if (ret != THD_SUCCESS) {
189
thd_log_error("No thermal sensors found\n");
190
// This is a fatal error and daemon will exit
191
return THD_FATAL_ERROR;
194
// Check if polling is disabled and sensors don't support
195
// async mode, in that enable force polling
196
if (!poll_interval_sec) {
198
for (i = 0; i < zones.size(); ++i) {
199
cthd_zone *zone = zones[i];
200
if (!zone->zone_active_status())
202
if (!zone->check_sensor_async_status()) {
204
"Polling will be enabled as some sensors are not capable to notify asynchnously \n");
205
poll_timeout_msec = def_poll_interval;
209
if (i == zones.size()) {
210
thd_log_info("Proceed without polling mode! \n");
214
poll_fds[0].fd = kobj_uevent.kobj_uevent_open();
215
if (poll_fds[0].fd < 0) {
216
thd_log_warn("Invalid kobj_uevent handle\n");
219
thd_log_info("FD = %d\n", poll_fds[0].fd);
220
kobj_uevent.register_dev_path(
221
(char *) "/devices/virtual/thermal/thermal_zone");
222
poll_fds[0].events = POLLIN;
223
poll_fds[0].revents = 0;
225
#ifndef DISABLE_PTHREAD
226
// condition variable
227
pthread_cond_init(&thd_cond_var, NULL);
228
pthread_mutex_init(&thd_cond_mutex, NULL);
230
pthread_attr_init(&thd_attr);
231
pthread_attr_setdetachstate(&thd_attr, PTHREAD_CREATE_DETACHED);
232
ret = pthread_create(&thd_engine, &thd_attr, cthd_engine_thread,
238
if((childpid = fork()) == - 1)
246
/* Child process closes up input side of pipe */
248
cthd_engine_thread((void*)this);
252
/* Parent process closes up output side of pipe */
257
preference = thd_pref.get_preference();
258
thd_log_info("Current user preference is %d\n", preference);
260
if (control_mode == EXCLUSIVE) {
261
thd_log_info("Control is taken over from kernel\n");
262
takeover_thermal_control();
267
int cthd_engine::thd_engine_stop() {
271
static void *cthd_engine_thread(void *arg) {
272
cthd_engine *obj = (cthd_engine*) arg;
274
obj->thd_engine_thread();
279
void cthd_engine::send_message(message_name_t msg_id, int size,
280
unsigned char *msg) {
281
message_capsul_t msg_cap;
283
memset(&msg_cap, 0, sizeof(message_capsul_t));
285
msg_cap.msg_id = msg_id;
286
msg_cap.msg_size = (size > MAX_MSG_SIZE) ? MAX_MSG_SIZE : size;
288
memcpy(msg_cap.msg, msg, msg_cap.msg_size);
289
int result = write(write_pipe_fd, &msg_cap, sizeof(message_capsul_t));
291
thd_log_warn("Write to pipe failed \n");
294
void cthd_engine::process_pref_change() {
298
new_pref = thd_pref.get_preference();
299
if (new_pref == PREF_DISABLED) {
304
if (preference != new_pref) {
305
thd_log_warn("Preference changed \n");
307
preference = new_pref;
308
for (unsigned int i = 0; i < zones.size(); ++i) {
309
cthd_zone *zone = zones[i];
310
zone->update_zone_preference();
313
if (control_mode == EXCLUSIVE) {
314
thd_log_info("Control is taken over from kernel\n");
315
takeover_thermal_control();
319
void cthd_engine::thd_engine_terminate() {
320
send_message(TERMINATE, 0, NULL);
325
int cthd_engine::thd_engine_set_user_max_temp(const char *zone_type,
326
const char *user_set_point) {
327
std::string str(user_set_point);
330
thd_log_debug("thd_engine_set_user_set_point %s\n", user_set_point);
333
if (std::isdigit(str[0], loc) == 0) {
334
thd_log_warn("thd_engine_set_user_set_point Invalid set point\n");
338
zone = get_zone(zone_type);
340
thd_log_warn("thd_engine_set_user_set_point Invalid zone\n");
343
return zone->update_max_temperature(atoi(user_set_point));
346
void cthd_engine::thermal_zone_change(message_capsul_t *msg) {
348
thermal_zone_notify_t *pmsg = (thermal_zone_notify_t*) msg->msg;
349
for (unsigned i = 0; i < zones.size(); ++i) {
350
cthd_zone *zone = zones[i];
351
if (zone->zone_active_status())
352
zone->zone_temperature_notification(pmsg->type, pmsg->data);
354
thd_log_debug("zone is not active\n");
359
void cthd_engine::poll_enable_disable(bool status, message_capsul_t *msg) {
360
unsigned int *sensor_id = (unsigned int*) msg->msg;
363
poll_sensor_mask |= (1 << (*sensor_id));
364
poll_timeout_msec = def_poll_interval;
365
thd_log_debug("thd_engine polling enabled via %u \n", *sensor_id);
367
poll_sensor_mask &= ~(1 << (*sensor_id));
368
if (!poll_sensor_mask) {
369
poll_timeout_msec = -1;
370
thd_log_debug("thd_engine polling last disabled via %u \n",
376
int cthd_engine::proc_message(message_capsul_t *msg) {
379
thd_log_debug("Receieved message %d\n", msg->msg_id);
380
switch (msg->msg_id) {
384
thd_log_warn("Terminating ...\n");
390
process_pref_change();
392
case THERMAL_ZONE_NOTIFY:
394
thd_log_warn("Thermal Daemon is disabled \n");
397
thermal_zone_change(msg);
404
thd_engine_reload_zones();
407
if (!poll_interval_sec) {
408
poll_enable_disable(true, msg);
412
if (!poll_interval_sec) {
413
poll_enable_disable(false, msg);
423
cthd_cdev *cthd_engine::thd_get_cdev_at_index(int index) {
424
for (int i = 0; i < cdev_cnt; ++i) {
425
if (cdevs[i]->thd_cdev_get_index() == index)
431
void cthd_engine::takeover_thermal_control() {
432
csys_fs sysfs("/sys/class/thermal/");
435
struct dirent *entry;
436
const std::string base_path = "/sys/class/thermal/";
438
thd_log_info("Taking over thermal control \n");
439
if ((dir = opendir(base_path.c_str())) != NULL) {
440
while ((entry = readdir(dir)) != NULL) {
441
if (!strncmp(entry->d_name, "thermal_zone",
442
strlen("thermal_zone"))) {
445
i = atoi(entry->d_name + strlen("thermal_zone"));
446
std::stringstream policy;
447
std::stringstream type;
448
std::string type_val;
449
std::string curr_policy;
451
type << "thermal_zone" << i << "/type";
452
if (sysfs.exists(type.str().c_str())) {
453
sysfs.read(type.str(), type_val);
455
policy << "thermal_zone" << i << "/policy";
456
if (sysfs.exists(policy.str().c_str())) {
457
sysfs.read(policy.str(), curr_policy);
458
zone_preferences.push_back(curr_policy);
459
sysfs.write(policy.str(), "user_space");
467
void cthd_engine::giveup_thermal_control() {
468
if (control_mode != EXCLUSIVE)
471
if (zone_preferences.size() == 0)
474
thd_log_info("Giving up thermal control \n");
476
csys_fs sysfs("/sys/class/thermal/");
479
struct dirent *entry;
480
const std::string base_path = "/sys/class/thermal/";
482
if ((dir = opendir(base_path.c_str())) != NULL) {
483
while ((entry = readdir(dir)) != NULL) {
484
if (!strncmp(entry->d_name, "thermal_zone",
485
strlen("thermal_zone"))) {
488
i = atoi(entry->d_name + strlen("thermal_zone"));
489
std::stringstream policy;
490
std::stringstream type;
491
std::string type_val;
493
type << "thermal_zone" << i << "/type";
494
if (sysfs.exists(type.str().c_str())) {
495
sysfs.read(type.str(), type_val);
497
policy << "thermal_zone" << i << "/policy";
498
if (sysfs.exists(policy.str().c_str())) {
499
sysfs.write(policy.str(), zone_preferences[cnt++]);
507
void cthd_engine::process_terminate() {
508
thd_log_warn("terminating on user request ..\n");
509
giveup_thermal_control();
513
void cthd_engine::thd_engine_poll_enable(int sensor_id) {
514
send_message(POLL_ENABLE, (int) sizeof(sensor_id),
515
(unsigned char*) &sensor_id);
518
void cthd_engine::thd_engine_poll_disable(int sensor_id) {
519
send_message(POLL_DISABLE, (int) sizeof(sensor_id),
520
(unsigned char*) &sensor_id);
523
void cthd_engine::thd_engine_reload_zones() {
524
thd_log_warn(" Reloading zones\n");
525
for (unsigned int i = 0; i < zones.size(); ++i) {
526
cthd_zone *zone = zones[i];
531
int ret = read_thermal_zones();
532
if (ret != THD_SUCCESS) {
533
thd_log_error("No thermal sensors found\n");
534
// This is a fatal error and daemon will exit
539
// Add any tested platform ids in this table
540
static supported_ids_t id_table[] = { { 6, 0x2a }, // Sandybridge
541
{ 6, 0x2d }, // Sandybridge
542
{ 6, 0x3a }, // IvyBridge
543
{ 6, 0x3c }, { 6, 0x3e }, { 6, 0x3f }, { 6, 0x45 }, // Haswell ULT */
544
{ 6, 0x46 }, // Haswell ULT */
546
{ 0, 0 } // Last Invalid entry
549
int cthd_engine::check_cpu_id() {
551
// Copied from turbostat program
552
unsigned int ebx, ecx, edx, max_level;
553
unsigned int fms, family, model, stepping;
558
proc_list_matched = false;
561
__cpuid(0, max_level, ebx, ecx, edx);
562
if (ebx == 0x756e6547 && edx == 0x49656e69 && ecx == 0x6c65746e)
564
if (genuine_intel == 0) {
565
// Simply return without further capability check
568
__cpuid(1, fms, ebx, ecx, edx);
569
family = (fms >> 8) & 0xf;
570
model = (fms >> 4) & 0xf;
571
stepping = fms & 0xf;
572
if (family == 6 || family == 0xf)
573
model += ((fms >> 16) & 0xf) << 4;
576
"%d CPUID levels; family:model:stepping 0x%x:%x:%x (%d:%d:%d)\n",
577
max_level, family, model, stepping, family, model, stepping);
579
while (id_table[i].family) {
580
if (id_table[i].family == family && id_table[i].model == model) {
581
proc_list_matched = true;
588
thd_log_warn(" No support RAPL and Intel P state driver\n");
591
if (!(edx & (1 << 5))) {
592
thd_log_warn("No MSR supported on processor \n");
598
void cthd_engine::thd_read_default_thermal_sensors() {
600
struct dirent *entry;
601
const std::string base_path = "/sys/class/thermal/";
603
thd_log_debug("thd_read_default_thermal_sensors \n");
604
if ((dir = opendir(base_path.c_str())) != NULL) {
605
while ((entry = readdir(dir)) != NULL) {
606
if (!strncmp(entry->d_name, "thermal_zone",
607
strlen("thermal_zone"))) {
609
i = atoi(entry->d_name + strlen("thermal_zone"));
610
cthd_sensor *sensor = new cthd_sensor(i,
611
base_path + entry->d_name + "/", "");
612
if (sensor->sensor_update() != THD_SUCCESS)
614
sensors.push_back(sensor);
619
sensor_count = sensors.size();
620
thd_log_info("thd_read_default_thermal_sensors loaded %d sensors \n",
624
void cthd_engine::thd_read_default_thermal_zones() {
626
struct dirent *entry;
627
const std::string base_path = "/sys/class/thermal/";
629
thd_log_debug("thd_read_default_thermal_zones \n");
630
if ((dir = opendir(base_path.c_str())) != NULL) {
631
while ((entry = readdir(dir)) != NULL) {
632
if (!strncmp(entry->d_name, "thermal_zone",
633
strlen("thermal_zone"))) {
635
i = atoi(entry->d_name + strlen("thermal_zone"));
636
cthd_sysfs_zone *zone = new cthd_sysfs_zone(i,
637
"/sys/class/thermal/thermal_zone");
638
if (zone->zone_update() != THD_SUCCESS)
640
if (control_mode == EXCLUSIVE)
641
zone->set_zone_active();
642
zones.push_back(zone);
647
zone_count = zones.size();
648
thd_log_info("thd_read_default_thermal_zones loaded %d zones \n",
652
void cthd_engine::thd_read_default_cooling_devices() {
655
struct dirent *entry;
656
const std::string base_path = "/sys/class/thermal/";
658
thd_log_debug("thd_read_default_cooling devices \n");
659
if ((dir = opendir(base_path.c_str())) != NULL) {
660
while ((entry = readdir(dir)) != NULL) {
661
if (!strncmp(entry->d_name, "cooling_device",
662
strlen("cooling_device"))) {
664
i = atoi(entry->d_name + strlen("cooling_device"));
665
cthd_sysfs_cdev *cdev = new cthd_sysfs_cdev(i,
666
"/sys/class/thermal/");
667
if (cdev->update() != THD_SUCCESS)
669
cdevs.push_back(cdev);
674
cdev_cnt = cdevs.size();
675
thd_log_info("thd_read_default_cooling devices loaded %d cdevs \n",
679
cthd_zone* cthd_engine::search_zone(std::string name) {
682
for (unsigned int i = 0; i < zones.size(); ++i) {
686
if (zone->get_zone_type() == name)
693
cthd_cdev* cthd_engine::search_cdev(std::string name) {
696
for (unsigned int i = 0; i < cdevs.size(); ++i) {
700
if (cdev->get_cdev_type() == name)
707
cthd_sensor* cthd_engine::search_sensor(std::string name) {
710
for (unsigned int i = 0; i < sensors.size(); ++i) {
714
if (sensor->get_sensor_type() == name)
721
cthd_sensor* cthd_engine::get_sensor(int index) {
722
if (index < (int) sensors.size())
723
return sensors[index];
728
cthd_zone* cthd_engine::get_zone(int index) {
731
if (index < (int) zones.size())
737
cthd_zone* cthd_engine::get_zone(std::string type) {
740
for (unsigned int i = 0; i < zones.size(); ++i) {
742
if (zone->get_zone_type() == type)