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>
38
#include "thd_engine.h"
39
#include "thd_cdev_therm_sys_fs.h"
40
#include "thd_zone_therm_sys_fs.h"
42
static void *cthd_engine_thread(void *arg);
44
cthd_engine::cthd_engine() :
45
cdev_cnt(0), zone_count(0), sensor_count(0), parse_thermal_zone_success(
46
false), parse_thermal_cdev_success(false), poll_timeout_msec(
47
-1), wakeup_fd(0), control_mode(COMPLEMENTRY), write_pipe_fd(0), preference(
48
0), status(true), thz_last_time(0), terminate(false), genuine_intel(
49
0), has_invariant_tsc(0), has_aperf(0), proc_list_matched(
50
false), poll_interval_sec(0), poll_sensor_mask(0) {
51
thd_engine = pthread_t();
52
thd_attr = pthread_attr_t();
53
thd_cond_var = pthread_cond_t();
54
thd_cond_mutex = pthread_mutex_t();
57
cthd_engine::~cthd_engine() {
60
for (i = 0; i < sensors.size(); ++i) {
64
for (i = 0; i < zones.size(); ++i) {
68
for (i = 0; i < cdevs.size(); ++i) {
74
void cthd_engine::thd_engine_thread() {
78
thd_log_info("thd_engine_thread begin\n");
82
n = poll(poll_fds, THD_NUM_OF_POLL_FDS, poll_timeout_msec);
83
thd_log_debug("poll exit %d \n", n);
85
thd_log_warn("Write to pipe failed \n");
90
thd_log_warn("Thermal Daemon is disabled \n");
93
// Polling mode enabled. Trigger a temp change message
94
for (i = 0; i < zones.size(); ++i) {
95
cthd_zone *zone = zones[i];
96
zone->zone_temperature_notification(0, 0);
99
if (poll_fds[0].revents & POLLIN) {
101
if (kobj_uevent.check_for_event()) {
105
thd_log_debug("kobj uevent for thermal\n");
106
if ((tm - thz_last_time) >= thz_notify_debounce_interval) {
107
for (i = 0; i < zones.size(); ++i) {
108
cthd_zone *zone = zones[i];
109
zone->zone_temperature_notification(0, 0);
112
thd_log_debug("IGNORE THZ kevent\n");
117
if (poll_fds[wakeup_fd].revents & POLLIN) {
118
message_capsul_t msg;
120
thd_log_debug("wakeup fd event\n");
121
int result = read(poll_fds[wakeup_fd].fd, &msg,
122
sizeof(message_capsul_t));
124
thd_log_warn("read on wakeup fd failed\n");
125
poll_fds[wakeup_fd].revents = 0;
128
if (proc_message(&msg) < 0) {
129
thd_log_debug("Terminating thread..\n");
133
thd_log_debug("thd_engine_thread_end\n");
136
bool cthd_engine::set_preference(const int pref) {
140
int cthd_engine::thd_engine_start(bool ignore_cpuid_check) {
144
if (ignore_cpuid_check) {
145
thd_log_debug("Ignore CPU ID check for MSRs \n");
146
proc_list_matched = true;
150
// Pipe is used for communication between two processes
151
ret = pipe(wake_fds);
153
thd_log_error("Thermal sysfs: pipe creation failed %d:\n", ret);
154
return THD_FATAL_ERROR;
156
fcntl(wake_fds[0], F_SETFL, O_NONBLOCK);
157
fcntl(wake_fds[1], F_SETFL, O_NONBLOCK);
158
write_pipe_fd = wake_fds[1];
159
wakeup_fd = THD_NUM_OF_POLL_FDS - 1;
161
memset(poll_fds, 0, sizeof(poll_fds));
162
poll_fds[wakeup_fd].fd = wake_fds[0];
163
poll_fds[wakeup_fd].events = POLLIN;
164
poll_fds[wakeup_fd].revents = 0;
166
poll_timeout_msec = -1;
167
if (poll_interval_sec) {
168
thd_log_warn("Polling mode is enabled: %d\n", poll_interval_sec);
169
poll_timeout_msec = poll_interval_sec * 1000;
172
ret = read_thermal_sensors();
173
if (ret != THD_SUCCESS) {
174
thd_log_error("Thermal sysfs Error in reading sensors\n");
175
// This is a fatal error and daemon will exit
176
return THD_FATAL_ERROR;
179
ret = read_cooling_devices();
180
if (ret != THD_SUCCESS) {
181
thd_log_error("Thermal sysfs Error in reading cooling devs\n");
182
// This is a fatal error and daemon will exit
183
return THD_FATAL_ERROR;
186
ret = read_thermal_zones();
187
if (ret != THD_SUCCESS) {
188
thd_log_error("No thermal sensors found\n");
189
// This is a fatal error and daemon will exit
190
return THD_FATAL_ERROR;
193
// Check if polling is disabled and sensors don't support
194
// async mode, in that enable force polling
195
if (!poll_interval_sec) {
197
for (i = 0; i < zones.size(); ++i) {
198
cthd_zone *zone = zones[i];
199
if (!zone->zone_active_status())
201
if (!zone->check_sensor_async_status()) {
203
"Polling will be enabled as some sensors are not capable to notify asynchnously \n");
204
poll_timeout_msec = def_poll_interval;
208
if (i == zones.size()) {
209
thd_log_info("Proceed without polling mode! \n");
213
poll_fds[0].fd = kobj_uevent.kobj_uevent_open();
214
if (poll_fds[0].fd < 0) {
215
thd_log_warn("Invalid kobj_uevent handle\n");
218
thd_log_info("FD = %d\n", poll_fds[0].fd);
219
kobj_uevent.register_dev_path(
220
(char *) "/devices/virtual/thermal/thermal_zone");
221
poll_fds[0].events = POLLIN;
222
poll_fds[0].revents = 0;
224
#ifndef DISABLE_PTHREAD
225
// condition variable
226
pthread_cond_init(&thd_cond_var, NULL);
227
pthread_mutex_init(&thd_cond_mutex, NULL);
229
pthread_attr_init(&thd_attr);
230
pthread_attr_setdetachstate(&thd_attr, PTHREAD_CREATE_DETACHED);
231
ret = pthread_create(&thd_engine, &thd_attr, cthd_engine_thread,
237
if((childpid = fork()) == - 1)
245
/* Child process closes up input side of pipe */
247
cthd_engine_thread((void*)this);
251
/* Parent process closes up output side of pipe */
256
preference = thd_pref.get_preference();
257
thd_log_info("Current user preference is %d\n", preference);
259
if (control_mode == EXCLUSIVE) {
260
thd_log_info("Control is taken over from kernel\n");
261
takeover_thermal_control();
266
int cthd_engine::thd_engine_stop() {
270
static void *cthd_engine_thread(void *arg) {
271
cthd_engine *obj = (cthd_engine*) arg;
273
obj->thd_engine_thread();
278
void cthd_engine::send_message(message_name_t msg_id, int size,
279
unsigned char *msg) {
280
message_capsul_t msg_cap;
282
memset(&msg_cap, 0, sizeof(message_capsul_t));
284
msg_cap.msg_id = msg_id;
285
msg_cap.msg_size = (size > MAX_MSG_SIZE) ? MAX_MSG_SIZE : size;
287
memcpy(msg_cap.msg, msg, msg_cap.msg_size);
288
int result = write(write_pipe_fd, &msg_cap, sizeof(message_capsul_t));
290
thd_log_warn("Write to pipe failed \n");
293
void cthd_engine::process_pref_change() {
297
new_pref = thd_pref.get_preference();
298
if (new_pref == PREF_DISABLED) {
303
if (preference != new_pref) {
304
thd_log_warn("Preference changed \n");
306
preference = new_pref;
307
for (unsigned int i = 0; i < zones.size(); ++i) {
308
cthd_zone *zone = zones[i];
309
zone->update_zone_preference();
312
if (control_mode == EXCLUSIVE) {
313
thd_log_info("Control is taken over from kernel\n");
314
takeover_thermal_control();
318
void cthd_engine::thd_engine_terminate() {
319
send_message(TERMINATE, 0, NULL);
324
int cthd_engine::thd_engine_set_user_max_temp(const char *zone_type,
325
const char *user_set_point) {
326
std::string str(user_set_point);
329
thd_log_debug("thd_engine_set_user_set_point %s\n", user_set_point);
332
if (std::isdigit(str[0], loc) == 0) {
333
thd_log_warn("thd_engine_set_user_set_point Invalid set point\n");
337
zone = get_zone(zone_type);
339
thd_log_warn("thd_engine_set_user_set_point Invalid zone\n");
342
return zone->update_max_temperature(atoi(user_set_point));
345
void cthd_engine::thermal_zone_change(message_capsul_t *msg) {
347
thermal_zone_notify_t *pmsg = (thermal_zone_notify_t*) msg->msg;
348
for (unsigned i = 0; i < zones.size(); ++i) {
349
cthd_zone *zone = zones[i];
350
if (zone->zone_active_status())
351
zone->zone_temperature_notification(pmsg->type, pmsg->data);
353
thd_log_debug("zone is not active\n");
358
void cthd_engine::poll_enable_disable(bool status, message_capsul_t *msg) {
359
unsigned int *sensor_id = (unsigned int*) msg->msg;
362
poll_sensor_mask |= (1 << (*sensor_id));
363
poll_timeout_msec = def_poll_interval;
364
thd_log_debug("thd_engine polling enabled via %u \n", *sensor_id);
366
poll_sensor_mask &= ~(1 << (*sensor_id));
367
if (!poll_sensor_mask) {
368
poll_timeout_msec = -1;
369
thd_log_debug("thd_engine polling last disabled via %u \n",
375
int cthd_engine::proc_message(message_capsul_t *msg) {
378
thd_log_debug("Receieved message %d\n", msg->msg_id);
379
switch (msg->msg_id) {
383
thd_log_warn("Terminating ...\n");
389
process_pref_change();
391
case THERMAL_ZONE_NOTIFY:
393
thd_log_warn("Thermal Daemon is disabled \n");
396
thermal_zone_change(msg);
403
thd_engine_reload_zones();
406
if (!poll_interval_sec) {
407
poll_enable_disable(true, msg);
411
if (!poll_interval_sec) {
412
poll_enable_disable(false, msg);
422
cthd_cdev *cthd_engine::thd_get_cdev_at_index(int index) {
423
for (int i = 0; i < cdev_cnt; ++i) {
424
if (cdevs[i]->thd_cdev_get_index() == index)
430
void cthd_engine::takeover_thermal_control() {
431
csys_fs sysfs("/sys/class/thermal/");
434
struct dirent *entry;
435
const std::string base_path = "/sys/class/thermal/";
437
thd_log_info("Taking over thermal control \n");
438
if ((dir = opendir(base_path.c_str())) != NULL) {
439
while ((entry = readdir(dir)) != NULL) {
440
if (!strncmp(entry->d_name, "thermal_zone",
441
strlen("thermal_zone"))) {
444
i = atoi(entry->d_name + strlen("thermal_zone"));
445
std::stringstream policy;
446
std::stringstream type;
447
std::string type_val;
448
std::string curr_policy;
450
type << "thermal_zone" << i << "/type";
451
if (sysfs.exists(type.str().c_str())) {
452
sysfs.read(type.str(), type_val);
454
policy << "thermal_zone" << i << "/policy";
455
if (sysfs.exists(policy.str().c_str())) {
456
sysfs.read(policy.str(), curr_policy);
457
zone_preferences.push_back(curr_policy);
458
sysfs.write(policy.str(), "user_space");
466
void cthd_engine::giveup_thermal_control() {
467
if (control_mode != EXCLUSIVE)
470
if (zone_preferences.size() == 0)
473
thd_log_info("Giving up thermal control \n");
475
csys_fs sysfs("/sys/class/thermal/");
478
struct dirent *entry;
479
const std::string base_path = "/sys/class/thermal/";
481
if ((dir = opendir(base_path.c_str())) != NULL) {
482
while ((entry = readdir(dir)) != NULL) {
483
if (!strncmp(entry->d_name, "thermal_zone",
484
strlen("thermal_zone"))) {
487
i = atoi(entry->d_name + strlen("thermal_zone"));
488
std::stringstream policy;
489
std::stringstream type;
490
std::string type_val;
492
type << "thermal_zone" << i << "/type";
493
if (sysfs.exists(type.str().c_str())) {
494
sysfs.read(type.str(), type_val);
496
policy << "thermal_zone" << i << "/policy";
497
if (sysfs.exists(policy.str().c_str())) {
498
sysfs.write(policy.str(), zone_preferences[cnt++]);
506
void cthd_engine::process_terminate() {
507
thd_log_warn("terminating on user request ..\n");
508
giveup_thermal_control();
512
void cthd_engine::thd_engine_poll_enable(int sensor_id) {
513
send_message(POLL_ENABLE, (int) sizeof(sensor_id),
514
(unsigned char*) &sensor_id);
517
void cthd_engine::thd_engine_poll_disable(int sensor_id) {
518
send_message(POLL_DISABLE, (int) sizeof(sensor_id),
519
(unsigned char*) &sensor_id);
522
void cthd_engine::thd_engine_reload_zones() {
523
thd_log_warn(" Reloading zones\n");
524
for (unsigned int i = 0; i < zones.size(); ++i) {
525
cthd_zone *zone = zones[i];
530
int ret = read_thermal_zones();
531
if (ret != THD_SUCCESS) {
532
thd_log_error("No thermal sensors found\n");
533
// This is a fatal error and daemon will exit
538
// Add any tested platform ids in this table
539
static supported_ids_t id_table[] = { { 6, 0x2a }, // Sandybridge
540
{ 6, 0x2d }, // Sandybridge
541
{ 6, 0x3a }, // IvyBridge
542
{ 6, 0x3c }, { 6, 0x3e }, { 6, 0x3f }, { 6, 0x45 }, // Haswell ULT */
543
{ 6, 0x46 }, // Haswell ULT */
545
{ 0, 0 } // Last Invalid entry
548
int cthd_engine::check_cpu_id() {
550
// Copied from turbostat program
551
unsigned int ebx, ecx, edx, max_level;
552
unsigned int fms, family, model, stepping;
557
proc_list_matched = false;
560
asm("cpuid": "=a"(max_level), "=b"(ebx), "=c"(ecx), "=d"(edx): "a"(0));
562
if (ebx == 0x756e6547 && edx == 0x49656e69 && ecx == 0x6c65746e)
564
if (genuine_intel == 0) {
565
// Simply return without further capability check
568
asm("cpuid": "=a"(fms), "=c"(ecx), "=d"(edx): "a"(1): "ebx");
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)