pax_global_header00006660000000000000000000000064122541272240014513gustar00rootroot0000000000000052 comment=96d88c05bd951adc78ede57c56a66d8ac5c2410c thermal_daemon-1.1-rc2/000077500000000000000000000000001225412722400150175ustar00rootroot00000000000000thermal_daemon-1.1-rc2/.gitignore000066400000000000000000000005341225412722400170110ustar00rootroot00000000000000*~ .*.swp *.o *.lo *.la *.bz2 *.gir *.typelib *.stamp *-enum-types.[ch] Makefile Makefile.in* configure compile config.* aclocal.m4 depcomp install-sh libtool ltmain.sh missing mkinstalldirs POTFILES stamp-* .deps .libs autom4te.cache intltool-* gtk-doc.make TAGS INSTALL ABOUT-NLS *.pc cscope.*out thermald thd-dbus-interface.h .cproject .project thermal_daemon-1.1-rc2/Android.mk000066400000000000000000000033671225412722400167410ustar00rootroot00000000000000LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) thermald_src_path := ./src thermald_src_files := \ $(thermald_src_path)/android_main.cpp \ $(thermald_src_path)/thd_engine.cpp \ $(thermald_src_path)/thd_cdev.cpp \ $(thermald_src_path)/thd_cdev_therm_sys_fs.cpp \ $(thermald_src_path)/thd_engine_default.cpp \ $(thermald_src_path)/thd_sys_fs.cpp \ $(thermald_src_path)/thd_trip_point.cpp \ $(thermald_src_path)/thd_zone.cpp \ $(thermald_src_path)/thd_zone_surface.cpp \ $(thermald_src_path)/thd_zone_cpu.cpp \ $(thermald_src_path)/thd_zone_therm_sys_fs.cpp \ $(thermald_src_path)/thd_preference.cpp \ $(thermald_src_path)/thd_model.cpp \ $(thermald_src_path)/thd_parse.cpp \ $(thermald_src_path)/thd_sensor.cpp \ $(thermald_src_path)/thd_kobj_uevent.cpp \ $(thermald_src_path)/thd_cdev_order_parser.cpp \ $(thermald_src_path)/thd_cdev_gen_sysfs.cpp \ $(thermald_src_path)/thd_pid.cpp \ $(thermald_src_path)/thd_zone_generic.cpp \ $(thermald_src_path)/thd_cdev_cpufreq.cpp \ $(thermald_src_path)/thd_cdev_rapl.cpp \ $(thermald_src_path)/thd_cdev_intel_pstate_driver.cpp \ $(thermald_src_path)/thd_msr.cpp \ $(thermald_src_path)/thd_rapl_interface.cpp \ $(thermald_src_path)/thd_cdev_msr_rapl.cpp include external/stlport/libstlport.mk LOCAL_C_INCLUDES += $(LOCAL_PATH) $(thermald_src_path) \ external/icu4c/common \ external/libxml2/include LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS := -fpermissive -DTDRUNDIR='"data/thermal-daemon"' -DTDCONFDIR='"system/etc/thermal-daemon"' LOCAL_STATIC_LIBRARIES := libxml2 LOCAL_SHARED_LIBRARIES := liblog libcutils libdl libstlport libicuuc libicui18n LOCAL_PRELINK_MODULE := false LOCAL_SRC_FILES := $(thermald_src_files) LOCAL_MODULE := thermal-daemon include $(BUILD_EXECUTABLE) thermal_daemon-1.1-rc2/COPYING000066400000000000000000000431001225412722400160500ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. thermal_daemon-1.1-rc2/Makefile.am000066400000000000000000000033171225412722400170570ustar00rootroot00000000000000include $(GLIB_MAKEFILE) SUBDIRS = data ACLOCAL_AMFLAGS = # Global C Flags AM_CFLAGS = ${DBUS_CFLAGS} AM_CXXFLAGS = ${DBUS_CFLAGS}\ $(XML_CFLAGS) \ -DTDRUNDIR=\"$(tdrundir)\" \ -DTDCONFDIR=\"$(tdconfdir)\" \ -I src \ -fpermissive \ -fopenmp \ -Wreorder \ -Wsign-compare \ -Wreturn-type \ -Wunused-but-set-variable\ -Wformat EXTRA_DIST=Makefile.glib \ thermald.pc.in # Programs to build bin_PROGRAMS = thermald # Evaluate Table Application thermald_CPPFLAGS = \ -I@top_srcdir@/src \ -DTDLOCALEDIR=\"$(datadir)/locale\" \ -DGLIBC_SUPPORT thermald_includedir = @top_srcdir@ thermald_LDADD = \ $(DBUS_LIBS) \ $(GLIB_LIBS) \ $(LIBNL_LIBS) \ $(LIBM) \ $(LIBDL) \ $(XML_LIBS) BUILT_SOURCES = \ thd-dbus-interface.h thermald_SOURCES = \ src/main.cpp \ src/thd_engine.cpp \ src/thd_cdev.cpp \ src/thd_cdev_therm_sys_fs.cpp \ src/thd_engine_default.cpp \ src/thd_sys_fs.cpp \ src/thd_trip_point.cpp \ src/thd_zone.cpp \ src/thd_zone_surface.cpp \ src/thd_zone_cpu.cpp \ src/thd_zone_therm_sys_fs.cpp \ src/thd_preference.cpp \ src/thd_model.cpp \ src/thd_parse.cpp \ src/thd_sensor.cpp \ src/thd_kobj_uevent.cpp \ src/thd_cdev_order_parser.cpp \ src/thd_cdev_gen_sysfs.cpp \ src/thd_pid.cpp \ src/thd_zone_generic.cpp \ src/thd_cdev_cpufreq.cpp \ src/thd_cdev_rapl.cpp \ src/thd_cdev_intel_pstate_driver.cpp \ src/thd_msr.cpp \ src/thd_rapl_interface.cpp \ src/thd_cdev_msr_rapl.cpp man1_MANS = man/thermald.1 man/thermal-conf.xml.1 thd-dbus-interface.h: $(top_srcdir)/src/thd-dbus_interface.xml $(AM_V_GEN) dbus-binding-tool --prefix=thd_dbus_interface --mode=glib-server --output=$@ $< CLEANFILES = $(BUILT_SOURCES) thermal_daemon-1.1-rc2/README.txt000066400000000000000000000124311225412722400165160ustar00rootroot00000000000000Use man pages to check command line arguments in configuration: man thermald man thermal-conf.xml Prerequisites: Kernel Prefers kernel with Intel RAPL power capping driver : Available from Linux kernel 3.13.rc1 Intel P State driver (Available in Linux kernel stable release) Intel Power clamp driver (Available in Linux kernel stable release) CONFIG_X86_MSR, so that x86 MSR can be read/write from user space to control RAPL if no RAPL powecap class driver is not present. Default If none of the above available cpufreq to control P states. Building and executing on Fedora 1. Install yum install automake yum install gcc yum install gcc-c++ yum install glib-devel yum install dbus-glib-devel yum install libxml2-devel 2 Build ./autogen.sh ./configure prefix=/usr make sudo make install 3 - start service sudo systemctl start thermald.service - Get status sudo systemctl status thermald.service - Stop service sudo systemctl stop thermald.service 4. Terminate using DBUS I/F sudo test/test_pref.sh and select "TERMINATE" choice. Building on Ubuntu 1. Install sudo apt-get install autoconf sudo apt-get install g++ sudo apt-get install libglib2.0-dev sudo apt-get install libdbus-1-dev sudo apt-get install libdbus-glib-1-dev sudo apt-get install libxml2-dev 2 Build ./autogen.sh ./configure prefix=/usr make sudo make install (It will give error for systemd configuration, but ignore) cp data/thermald.conf /etc/init/ 3. Use "sudo start thermald" to start Use "sudo stop thermald" to stop ------------------------------------------- Releases Release 1.1 - Use powercap Intel RAPL driver - Use skin temperature sensor by default if available - Specify thermal relationship - Clean up for MSR related controls as up stream kernel driver are capable now - Override capability of thermal sysfs for a specific sensor or zone - Friendly to new thermal sysfs Release 1.04 - Android and chrome os integration - Minor fixes for buggy max temp Release 1.03 - Allow negative step increments while configuring via XML - Use powercap RAPL driver I/F - Additional cpuids in the list - Add man page with details of usage - Added P state turbo on/off Release 1.02 - Allow user to change the max temperarure via dbus message - Allow user to change the cooling method order via an XML configuration - Upstart fixes - Valgrind and zero warnings on build Release 1.01 - Implement RAPL using MSRs. - User can configure cooling device order via XML config file - Fix sensor path configuration for thermal-conf.xml, so that user cn specify custom sensor paths - Use CPU max scaling frequency to control CPU Frequencies - RPM generation scripts - Build and formatting fixes from Alexander Bersenev Release 1.0 - Tested on multiple platforms - Using PID version 0.9 - Replaced netlink with uevents - Fix issue with pre-configured thermal data to control daemon - Use pthreads version 0.8 - Fix RAPL PATH, which is submitted upstream - Handle case when there is no MSR access from user mode - Allow non Intel CPUs version 0.7 - Conditional per cpu control - Family id check - If no max use offset from critical temperature - Switch to hwmon if there is no coretemp - Error handling if MSR support is not enabled in kernel - Code clean up and comments Version 0.6 - Use Intel P state driver to control P states - Use RAPL cooling device - Fix valgrind reported errors and cleanup - Add document Version 0.5 - License update to GPL v2 or later - Change dbus session bus to system - Load thermal-conf.xml data if exact UUID match Version 0.4 - Added power clamp driver interface - Added per cpu controls by trying to calibrate in the background to learn sensor cpu relationship - Optimized p states and turbo states and cleaned up - systemd and service start stop interface Version 0.3 - Added P states t states turbo states as the cooling methods - No longer depend on any thermal sysfs, zone cooling device by default - Uses DTS core temperature and p/turbo/t states to cool system - By default only will use DTS core temerature and p/turbo/t states only - All the previous controls based on the zones/cdevs and XML configuration is only done, when activated via command line - The set points are calculated and stored in a config file when it hits thermal threshold and adjusted based on slope and angular increments to dynamically adjust set point Version 0.2 - Define XML interface to set configuration data. Refere to thermal-conf.xml. This allows to overide buggy Bios thermal comfiguration and also allows to extend the capability. - Use platform DMI UUID to index into configuration data. If there is no UUID match, falls back to thermal sysfs - Terminate interface - Takes over control from kernel thermal processing - Clean up of classes. Version 0.1 - Dbus interface to set preferred policy: "performance", "quiet/power", "disabled" - Defines a C++ classes for zones, cooling devices, trip points, thermal engine - Methods can be overridden in a custom class to modify default behaviour - Read thermal zone and cooling devices, trip points etc, - Read temprature via netlink notification or via polling configurable via command line - Once a trip point is crossed, activate the associate cooling devices. Start with min tstate to max tstate for each cooling device. - Based on active or passive settings it decides the cooling devices thermal_daemon-1.1-rc2/android_main_dbus.cpp000066400000000000000000000234131225412722400211670ustar00rootroot00000000000000/* * android_main.cpp: Thermal Daemon entry point tuned for Android * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * * This is the main entry point for thermal daemon. This has main function * which parses command line arguments, set up dbus server and log related * functions. */ #include #include "thermald.h" #include "thd_preference.h" #include "thd_engine.h" #include "thd_engine_default.h" #include "thd_parse.h" #define THERMALD_DAEMON_NAME "thermald" // poll mode int thd_poll_interval = 4; //in seconds static int pid_file_handle; // Thermal engine cthd_engine *thd_engine; static DBusConnection *dbus_conn; // Start dbus server static int thermald_dbus_server_start() { DBusError err; int ret; char* param; dbus_error_init(&err); dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); if (dbus_error_is_set(&err)) { thd_log_fatal("Connection Error (%s)\n", err.message); dbus_error_free(&err); } if (NULL == dbus_conn) { thd_log_fatal("Connection Null\n"); exit(1); } ret = dbus_bus_request_name(dbus_conn, THD_SERVICE_NAME, DBUS_NAME_FLAG_REPLACE_EXISTING, &err); if (dbus_error_is_set(&err)) { thd_log_fatal("Name Error (%s)\n", err.message); dbus_error_free(&err); } if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) { thd_log_fatal("Not Primary Owner (%d)\n", ret); exit(1); } thd_log_info("thermald dbus server started \n"); return 0; } // Stop dbus server static void thermald_dbus_server_stop() { if (dbus_conn) dbus_connection_close(dbus_conn); } // Stop daemon static void daemonShutdown() { if (dbus_conn) thermald_dbus_server_stop(); if (pid_file_handle) close(pid_file_handle); thd_engine->thd_engine_terminate(); sleep(1); delete thd_engine; } // Stop daemon static void thermald_dbus_listen() { DBusMessage *msg; DBusMessage *reply; DBusMessageIter args; const char *method; while (true) { bool read_msg = true; // Blocking read of the next available message dbus_connection_read_write(dbus_conn, -1); thd_log_debug("Got some dbus message... \n"); while (read_msg) { msg = dbus_connection_pop_message(dbus_conn); // loop again if we haven't got a message if (NULL == msg) { read_msg = false; break; } method = dbus_message_get_member(msg); if (!method) { dbus_message_unref(msg); read_msg = false; break; } thd_log_info("Received dbus msg %s\n", method); if (!strcasecmp(method, "SetUserMaxTemperature")) { bool status; char *zone_name; char *set_point; DBusError error; dbus_error_init(&error); if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &zone_name, DBUS_TYPE_STRING, &set_point, DBUS_TYPE_INVALID)) { thd_log_info("New Set Point %s\n", set_point); cthd_preference thd_pref; if (thd_engine->thd_engine_set_user_max_temp(zone_name, set_point) == THD_SUCCESS) thd_engine->send_message(PREF_CHANGED, 0, NULL); } else { thd_log_error("dbus_message_get_args failed %s\n", error.message); } dbus_error_free(&error); } else if (!strcasecmp(method, "SetCurrentPreference")) { bool status; char *pref_str; DBusError error; dbus_error_init(&error); if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &pref_str, DBUS_TYPE_INVALID)) { thd_log_info("New Pref %s\n", pref_str); cthd_preference thd_pref; thd_pref.set_preference(pref_str); thd_engine->send_message(PREF_CHANGED, 0, NULL); } else { thd_log_error("dbus_message_get_args failed %s\n", error.message); } dbus_error_free(&error); } else if (!strcasecmp(method, "CalibrateStart")) { // TBD } else if (!strcasecmp(method, "CalibrateEnd")) { // TBD } else if (!strcasecmp(method, "Terminate")) { daemonShutdown(); exit(EXIT_SUCCESS); } else { thd_log_error("dbus_message_get_args Invalid Message\n"); } // free the message dbus_message_unref(msg); } } } // signal handler static void signal_handler(int sig) { switch (sig) { case SIGHUP: thd_log_warn("Received SIGHUP signal. \n"); break; case SIGINT: case SIGTERM: thd_log_info("Daemon exiting \n"); daemonShutdown(); exit(EXIT_SUCCESS); break; default: thd_log_warn("Unhandled signal %s", strsignal(sig)); break; } } static void daemonize(char *rundir, char *pidfile) { int pid, sid, i; char str[10]; struct sigaction sig_actions; sigset_t sig_set; if (getppid() == 1) { return; } sigemptyset(&sig_set); sigaddset(&sig_set, SIGCHLD); sigaddset(&sig_set, SIGTSTP); sigaddset(&sig_set, SIGTTOU); sigaddset(&sig_set, SIGTTIN); sigprocmask(SIG_BLOCK, &sig_set, NULL); sig_actions.sa_handler = signal_handler; sigemptyset(&sig_actions.sa_mask); sig_actions.sa_flags = 0; sigaction(SIGHUP, &sig_actions, NULL); sigaction(SIGTERM, &sig_actions, NULL); sigaction(SIGINT, &sig_actions, NULL); pid = fork(); if (pid < 0) { /* Could not fork */ exit(EXIT_FAILURE); } if (pid > 0) { thd_log_info("Child process created: %d\n", pid); exit(EXIT_SUCCESS); } umask(027); sid = setsid(); if (sid < 0) { exit(EXIT_FAILURE); } /* close all descriptors */ for (i = getdtablesize(); i >= 0; --i) { close(i); } i = open("/dev/null", O_RDWR); dup(i); dup(i); chdir(rundir); pid_file_handle = open(pidfile, O_RDWR | O_CREAT, 0600); if (pid_file_handle == -1) { /* Couldn't open lock file */ thd_log_info("Could not open PID lock file %s, exiting", pidfile); exit(1); } /* Try to lock file */ #ifdef LOCKF_SUPPORT if (lockf(pid_file_handle, F_TLOCK, 0) == -1) { #else if (flock(pid_file_handle,LOCK_EX|LOCK_NB) < 0) { #endif /* Couldn't get lock on lock file */ thd_log_info("Couldn't get lock file %d\n", getpid()); exit(1); } thd_log_info("Thermal PID %d\n", getpid()); sprintf(str, "%d\n", getpid()); write(pid_file_handle, str, strlen(str)); } static void print_usage(FILE* stream, int exit_code) { fprintf(stream, "Usage: dptf_if options [ ... ]\n"); fprintf(stream, " --help Display this usage information.\n" " --version Show version.\n" " --no-daemon No daemon.\n" " -poll-interval poll interval 0 to disable.\n" " --dbus-enable Enable dbus I/F.\n" " --exclusive_control to act as exclusive thermal controller. \n"); exit(exit_code); } int main(int argc, char *argv[]) { int c; int option_index = 0; bool no_daemon = false; bool exclusive_control = false; bool dbus_enable = false; bool test_mode = false; const char* const short_options = "hvnp:de"; static struct option long_options[] = { { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'v' }, { "no-daemon", no_argument, 0, 'n' }, { "poll-interval", required_argument, 0, 'p' }, { "dbus-enable", no_argument, 0, 'd' }, { "exclusive_control", no_argument, 0, 'e' }, { "test-mode", no_argument, 0, 't' }, { NULL, 0, NULL, 0 } }; if (argc > 1) { while ((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) { int this_option_optind = optind ? optind : 1; switch (c) { case 'h': print_usage(stdout, 0); break; case 'v': fprintf(stdout, "1.03-01\n"); exit(0); break; case 'n': no_daemon = true; break; case 'd': dbus_enable = true; break; case 'p': thd_poll_interval = atoi(optarg); break; case 'e': exclusive_control = true; break; case 't': test_mode = true; break; case -1: case 0: break; default: break; } } } if (getuid() != 0 && !test_mode) { fprintf(stderr, "You must be root to run thermal daemon!\n"); exit(1); } if ((c = mkdir(TDRUNDIR, 0755)) != 0) { if (errno != EEXIST) { fprintf(stderr, "Cannot create '%s': %s\n", TDRUNDIR, strerror(errno)); exit(1); } } mkdir(TDCONFDIR, 0755); // Don't care return value as directory if (!no_daemon) { daemonize((char *) "/tmp/", (char *) "/tmp/thermald.pid"); } else signal(SIGINT, signal_handler); thd_log_info( "Linux Thermal Daemon is starting mode %d : poll_interval %d :ex_control %d\n", no_daemon, thd_poll_interval, exclusive_control); thd_engine = new cthd_engine_default(); if (exclusive_control) thd_engine->set_control_mode(EXCLUSIVE); thd_engine->set_poll_interval(thd_poll_interval); if (thd_engine->thd_engine_start(false) != THD_SUCCESS) { thd_log_error("THD engine start failed: "); exit(1); } // Initialize thermald objects if (thd_engine->thd_engine_start(false) != THD_SUCCESS) { thd_log_error("thermald engine start failed: "); exit(1); } #ifdef VALGRIND_TEST // lots of STL lib function don't free memory // when called with exit() fn. // Here just run for some time and gracefully return. sleep(10); if (dbus_conn) thermald_dbus_server_stop(); if (pid_file_handle) close(pid_file_handle); thd_engine->thd_engine_terminate(); sleep(1); delete thd_engine; #else if (dbus_enable) { if (thermald_dbus_server_start()) { fprintf(stderr, "Dbus start error\n"); exit(1); } thermald_dbus_listen(); // Will block here thermald_dbus_server_stop(); } else { for (;;) sleep(0xffff); } thd_log_info("Linux Thermal Daemon is exiting \n"); #endif return 0; } thermal_daemon-1.1-rc2/autogen.sh000077500000000000000000000000521225412722400170150ustar00rootroot00000000000000#!/bin/sh autoreconf --install --verbose thermal_daemon-1.1-rc2/configure.ac000066400000000000000000000051531225412722400173110ustar00rootroot00000000000000AC_PREREQ(1.0) m4_define([td_major_version], [1]) m4_define([td_minor_version], [1]) m4_define([td_version], [td_major_version.td_minor_version]) AC_INIT([thermald], [td_version], [], [thermald]) AC_CONFIG_AUX_DIR(build-aux) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([1.11 foreign no-define subdir-objects]) AM_MAINTAINER_MODE([enable]) AC_ARG_WITH(dbus-sys-dir, AS_HELP_STRING([--with-dbus-sys-dir=DIR], [where D-BUS system.d directory is])) if test -n "$with_dbus_sys_dir" ; then DBUS_SYS_DIR="$with_dbus_sys_dir" else DBUS_SYS_DIR="/etc/dbus-1/system.d" fi AC_SUBST(DBUS_SYS_DIR) # paths AC_SUBST(tdbinary, "$sbindir/$PACKAGE", [Binary executable]) AC_SUBST(tdconfdir, "$sysconfdir/$PACKAGE", [Configuration directory]) AC_SUBST(tdrundir, "$localstatedir/run/$PACKAGE", [Runtime state directory]) PKG_PROG_PKG_CONFIG AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]), [], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)]) if test "x$with_systemdsystemunitdir" != xno; then AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) fi AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ]) # print configuration echo echo "System paths:" echo " prefix: $prefix" echo " exec_prefix: $exec_prefix" echo " systemdunitdir: $with_systemdsystemunitdir" echo " tdbinary: $tdbinary" echo " tdconfdir: $tdconfdir" echo " tdrundir: $tdrundir" echo GETTEXT_PACKAGE=thermald AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package]) dnl dnl Checks for new dbus-glib property access function dnl AC_CHECK_LIB([dbus-glib-1], [dbus_glib_global_set_disable_legacy_property_access], ac_have_dg_prop="1", ac_have_dg_prop="0") AC_DEFINE_UNQUOTED(HAVE_DBUS_GLIB_DISABLE_LEGACY_PROP_ACCESS, $ac_have_dg_prop, [Define if you have a dbus-glib with dbus_glib_global_set_disable_legacy_property_access()]) PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.1 dbus-glib-1 >= 0.94) AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) GLIB_VERSION_DEFINES="-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26 '-DGLIB_VERSION_MAX_ALLOWED=G_ENCODE_VERSION(2,34)'" DBUS_CFLAGS="$DBUS_CFLAGS $GLIB_VERSION_DEFINES" PKG_CHECK_MODULES(GLIB, gio-unix-2.0 >= 2.22 gmodule-2.0) GLIB_CFLAGS="$GLIB_CFLAGS $GLIB_VERSION_DEFINES" AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) PKG_CHECK_MODULES(XML, libxml-2.0 >= 2.4) AC_PROG_CC AC_PROG_CPP AC_PROG_CXX AC_PROG_INSTALL AC_C_CONST AC_C_INLINE AC_TYPE_SIZE_T AC_CONFIG_FILES([Makefile data/Makefile]) AC_OUTPUT thermal_daemon-1.1-rc2/data/000077500000000000000000000000001225412722400157305ustar00rootroot00000000000000thermal_daemon-1.1-rc2/data/Makefile.am000066400000000000000000000017231225412722400177670ustar00rootroot00000000000000include $(GLIB_MAKEFILE) if HAVE_SYSTEMD systemdsystemunit_DATA = \ thermald.service thermald.service: thermald.service.in $(edit) $< >$@ servicedir = $(datadir)/dbus-1/system-services service_in_files = org.freedesktop.thermald.service.in service_DATA = $(service_in_files:.service.in=.service) $(service_DATA): $(service_in_files) Makefile $(edit) $< >$@ endif edit = sed \ -e 's|@bindir[@]|$(bindir)|g' \ -e 's|@sbindir[@]|$(sbindir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' dbusservicedir = $(DBUS_SYS_DIR) dbusservice_DATA = org.freedesktop.thermald.conf tdconfigdir = $(tdconfdir) tdconfig_DATA = thermal-conf.xml \ thermal-cpu-cdev-order.xml upstartconfdir = /etc/init upstartconf_DATA = thermald.conf EXTRA_DIST = \ thermald.service.in \ org.freedesktop.thermald.service.in \ $(dbusservice_DATA) \ $(tdconfig_DATA) \ $(upstartconf_DATA) CLEANFILES = thermald.service org.freedesktop.thermald.service thermal_daemon-1.1-rc2/data/org.freedesktop.thermald.conf000066400000000000000000000016451225412722400235050ustar00rootroot00000000000000 thermal_daemon-1.1-rc2/data/org.freedesktop.thermald.service.in000066400000000000000000000001751225412722400246220ustar00rootroot00000000000000[D-BUS Service] Name=org.freedesktop.thermald Exec=/bin/false User=root SystemdService=dbus-org.freedesktop.thermald.service thermal_daemon-1.1-rc2/data/thermal-conf.xml000066400000000000000000000101101225412722400210220ustar00rootroot00000000000000 Generic X86 Laptop Device * QUIET TSKN 1 SKIN TSKN 44000 passive SEQUENTIAL 1 rapl_controller 100 16 2 intel_powerclamp 100 12 Example Platform Name Example UUID Example Product Name QUIET example_sensor_1 /some_path 0 example_thermal_sysfs_sensor 1 Example Zone type example_sensor_1 75000 max SEQUENTIAL 1 example_cooling_device 100 12 example_cooling_device 0 10 0 50 5000 > 0.001 0.0001 0.0001 thermal_daemon-1.1-rc2/data/thermal-cpu-cdev-order.xml000066400000000000000000000007741225412722400227330ustar00rootroot00000000000000 rapl_controller intel_pstate intel_powerclamp cpufreq Processor thermal_daemon-1.1-rc2/data/thermald.conf000066400000000000000000000004541225412722400204020ustar00rootroot00000000000000# thermald - thermal daemon # Upstart configuration file # Manages platform thermals description "thermal daemon" start on started dbus stop on stopping dbus respawn script echo "Waiting for thermal zone and coretemp sysfs to be ready " sleep 5 exec thermald --no-daemon --dbus-enable end script thermal_daemon-1.1-rc2/data/thermald.service.in000066400000000000000000000004201225412722400215130ustar00rootroot00000000000000[Unit] Description=Thermal Daemon Service After=syslog.target [Service] Type=dbus BusName=org.freedesktop.thermald ExecStart=/usr/bin/thermald --no-daemon --dbus-enable StandardError=null [Install] WantedBy=multi-user.target Alias=dbus-org.freedesktop.thermald.service thermal_daemon-1.1-rc2/distribution_integration/000077500000000000000000000000001225412722400221415ustar00rootroot00000000000000thermal_daemon-1.1-rc2/distribution_integration/android_dbus_service_information000066400000000000000000000007531225412722400306530ustar00rootroot00000000000000Android Integration Thermald required dbus daemon. To start dbus daemon add the following to your init.rc. service dbus /system/bin/dbus-daemon --system --nofork class main socket dbus stream 660 root root user root group system inet To start thermald as a service service thermald /system/bin/thermald --dbus-enable user root group system class main disabled oneshot By default it is disabled. So need explicit command somewhere: "start thermald" thermal_daemon-1.1-rc2/distribution_integration/chromeos_gentoo_ebuild_spec000066400000000000000000000012351225412722400276150ustar00rootroot00000000000000# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Header: $ EAPI=4 inherit autotools systemd DESCRIPTION="Thermal daemon for Intel architectures" HOMEPAGE="https://01.org/linux-thermal-daemon" SRC_URI="https://github.com/01org/thermal_daemon/archive/v${PV}.tar.gz -> ${P}.tar.gz" LICENSE="GPL-2+" SLOT="0" KEYWORDS="amd64 ia64 x86" IUSE="" S=${WORKDIR}/thermal_daemon-${PV} DEPEND="dev-libs/dbus-glib dev-libs/libxml2" RDEPEND="${DEPEND}" DOCS=( ThermalDaemon_Introduction.pdf README.txt ) src_prepare() { eautoreconf } src_configure() { econf --with-systemdsystemunitdir=$(systemd_get_unitdir) } thermal_daemon-1.1-rc2/distribution_integration/make_rpm.sh000077500000000000000000000005001225412722400242660ustar00rootroot00000000000000#!/bin/bash rsync -av --exclude='rpms' ../../thermal_daemon . mv thermal_daemon thermal_daemon-1.02 tar cvzf thermal_daemon-1.02.tar.gz thermal_daemon-1.02 cp thermal_daemon-1.02.tar.gz ~/rpmbuild/SOURCES rpmbuild -ba thermal_daemon.spec rpmlint thermal_daemon.spec ~/rpmbuild/SRPMS/thermal* rm -rf thermal_daemon-1.02 thermal_daemon-1.1-rc2/distribution_integration/thermal-daemon.spec000066400000000000000000000045221225412722400257150ustar00rootroot00000000000000Name: thermal-daemon Version: 1.03 Release: 1%{?dist} Summary: The "Linux Thermal Daemon" program from 01.org License: GPLv2+ URL: https://github.com/01org/thermal_daemon %global pkgname thermal_daemon %global commit 3ba658cff9c9237bb49a94aa553c77f57e1f8664 %global shortcommit %(c=%{commit}; echo ${c:0:7}) Source0: https://github.com/01org/thermal_daemon/archive/%{commit}/%{pkgname}-v%{version}-%{shortcommit}.tar.gz BuildRequires: automake BuildRequires: autoconf BuildRequires: glib-devel BuildRequires: dbus-glib-devel BuildRequires: libxml2-devel BuildRequires: systemd Requires(post): systemd-units Requires(preun): systemd-units Requires(postun): systemd-units %description Thermal Daemon monitors and controls platform temperature. %prep %setup -qn %{pkgname}-%{commit} %build autoreconf -f -i %configure prefix=%{_prefix} make %{?_smp_mflags} # Although there is a folder test in the upstream repo, this is not for self tests. # Hence check section is not present. %install %make_install DESTDIR=%{buildroot} %post %systemd_post thermald.service %preun %systemd_preun thermald.service %postun %systemd_postun_with_restart thermald.service %files %{_bindir}/thermald %config(noreplace) %{_sysconfdir}/dbus-1/system.d/org.freedesktop.thermald.conf %{_datadir}/dbus-1/system-services/org.freedesktop.thermald.service %config(noreplace) %{_sysconfdir}/thermald/thermal-conf.xml %config(noreplace) %{_sysconfdir}/thermald/thermal-cdev-order.xml %doc COPYING README.txt %{_mandir}/man1/thermald.1.gz %{_unitdir}/thermald.service %changelog * Tue Oct 01 2013 Srinivas Pandruvada 1.03-1 - Upgraded to thermal daemon 1.03 * Mon Jun 24 2013 Srinivas Pandruvada 1.02-5 - Replaced underscore with dash in the package name * Thu Jun 20 2013 Srinivas Pandruvada 1.02-4 - Resolved prefix and RPM_BUILD_ROOT as per review comments * Wed Jun 19 2013 Srinivas Pandruvada 1.02-3 - Removed libxml2 requirement and uses shortcommit in the Source0 * Tue Jun 18 2013 Srinivas Pandruvada 1.02-2 - Update spec file after first review * Fri Jun 14 2013 Srinivas Pandruvada 1.02-1 - Initial package thermal_daemon-1.1-rc2/man/000077500000000000000000000000001225412722400155725ustar00rootroot00000000000000thermal_daemon-1.1-rc2/man/thermal-conf.xml.1000066400000000000000000000163231225412722400210370ustar00rootroot00000000000000.\" thermal-conf.xml(1) manual page .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public Licence along .\" with this manual; if not, write to the Free Software Foundation, Inc., .\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. .\" .\" Copyright (C) 2012 Intel Corporation. All rights reserved. .\" .TH thermal-conf.xml "1" "11 Dec 2013" .SH NAME thermal-conf.xml \- Configuration file for thermal daemon .SH SYNOPSIS $(TDCONFDIR)/etc/thermald/thermal-conf.xml .SH DESCRIPTION .B thermal-conf.xml is a configuration file for the thermal daemon. It is used to configure thermal sensors, zone and cooling devices.The location of this file depends on the configuration option used during build time. .TP The terminology used in this file confirms to "Advanced Configuration and Power Interface Specification". The ACPI thermal model is based around conceptual platform regions called thermal zones that physically contain devices, thermal sensors, and cooling controls. For example of a thermal zone can be a CPU or a laptop cover. A zone can contain multiple sensors for monitoring temperature. A cooling device provides interface to reduce the temperature of a source device, which causes increase in the temperature. An example of a cooling device is a FAN or some Linux driver which can throttle the source device. .TP A thermal zone configuration includes one or more trip points. A trip point is a temperature at which a cooling device needs to be activated. .TP A cooling device can be either active or passive. An example of an active device is a FAN, which will not reduce performance at the cost of consuming more power and noise. A passive device uses performance throttling to control temperature. In addition to cooling devices present in the thermal sysfs, the following cooling devices are built into the thermald, which can be used as valid cooling device type: .TP - rapl_controller .TP - intel_pstate .TP - cpufreq .TP The thermal sysfs under Linux (/sys/class/thermal) provides a way to represent per platform ACPI configuration. The kernel thermal governor uses this data to keep the platform thermals under control. But there are some limitations, which thermald tries to resolve. For example: .TP - If the ACPI data is not optimized or buggy. In this case thermal-conf.xml can be used to correct the behavior without change in BIOS. .TP - There may be thermal zones exposed by the thermal sysfs without associated cooling actions. In this case thermal conf.xml can be used to tie the cooling devices to those zones. .TP - The best cooling method may not be in the thermal sysfs. In this case thermal-conf.xml can be used to bind a zone to an external cooling device. .TP - Specify thermal relationships. A zone can be influenced by multiple source devices with varying degrees. In this case thermal-conf.xml can be used to define the relative influence for apply compensation. .SH FILE FORMAT The configuration file format confirms to XML specifications. A set of tags defined to define platform, sensors, zones, cooling devices and trip points. .TP .TP .TP Example Platform Name .TP .TP Example UUID .TP configuration file format confirms to XML specifications. A set of tags defined to define platform, sensors, zones, cooling devices and trip points. Example Product Name .TP QUIET|PERFORMANCE .TP .TP .TP .TP .TP example_sensor_1 .TP /some_path .TP 0 .TP .TP .TP .TP example_thermal_sysfs_sensor .TP .TP 1 .TP .TP .TP .TP .TP Example Zone type .TP .TP .TP example_sensor_1 .TP .TP 75000 .TP .TP max .TP .TP SEQUENTIAL .TP .TP 1 .TP example_cooling_device .TP .TP 100 .TP .TP 12 .TP .TP .TP .TP .TP .TP .TP .TP .TP example_cooling_device .TP 0 .TP 10 .TP 0 .TP 50 .TP 5000 .TP .TP .TP 0.001 .TP 0.0001 .TP 0.0001 .TP .TP .TP .TP .TP .TP thermal_daemon-1.1-rc2/man/thermald.1000066400000000000000000000050211225412722400174520ustar00rootroot00000000000000.\" thermald (1) manual page .\" .\" This is free documentation; you can redistribute it and/or .\" modify it under the terms of the GNU General Public License as .\" published by the Free Software Foundation; either version 2 of .\" the License, or (at your option) any later version. .\" .\" The GNU General Public License's references to "object code" .\" and "executables" are to be interpreted as the output of any .\" document formatting or typesetting system, including .\" intermediate and printed output. .\" .\" This manual is distributed in the hope that it will be useful, .\" but WITHOUT ANY WARRANTY; without even the implied warranty of .\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the .\" GNU General Public License for more details. .\" .\" You should have received a copy of the GNU General Public Licence along .\" with this manual; if not, write to the Free Software Foundation, Inc., .\" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. .\" .\" Copyright (C) 2012 Intel Corporation. All rights reserved. .\" .TH thermald "1" "8 May 2013" .SH NAME thermald \- start Linux thermal daemon .SH SYNOPSIS .B thermald .RI " [ " OPTIONS " ] .SH DESCRIPTION .B thermald is a Linux daemon used to prevent the overheating of platforms. This daemon monitors temperature and applies compensation using available cooling methods. By default, it monitors CPU temperature using available CPU digital temperature sensors and maintains CPU temperature under control, before HW takes aggressive correction action. If there is a skin temperature sensor in thermal sysfs, then it tries to keep skin temperature under 45C. .TP Optionally thermal daemon can act as a primary thermal controller by using thermal sysfs or an comprehensive configuration using an XML configuration file (thermal-conf.xml). .SH OPTIONS .TP .B \-h, \-\-help Show help options. .TP .B \-\-version Print thermald version and exit. .TP .B \-\-no-daemon Don't become a daemon: Default is daemon mode. .TP .B \-\-loglevel=info log severity: info level and up. .TP .B \-\-loglevel=debug log severity: debug level and up: Max logging. .TP .B \-\-poll-interval Poll interval in seconds: Poll for zone temperature changes. To disable polling, set to zero. Polling can only be disabled, if available temperature sensors can notify temperature change asynchronously. .TP .B \-\-dbus-enable Enable Dbus. .TP .B \-\-exclusive-control Act as exclusive thermal controller. This will use user-space governor for thermal sysfs and take over control. .SH SEE ALSO thermal-conf.xml(1) thermal_daemon-1.1-rc2/misc/000077500000000000000000000000001225412722400157525ustar00rootroot00000000000000thermal_daemon-1.1-rc2/misc/valgrind.supp000066400000000000000000000040771225412722400205010ustar00rootroot00000000000000# Valgrind suppressions file for glib, stl string calls { Syscall param write Memcheck:Param ... fun:g_main_loop_run fun:main } { g_type_init_with_debug_flags Memcheck:Leak ... fun:g_type_init_with_debug_flags fun:main } { g_main_loop_run Memcheck:Leak fun:memalign ... fun:g_main_loop_run fun:main } { c_plus_plus_string_hack Memcheck:Leak fun:_Znwm fun:_ZNSs?_Rep?_S_createEmmRKSaIcE fun:_ZNSs??_S_constructIPKcEEPcT_S?_RKSaIcESt??forward_iterator_tag fun:_ZNSsC?EPKcRKSaIcE fun:_ZN?cthd_msrC?Ev fun:_ZN??cthd_engine_dts??read_cooling_devicesEv fun:_ZN??cthd_engine??thd_engine_startEv fun:main } { c_plus_plus_string_hack Memcheck:Leak fun:_Znwm fun:_ZNSs4_Rep9_S_createEmmRKSaIcE fun:_ZNSs12_S_constructIPKcEEPcT_S3_RKSaIcESt20forward_iterator_tag fun:_ZNSsC1EPKcRKSaIcE fun:_ZN8cthd_msrC1Ev fun:_ZN15cthd_engine_dts26check_intel_p_state_driverEv fun:_ZN15cthd_engine_dts20read_cooling_devicesEv fun:_ZN11cthd_engine16thd_engine_startEv fun:main } { c_plus_plus_string_hack Memcheck:Leak fun:_Znwm fun:_ZNSs?_Rep?_S_createEmmRKSaIcE fun:_ZNSs??_S_constructIPKcEEPcT_S?_RKSaIcESt??forward_iterator_tag fun:_ZNSsC?EPKcRKSaIcE fun:_ZN?cthd_msrC1Ev fun:_ZN??c_rapl_interfaceC?Ei fun:_ZN??cthd_engine_dts??read_cooling_devicesEv fun:_ZN??cthd_engine??thd_engine_startEv fun:main } { c_plus_plus_string_hack Memcheck:Leak fun:_Znwm fun:_ZNSs?_Rep?_S_createEmmRKSaIcE fun:_ZNSs??_S_constructIPKcEEPcT_S?_RKSaIcESt??forward_iterator_tag fun:_ZNSsC?EPKcRKSaIcE fun:_ZN?cthd_msrC?Ev fun:_ZN??cthd_engine_dts??check_intel_p_state_driverEv fun:_ZN??cthd_engine_dts??read_cooling_devicesEv fun:_ZN??cthd_engine??thd_engine_startEv fun:main } { c_plus_plus_string_hack Memcheck:Leak fun:_Znwm fun:_ZNSt?vectorIiSaIiEE??_M_insert_auxEN?__gnu_cxx??__normal_iteratorIPiS?_EERKi fun:_ZN??cthd_cdev_pstates?initEv fun:_ZN??cthd_engine_dts??read_cooling_devicesEv fun:_ZN??cthd_engine??thd_engine_startEv fun:main } thermal_daemon-1.1-rc2/src/000077500000000000000000000000001225412722400156065ustar00rootroot00000000000000thermal_daemon-1.1-rc2/src/android_main.cpp000066400000000000000000000143041225412722400207400ustar00rootroot00000000000000/* * android_main.cpp: Thermal Daemon entry point tuned for Android * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * * This is the main entry point for thermal daemon. This has main function * which parses command line arguments, set up dbus server and log related * functions. */ #include "thermald.h" #include "thd_preference.h" #include "thd_engine.h" #include "thd_engine_default.h" #include "thd_parse.h" #define THERMALD_DAEMON_NAME "thermald" // poll mode int thd_poll_interval = 4; //in seconds static int pid_file_handle; // Thermal engine cthd_engine *thd_engine; // Stop daemon static void daemonShutdown() { if (pid_file_handle) close(pid_file_handle); thd_engine->thd_engine_terminate(); sleep(1); delete thd_engine; } // signal handler static void signal_handler(int sig) { switch (sig) { case SIGHUP: thd_log_warn("Received SIGHUP signal. \n"); break; case SIGINT: case SIGTERM: thd_log_info("Daemon exiting \n"); daemonShutdown(); exit(EXIT_SUCCESS); break; default: thd_log_warn("Unhandled signal %s", strsignal(sig)); break; } } static void daemonize(char *rundir, char *pidfile) { int pid, sid, i; char str[10]; struct sigaction sig_actions; sigset_t sig_set; if (getppid() == 1) { return; } sigemptyset(&sig_set); sigaddset(&sig_set, SIGCHLD); sigaddset(&sig_set, SIGTSTP); sigaddset(&sig_set, SIGTTOU); sigaddset(&sig_set, SIGTTIN); sigprocmask(SIG_BLOCK, &sig_set, NULL); sig_actions.sa_handler = signal_handler; sigemptyset(&sig_actions.sa_mask); sig_actions.sa_flags = 0; sigaction(SIGHUP, &sig_actions, NULL); sigaction(SIGTERM, &sig_actions, NULL); sigaction(SIGINT, &sig_actions, NULL); pid = fork(); if (pid < 0) { /* Could not fork */ exit(EXIT_FAILURE); } if (pid > 0) { thd_log_info("Child process created: %d\n", pid); exit(EXIT_SUCCESS); } umask(027); sid = setsid(); if (sid < 0) { exit(EXIT_FAILURE); } /* close all descriptors */ for (i = getdtablesize(); i >= 0; --i) { close(i); } i = open("/dev/null", O_RDWR); dup(i); dup(i); chdir(rundir); pid_file_handle = open(pidfile, O_RDWR | O_CREAT, 0600); if (pid_file_handle == -1) { /* Couldn't open lock file */ thd_log_info("Could not open PID lock file %s, exiting", pidfile); exit(1); } /* Try to lock file */ #ifdef LOCKF_SUPPORT if (lockf(pid_file_handle, F_TLOCK, 0) == -1) { #else if (flock(pid_file_handle,LOCK_EX|LOCK_NB) < 0) { #endif /* Couldn't get lock on lock file */ thd_log_info("Couldn't get lock file %d\n", getpid()); exit(1); } thd_log_info("Thermal PID %d\n", getpid()); sprintf(str, "%d\n", getpid()); write(pid_file_handle, str, strlen(str)); } static void print_usage(FILE* stream, int exit_code) { fprintf(stream, "Usage: dptf_if options [ ... ]\n"); fprintf(stream, " --help Display this usage information.\n" " --version Show version.\n" " --no-daemon No daemon.\n" " -poll-interval poll interval 0 to disable.\n" " --exclusive_control to act as exclusive thermal controller. \n"); exit(exit_code); } int main(int argc, char *argv[]) { int c; int option_index = 0; bool no_daemon = false; bool exclusive_control = false; bool dbus_enable = false; bool test_mode = false; const char* const short_options = "hvnp:de"; static struct option long_options[] = { { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'v' }, { "no-daemon", no_argument, 0, 'n' }, { "poll-interval", required_argument, 0, 'p' }, { "dbus-enable", no_argument, 0, 'd' }, { "exclusive_control", no_argument, 0, 'e' }, { "test-mode", no_argument, 0, 't' }, { NULL, 0, NULL, 0 } }; if (argc > 1) { while ((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) { int this_option_optind = optind ? optind : 1; switch (c) { case 'h': print_usage(stdout, 0); break; case 'v': fprintf(stdout, "1.03-01\n"); exit(0); break; case 'n': no_daemon = true; break; case 'd': dbus_enable = true; break; case 'p': thd_poll_interval = atoi(optarg); break; case 'e': exclusive_control = true; break; case 't': test_mode = true; break; case -1: case 0: break; default: break; } } } if (getuid() != 0 && !test_mode) { fprintf(stderr, "You must be root to run thermal daemon!\n"); exit(1); } if ((c = mkdir(TDRUNDIR, 0755)) != 0) { if (errno != EEXIST) { fprintf(stderr, "Cannot create '%s': %s\n", TDRUNDIR, strerror(errno)); exit(1); } } mkdir(TDCONFDIR, 0755); // Don't care return value as directory if (!no_daemon) { daemonize((char *) "/tmp/", (char *) "/tmp/thermald.pid"); } else signal(SIGINT, signal_handler); thd_log_info( "Linux Thermal Daemon is starting mode %d : poll_interval %d :ex_control %d\n", no_daemon, thd_poll_interval, exclusive_control); thd_engine = new cthd_engine_default(); if (exclusive_control) thd_engine->set_control_mode(EXCLUSIVE); thd_engine->set_poll_interval(thd_poll_interval); // Initialize thermald objects if (thd_engine->thd_engine_start(false) != THD_SUCCESS) { thd_log_error("thermald engine start failed: "); exit(1); } #ifdef VALGRIND_TEST // lots of STL lib function don't free memory // when called with exit(). // Here just run for some time and gracefully return. sleep(10); if (pid_file_handle) close(pid_file_handle); thd_engine->thd_engine_terminate(); sleep(1); delete thd_engine; #else for (;;) sleep(0xffff); thd_log_info("Linux Thermal Daemon is exiting \n"); #endif return 0; } thermal_daemon-1.1-rc2/src/main.cpp000066400000000000000000000306621225412722400172450ustar00rootroot00000000000000/* * main.cpp: Thermal Daemon entry point * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * * This is the main entry point for thermal daemon. This has main function * which parses command line arguments, set up dbus server and log related * functions. */ /* This implements main() function. This will parse command line options and * a new instance of cthd_engine object. By default it will create a engine * which uses dts engine, which DTS sensor and use P states to control * temperature, without any configuration. Alternatively if the * thermal-conf.xml has exact UUID match then it can use the zones and * cooling devices defined it to control thermals. This file will allow fine * tune ACPI thermal config or create new thermal config using custom * sensors. * Dbus interface allows user to switch between active/passive thermal controls * if the thermal-conf.xml defines parameters. */ #include #include #include "thermald.h" #include "thd_preference.h" #include "thd_engine.h" #include "thd_engine_default.h" #include "thd_parse.h" #include #if !defined(TD_DIST_VERSION) #define TD_DIST_VERSION PACKAGE_VERSION #endif typedef struct { GObject parent; } PrefObject; typedef struct { GObjectClass parent; } PrefObjectClass; GType pref_object_get_type(void); #define MAX_DBUS_REPLY_STR_LEN 20 #define PREF_TYPE_OBJECT (pref_object_get_type()) G_DEFINE_TYPE(PrefObject, pref_object, G_TYPE_OBJECT) gboolean thd_dbus_interface_calibrate(PrefObject *obj, GError **error); gboolean thd_dbus_interface_terminate(PrefObject *obj, GError **error); gboolean thd_dbus_interface_set_current_preference(PrefObject *obj, gchar *pref, GError **error); gboolean thd_dbus_interface_get_current_preference(PrefObject *obj, gchar **pref_out, GError **error); gboolean thd_dbus_interface_set_user_max_temperature(PrefObject *obj, gchar *zone_name, gchar *temperature, GError **error); // This is a generated file, which expects the above prototypes #include "thd-dbus-interface.h" // Default log level static int thd_log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO; // Daemonize or not static gboolean thd_daemonize; // Disable dbus static gboolean dbus_enable; // poll mode int thd_poll_interval = 4; //in seconds // check cpuid static gboolean ignore_cpuid_check = false; // Thermal engine cthd_engine *thd_engine; gboolean exclusive_control = FALSE; // DBUS Related functions // Dbus object initialization static void pref_object_init(PrefObject *obj) { g_assert(obj != NULL); } // Dbus object class initialization static void pref_object_class_init(PrefObjectClass *_class) { g_assert(_class != NULL); dbus_g_object_type_install_info(PREF_TYPE_OBJECT, &dbus_glib_thd_dbus_interface_object_info); } // Callback function called to inform a sent value via dbus gboolean thd_dbus_interface_set_current_preference(PrefObject *obj, gchar *pref, GError **error) { int ret; thd_log_debug("thd_dbus_interface_set_current_preference %s\n", (char*) pref); g_assert(obj != NULL); cthd_preference thd_pref; ret = thd_pref.set_preference((char*) pref); thd_engine->send_message(PREF_CHANGED, 0, NULL); return ret; } // Callback function called to get value via dbus gboolean thd_dbus_interface_get_current_preference(PrefObject *obj, gchar **pref_out, GError **error) { thd_log_debug("thd_dbus_interface_get_current_preference\n"); g_assert(obj != NULL); gchar *value_out; static char *pref_str; pref_str = g_new(char, MAX_DBUS_REPLY_STR_LEN); if (!pref_str) return FALSE; cthd_preference thd_pref; value_out = (gchar*) thd_pref.get_preference_cstr(); strncpy(pref_str, value_out, MAX_DBUS_REPLY_STR_LEN); thd_log_debug("thd_dbus_interface_get_current_preference out :%s\n", pref_str); *pref_out = pref_str; return TRUE; } gboolean thd_dbus_interface_calibrate(PrefObject *obj, GError **error) { // thd_engine->thd_engine_calibrate(); return TRUE; } gboolean thd_dbus_interface_terminate(PrefObject *obj, GError **error) { thd_engine->thd_engine_terminate(); return TRUE; } gboolean thd_dbus_interface_set_user_max_temperature(PrefObject *obj, gchar *zone_name, gchar *temperature, GError **error) { thd_log_debug("thd_dbus_interface_set_user_set_point %s:%s\n", zone_name, temperature); g_assert(obj != NULL); cthd_preference thd_pref; if (thd_engine->thd_engine_set_user_max_temp(zone_name, (char*) temperature) == THD_SUCCESS) thd_engine->send_message(PREF_CHANGED, 0, NULL); return TRUE; } // g_log handler. All logs will be directed here void thd_logger(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { if (!(thd_log_level & log_level)) return; if (thd_daemonize) { int syslog_priority; switch (log_level) { case G_LOG_LEVEL_ERROR: syslog_priority = LOG_CRIT; break; case G_LOG_LEVEL_CRITICAL: syslog_priority = LOG_ERR; break; case G_LOG_LEVEL_WARNING: syslog_priority = LOG_WARNING; break; case G_LOG_LEVEL_MESSAGE: syslog_priority = LOG_NOTICE; break; case G_LOG_LEVEL_DEBUG: syslog_priority = LOG_DEBUG; break; case G_LOG_LEVEL_INFO: default: syslog_priority = LOG_INFO; break; } syslog(syslog_priority, "%s", message); } else g_print("%s", message); } static GMainLoop *g_main_loop; // Setup dbus server static int thd_dbus_server_proc(gboolean no_daemon) { DBusGConnection *bus; DBusGProxy *bus_proxy; GMainLoop *main_loop; GError *error = NULL; guint result; PrefObject *value_obj; thd_engine = NULL; // Initialize the GType/GObject system g_type_init(); // Create a main loop that will dispatch callbacks g_main_loop = main_loop = g_main_loop_new(NULL, FALSE); if (main_loop == NULL) { thd_log_error("Couldn't create GMainLoop:"); return THD_FATAL_ERROR; } if (dbus_enable) { bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error); if (error != NULL) { thd_log_error("Couldn't connect to session bus: %s:", error->message); return THD_FATAL_ERROR; } // Get a bus proxy instance bus_proxy = dbus_g_proxy_new_for_name(bus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); if (bus_proxy == NULL) { thd_log_error("Failed to get a proxy for D-Bus:"); return THD_FATAL_ERROR; } thd_log_debug("Registering the well-known name (%s)\n", THD_SERVICE_NAME); // register the well-known name if (!dbus_g_proxy_call(bus_proxy, "RequestName", &error, G_TYPE_STRING, THD_SERVICE_NAME, G_TYPE_UINT, 0, G_TYPE_INVALID, G_TYPE_UINT, &result, G_TYPE_INVALID)) { thd_log_error("D-Bus.RequestName RPC failed: %s\n", error->message); return THD_FATAL_ERROR; } thd_log_debug("RequestName returned %d.\n", result); if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { thd_log_error("Failed to get the primary well-known name:"); return THD_FATAL_ERROR; } value_obj = (PrefObject*) g_object_new(PREF_TYPE_OBJECT, NULL); if (value_obj == NULL) { thd_log_error("Failed to create one Value instance:"); return THD_FATAL_ERROR; } thd_log_debug("Registering it on the D-Bus.\n"); dbus_g_connection_register_g_object(bus, THD_SERVICE_OBJECT_PATH, G_OBJECT(value_obj)); } if (!no_daemon) { printf("Ready to serve requests: Daemonizing.. %d\n", thd_daemonize); thd_log_info( "thermald ver %s: Ready to serve requests: Daemonizing..\n", TD_DIST_VERSION); if (daemon(0, 1) != 0) { thd_log_error("Failed to daemonize.\n"); return THD_FATAL_ERROR; } } thd_engine = new cthd_engine_default(); if (exclusive_control) thd_engine->set_control_mode(EXCLUSIVE); // Initialize thermald objects thd_engine->set_poll_interval(thd_poll_interval); if (thd_engine->thd_engine_start(ignore_cpuid_check) != THD_SUCCESS) { thd_log_error("THD engine start failed: "); closelog(); exit(1); } // Start service requests on the D-Bus thd_log_debug("Start main loop\n"); g_main_loop_run(main_loop); thd_log_warn("Oops g main loop exit..\n"); return THD_SUCCESS; } void sig_int_handler(int signum) { // control+c handler thd_engine->thd_engine_terminate(); sleep(1); delete thd_engine; if (g_main_loop) g_main_loop_quit(g_main_loop); } // main function int main(int argc, char *argv[]) { gboolean show_version = FALSE; gboolean log_info = FALSE; gboolean log_debug = FALSE; gboolean no_daemon = FALSE; gboolean test_mode = FALSE; gint poll_interval = -1; gboolean success; GOptionContext *opt_ctx; thd_daemonize = TRUE; dbus_enable = FALSE; GOptionEntry options[] = { { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Print thermald version and exit"), NULL }, { "no-daemon", 0, 0, G_OPTION_ARG_NONE, &no_daemon, N_( "Don't become a daemon: Default is daemon mode"), NULL }, { "loglevel=info", 0, 0, G_OPTION_ARG_NONE, &log_info, N_( "log severity: info level and up"), NULL }, { "loglevel=debug", 0, 0, G_OPTION_ARG_NONE, &log_debug, N_( "log severity: debug level and up: Max logging"), NULL }, { "test-mode", 0, 0, G_OPTION_ARG_NONE, &test_mode, N_( "Test Mode only: Allow non root user"), NULL }, { "poll-interval", 0, 0, G_OPTION_ARG_INT, &poll_interval, N_("Poll interval in seconds: Poll for zone temperature changes. " "If want to disable polling set to zero."), NULL }, { "dbus-enable", 0, 0, G_OPTION_ARG_NONE, &dbus_enable, N_( "Enable Dbus."), NULL }, { "exclusive-control", 0, 0, G_OPTION_ARG_NONE, &exclusive_control, N_( "Take over thermal control from kernel thermal driver."), NULL }, { "ingore-cpuid-check", 0, 0, G_OPTION_ARG_NONE, &ignore_cpuid_check, N_("Ignore CPU ID check."), NULL }, { NULL } }; if (!g_module_supported()) { fprintf(stderr, _("GModules are not supported on your platform!\n")); exit(1); } /* Set locale to be able to use environment variables */ setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, TDLOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain(GETTEXT_PACKAGE); /* Parse options */ opt_ctx = g_option_context_new(NULL); g_option_context_set_translation_domain(opt_ctx, GETTEXT_PACKAGE); g_option_context_set_ignore_unknown_options(opt_ctx, FALSE); g_option_context_set_help_enabled(opt_ctx, TRUE); g_option_context_add_main_entries(opt_ctx, options, NULL); g_option_context_set_summary(opt_ctx, _( "Thermal daemon monitors temperature sensors and decides the best action " "based on the temperature readings and user preferences.")); success = g_option_context_parse(opt_ctx, &argc, &argv, NULL); g_option_context_free(opt_ctx); if (!success) { fprintf(stderr, _( "Invalid option. Please use --help to see a list of valid options.\n")); exit(1); } if (show_version) { fprintf(stdout, TD_DIST_VERSION "\n"); exit(0); } if (getuid() != 0 && !test_mode) { fprintf(stderr, _("You must be root to run thermald!\n")); exit(1); } if (g_mkdir_with_parents(TDRUNDIR, 0755) != 0) { fprintf(stderr, "Cannot create '%s': %s", TDRUNDIR, strerror(errno)); exit(1); } g_mkdir_with_parents(TDCONFDIR, 0755); // Don't care return value as directory // may already exist if (log_info) { thd_log_level |= G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO; } if (log_debug) { thd_log_level |= G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG; } if (poll_interval >= 0) { fprintf(stdout, "Polling enabled: %d\n", poll_interval); thd_poll_interval = poll_interval; } openlog("thermald", LOG_PID, LOG_USER | LOG_DAEMON | LOG_SYSLOG); // Don't care return val //setlogmask(LOG_CRIT | LOG_ERR | LOG_WARNING | LOG_NOTICE | LOG_DEBUG | LOG_INFO); thd_daemonize = !no_daemon; g_log_set_handler(NULL, G_LOG_LEVEL_MASK, thd_logger, NULL); if (no_daemon) signal(SIGINT, sig_int_handler); // dbus glib processing begin thd_dbus_server_proc(no_daemon); fprintf(stdout, "Exiting ..\n"); closelog(); } thermal_daemon-1.1-rc2/src/thd-dbus_interface.xml000066400000000000000000000021151225412722400220610ustar00rootroot00000000000000 thermal_daemon-1.1-rc2/src/thd_cdev.cpp000066400000000000000000000124221225412722400200730ustar00rootroot00000000000000/* * thd_cdev.cpp: thermal cooling class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ /* * This is parent class for all cooling devices. Most of the functions * are implemented in interface file except set state. * set_state uses the interface to get the current state, max state and * set the device state. * When state = 0, it causes reduction in the cooling device state * when state = 1, it increments cooling device state * When increments, it goes step by step, unless it finds that the temperature * can't be controlled by previous state with in def_poll_interval, otherwise * it will increase exponentially. * Reduction is always uses step by step to reduce ping pong affect. * */ #include "thd_cdev.h" #include "thd_engine.h" int cthd_cdev::thd_cdev_exponential_controller(int set_point, int target_temp, int temperature, int state, int zone_id) { curr_state = get_curr_state(); if ((min_state < max_state && curr_state < min_state) || (min_state > max_state && curr_state > min_state)) curr_state = min_state; max_state = get_max_state(); thd_log_debug("thd_cdev_set_%d:curr state %d max state %d\n", index, curr_state, max_state); if (state) { if ((min_state < max_state && curr_state < max_state) || (min_state > max_state && curr_state > max_state)) { int state = curr_state + inc_dec_val; if (trend_increase) { if (curr_pow == 0) base_pow_state = curr_state; ++curr_pow; state = base_pow_state + int_2_pow(curr_pow) * inc_dec_val; thd_log_info( "consecutive call, increment exponentially state %d\n", state); if ((min_state < max_state && state >= max_state) || (min_state > max_state && state <= max_state)) { state = max_state; curr_pow = 0; curr_state = max_state; } } else { curr_pow = 0; } trend_increase = true; if ((min_state < max_state && state > max_state) || (min_state > max_state && state < max_state)) state = max_state; thd_log_debug("op->device:%s %d\n", type_str.c_str(), state); set_curr_state(state, zone_id); } } else { curr_pow = 0; trend_increase = false; if (((min_state < max_state && curr_state > min_state) || (min_state > max_state && curr_state < min_state)) && auto_down_adjust == false) { int state = curr_state - inc_dec_val; if ((min_state < max_state && state < min_state) || (min_state > max_state && state > min_state)) state = min_state; thd_log_debug("op->device:%s %d\n", type_str.c_str(), state); set_curr_state(state, zone_id); } else { thd_log_debug("op->device: force min %s %d\n", type_str.c_str(), min_state); set_curr_state(min_state, zone_id); } } thd_log_info("Set : %d, %d, %d, %d, %d\n", set_point, temperature, index, get_curr_state(), max_state); thd_log_debug("<>thd_cdev_set_state index:%d state:%d\n", index, state); if (last_state == state && (tm - last_action_time) <= debounce_interval) { thd_log_debug("Ignore delay < debounce interval\n"); return THD_SUCCESS; } last_state = state; if (state) zone_mask |= (1 << zone_id); else { zone_mask &= ~(1 << zone_id); if (zone_mask != 0) { thd_log_debug( "skip to reduce current state as this is controlled by two zone actions and one is still on %x\n", zone_mask); return THD_SUCCESS; } } last_action_time = tm; curr_state = get_curr_state(); if (curr_state == get_min_state()) { control_begin(); } if (pid_enable) { pid_ctrl.set_target_temp(target_temp); ret = pid_ctrl.pid_output(temperature); ret += get_curr_state(); if (ret > get_max_state()) ret = get_max_state(); if (ret < get_min_state()) ret = get_min_state(); set_curr_state_raw(ret, zone_id); thd_log_info("Set : %d, %d, %d, %d, %d\n", set_point, temperature, index, get_curr_state(), max_state); ret = THD_SUCCESS; } else { ret = thd_cdev_exponential_controller(set_point, target_temp, temperature, state, zone_id); } if (curr_state == get_max_state()) { control_end(); } return ret; } ; int cthd_cdev::thd_cdev_set_min_state(int zone_id) { zone_mask &= ~(1 << zone_id); if (zone_mask != 0) { thd_log_debug( "skip to reduce current state as this is controlled by two zone actions and one is still on %x\n", zone_mask); return THD_SUCCESS; } trend_increase = false; set_curr_state(min_state, zone_id); return THD_SUCCESS; } thermal_daemon-1.1-rc2/src/thd_cdev.h000066400000000000000000000106211225412722400175370ustar00rootroot00000000000000/* * thd_cdev.h: thermal cooling class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_CDEV_H #define THD_CDEV_H #include #include "thd_common.h" #include "thd_sys_fs.h" #include "thd_preference.h" #include "thd_pid.h" class cthd_cdev { protected: int index; csys_fs cdev_sysfs; unsigned int trip_point; int max_state; int min_state; int curr_state; unsigned int zone_mask; int curr_pow; int base_pow_state; int inc_dec_val; bool auto_down_adjust; bool read_back; std::string type_str; int debounce_interval; time_t last_action_time; bool trend_increase; bool pid_enable; cthd_pid pid_ctrl; int last_state; private: unsigned int int_2_pow(int pow) { int i; int _pow = 1; for (i = 0; i < pow; ++i) _pow = _pow * 2; return _pow; } int thd_cdev_exponential_controller(int set_point, int target_temp, int temperature, int state, int arg); public: static const int default_debounce_interval = 3; // In seconds cthd_cdev(unsigned int _index, std::string control_path) : index(_index), cdev_sysfs(control_path.c_str()), trip_point(0), max_state( 0), min_state(0), curr_state(0), zone_mask(0), curr_pow(0), base_pow_state( 0), inc_dec_val(1), auto_down_adjust(false), read_back( true), debounce_interval(default_debounce_interval), last_action_time( 0), trend_increase(false), pid_enable(false), pid_ctrl(), last_state( 0) { } virtual ~cthd_cdev() { } virtual int thd_cdev_set_state(int set_point, int target_temp, int temperature, int state, int arg); virtual int thd_cdev_set_min_state(int zone_id); virtual void thd_cdev_set_min_state_param(int arg) { min_state = arg; } virtual void thd_cdev_set_max_state_param(int arg) { max_state = arg; } virtual void thd_cdev_set_read_back_param(bool arg) { read_back = arg; } virtual int thd_cdev_get_index() { return index; } virtual int init() { return 0; } ; virtual int control_begin() { if (pid_enable) { pid_ctrl.reset(); } return 0; } ; virtual int control_end() { return 0; } ; virtual void set_curr_state(int state, int arg) { } virtual void set_curr_state_raw(int state, int arg) { set_curr_state(state, arg); } virtual int get_curr_state() { return curr_state; } virtual int get_min_state() { return min_state; } virtual int get_max_state() { return max_state; } ; virtual int update() { return 0; } ; virtual void set_inc_dec_value(int value) { inc_dec_val = value; } virtual void set_down_adjust_control(bool value) { auto_down_adjust = value; } void set_debounce_interval(int interval) { debounce_interval = interval; } bool in_min_state() { if ((min_state < max_state && get_curr_state() <= min_state) || (min_state > max_state && get_curr_state() >= min_state)) return true; return false; } bool in_max_state() { if ((min_state < max_state && get_curr_state() >= get_max_state()) || (min_state > max_state && get_curr_state() <= get_max_state())) return true; return false; } std::string get_cdev_type() { return type_str; } std::string get_base_path() { return cdev_sysfs.get_base_path(); } void set_cdev_type(std::string _type_str) { type_str = _type_str; } void set_pid_param(double kp, double ki, double kd) { pid_ctrl.kp = kp; pid_ctrl.ki = ki; pid_ctrl.kd = kd; thd_log_info("set_pid_param %d [%g.%g,%g]\n", index, kp, ki, kd); } void enable_pid() { thd_log_info("PID control enabled %d\n", index); pid_enable = true; } void cdev_dump() { thd_log_info("%d: %s, C:%d MN: %d MX:%d ST:%d pt:%s rd_bk %d \n", index, type_str.c_str(), curr_state, min_state, max_state, inc_dec_val, get_base_path().c_str(), read_back); } }; #endif thermal_daemon-1.1-rc2/src/thd_cdev_cpufreq.cpp000066400000000000000000000115571225412722400216300ustar00rootroot00000000000000/* * thd_cdev_pstates.cpp: thermal cooling class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ /* Control P states using cpufreq. Each step reduces to next lower frequency * */ #include "thd_cdev_cpufreq.h" #include "thd_engine.h" int cthd_cdev_cpufreq::init() { // Get number of CPUs if (cdev_sysfs.exists("present")) { std::string count_str; size_t p0 = 0, p1; cdev_sysfs.read("present", count_str); p1 = count_str.find_first_of("-", p0); std::string token1 = count_str.substr(p0, p1 - p0); std::istringstream(token1) >> cpu_start_index; std::string token2 = count_str.substr(p1 + 1); std::istringstream(token2) >> cpu_end_index; } else { cpu_start_index = 0; cpu_end_index = 0; return THD_ERROR; } thd_log_debug("pstate CPU present %d-%d\n", cpu_start_index, cpu_end_index); // Get list of available frequencies for each CPU // Assuming every core supports same sets of frequencies, so // just reading for cpu0 std::vector _cpufreqs; if (cdev_sysfs.exists("cpu0/cpufreq/scaling_available_frequencies")) { std::string p = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies"; std::ifstream f(p.c_str(), std::fstream::in); if (f.fail()) return -EINVAL; while (!f.eof()) { std::string token; f >> token; if (!f.bad()) { if (!token.empty()) _cpufreqs.push_back(token); } } f.close(); } else return THD_ERROR; // Check scaling max frequency and min frequency // Remove frequencies above and below this in the freq list // The available list contains these frequencies even if they are not allowed unsigned int scaling_min_frqeuency = 0; unsigned int scaling_max_frqeuency = 0; for (int i = cpu_start_index; i <= cpu_end_index; ++i) { std::stringstream str; std::string freq_str; str << "cpu" << i << "/cpufreq/scaling_min_freq"; if (cdev_sysfs.exists(str.str())) { cdev_sysfs.read(str.str(), freq_str); unsigned int freq_int; std::istringstream(freq_str) >> freq_int; if (scaling_min_frqeuency == 0 || freq_int < scaling_min_frqeuency) scaling_min_frqeuency = freq_int; } } for (int i = cpu_start_index; i <= cpu_end_index; ++i) { std::stringstream str; std::string freq_str; str << "cpu" << i << "/cpufreq/scaling_max_freq"; if (cdev_sysfs.exists(str.str())) { cdev_sysfs.read(str.str(), freq_str); unsigned int freq_int; std::istringstream(freq_str) >> freq_int; if (scaling_max_frqeuency == 0 || freq_int > scaling_max_frqeuency) scaling_max_frqeuency = freq_int; } } thd_log_debug("cpu freq max %u min %u\n", scaling_max_frqeuency, scaling_min_frqeuency); for (unsigned int i = 0; i < _cpufreqs.size(); ++i) { thd_log_debug("cpu freq Add %d: %s\n", i, _cpufreqs[i].c_str()); unsigned int freq_int; std::istringstream(_cpufreqs[i]) >> freq_int; if (freq_int >= scaling_min_frqeuency && freq_int <= scaling_max_frqeuency) { cpufreqs.push_back(freq_int); } } for (unsigned int i = 0; i < cpufreqs.size(); ++i) { thd_log_debug("cpu freq %d: %d\n", i, cpufreqs[i]); } pstate_active_freq_index = 0; return THD_SUCCESS; } void cthd_cdev_cpufreq::set_curr_state(int state, int arg) { if (state < (int) cpufreqs.size()) { thd_log_debug("cpu freq set_curr_stat %d: %d\n", state, cpufreqs[state]); if (cpu_index == -1) { for (int i = cpu_start_index; i <= cpu_end_index; ++i) { std::stringstream str; str << "cpu" << i << "/cpufreq/scaling_max_freq"; if (cdev_sysfs.exists(str.str())) { std::stringstream speed; speed << cpufreqs[state]; cdev_sysfs.write(str.str(), speed.str()); } pstate_active_freq_index = state; curr_state = state; } } else { if (thd_engine->apply_cpu_operation(cpu_index)) { std::stringstream str; str << "cpu" << cpu_index << "/cpufreq/scaling_max_freq"; if (cdev_sysfs.exists(str.str())) { std::stringstream speed; speed << cpufreqs[state]; cdev_sysfs.write(str.str(), speed.str()); } pstate_active_freq_index = state; curr_state = state; } } } } int cthd_cdev_cpufreq::get_max_state() { return cpufreqs.size(); } int cthd_cdev_cpufreq::update() { return init(); } thermal_daemon-1.1-rc2/src/thd_cdev_cpufreq.h000066400000000000000000000026711225412722400212720ustar00rootroot00000000000000/* * thd_cdev_pstates.h: thermal cooling class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_CDEV_PSTATES_H_ #define THD_CDEV_PSTATES_H_ #include #include #include "thd_cdev.h" class cthd_cdev_cpufreq: public cthd_cdev { private: int cpu_start_index; int cpu_end_index; std::vector cpufreqs; int pstate_active_freq_index; int turbo_state; std::string last_governor; int cpu_index; public: cthd_cdev_cpufreq(unsigned int _index, int _cpu_index) : cthd_cdev(_index, "/sys/devices/system/cpu/"), cpu_index(_cpu_index) { } int init(); void set_curr_state(int state, int arg); int get_max_state(); int update(); }; #endif /* THD_CDEV_PSTATES_H_ */ thermal_daemon-1.1-rc2/src/thd_cdev_gen_sysfs.cpp000066400000000000000000000026271225412722400221610ustar00rootroot00000000000000/* * cthd_sysfs_gen_sysfs.cpp: thermal cooling class interface * for non thermal cdev sysfs * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_cdev_gen_sysfs.h" int cthd_gen_sysfs_cdev::update() { if (cdev_sysfs.exists()) { std::string state_str; cdev_sysfs.read("", state_str); std::istringstream(state_str) >> curr_state; min_state = max_state = curr_state; } else return THD_ERROR; return THD_SUCCESS; } void cthd_gen_sysfs_cdev::set_curr_state(int state, int arg) { std::stringstream state_str; state_str << state; thd_log_debug("set cdev state index %d state %d\n", index, state); cdev_sysfs.write("", state_str.str()); curr_state = state; } thermal_daemon-1.1-rc2/src/thd_cdev_gen_sysfs.h000066400000000000000000000023541225412722400216230ustar00rootroot00000000000000/* * cthd_sysfs_gen_sysfs.h: thermal cooling class interface * for non thermal cdev sysfs * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_CDEV_GEN_SYSFS_H_ #define THD_CDEV_GEN_SYSFS_H_ #include "thd_cdev.h" class cthd_gen_sysfs_cdev: public cthd_cdev { protected: public: cthd_gen_sysfs_cdev(unsigned int _index, std::string control_path) : cthd_cdev(_index, control_path) { } virtual void set_curr_state(int state, int arg); virtual int update(); }; #endif /* THD_CDEV_GEN_SYSFS_H_ */ thermal_daemon-1.1-rc2/src/thd_cdev_intel_pstate_driver.cpp000066400000000000000000000064331225412722400242260ustar00rootroot00000000000000/* * thd_sysfs_intel_pstate_driver.cpp: thermal cooling class implementation * using Intel p state driver * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_cdev_intel_pstate_driver.h" /* * This implementation allows to control get max state and * set current state of using Intel P state driver * P state drives uses a percent count. 100% means full * performance, setting anything lower limits performance. * Each lower state reduces performance by unit value. * unit value is calculated using number of possible p states * . Each step reduces bye one p state. * Contents of "/sys/devices/system/cpu/intel_pstate/" max_perf_pct, min_perf_pct, no_turbo */ void cthd_intel_p_state_cdev::set_curr_state(int state, int arg) { std::stringstream tc_state_dev; int new_state; tc_state_dev << "/max_perf_pct"; if (cdev_sysfs.exists(tc_state_dev.str())) { std::stringstream state_str; if (state == 0) new_state = 100; else { new_state = 100 - (state + min_compensation) * unit_value; } state_str << new_state; thd_log_debug("set cdev state index %d state %d percent %d\n", index, state, new_state); if (new_state <= turbo_disable_percent) set_turbo_disable_status(true); else set_turbo_disable_status(false); if (cdev_sysfs.write(tc_state_dev.str(), state_str.str()) < 0) curr_state = (state == 0) ? 0 : max_state; else curr_state = state; } else curr_state = (state == 0) ? 0 : max_state; } void cthd_intel_p_state_cdev::set_turbo_disable_status(bool enable) { std::stringstream tc_state_dev; if (enable == turbo_status) { return; } tc_state_dev << "/no_turbo"; if (enable) { cdev_sysfs.write(tc_state_dev.str(), "1"); thd_log_info("turbo disabled \n"); } else { cdev_sysfs.write(tc_state_dev.str(), "0"); thd_log_info("turbo enabled \n"); } turbo_status = enable; } int cthd_intel_p_state_cdev::get_max_state() { return max_state; } int cthd_intel_p_state_cdev::update() { std::stringstream tc_state_dev; tc_state_dev << "/max_perf_pct"; if (cdev_sysfs.exists(tc_state_dev.str())) { std::string state_str; cdev_sysfs.read(tc_state_dev.str(), state_str); std::istringstream(state_str) >> curr_state; } else { return THD_ERROR; } thd_log_info("Use Default pstate drv settings\n"); max_state = default_max_state; min_compensation = 0; unit_value = 100 / max_state; curr_state = 0; thd_log_debug( "cooling dev index:%d, curr_state:%d, max_state:%d, unit:%f, min_com:%d, type:%s\n", index, curr_state, max_state, unit_value, min_compensation, type_str.c_str()); return THD_SUCCESS; } thermal_daemon-1.1-rc2/src/thd_cdev_intel_pstate_driver.h000066400000000000000000000031721225412722400236700ustar00rootroot00000000000000/* * thd_sysfs_intel_pstate_driver.h: thermal cooling class interface * using Intel p state driver * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_CDEV_INTEL_PSATATE_DRIVER_H_ #define THD_CDEV_INTEL_PSATATE_DRIVER_H_ #include "thd_cdev.h" class cthd_intel_p_state_cdev: public cthd_cdev { private: float unit_value; int min_compensation; int max_offset; bool turbo_status; void set_turbo_disable_status(bool enable); public: static const int intel_pstate_limit_ratio = 2; static const int default_max_state = 10; static const int turbo_disable_percent = 70; cthd_intel_p_state_cdev(unsigned int _index) : cthd_cdev(_index, "/sys/devices/system/cpu/intel_pstate/"), unit_value( 1), min_compensation(0), max_offset(0), turbo_status(false) { } ; void set_curr_state(int state, int arg); int get_max_state(); int update(); }; #endif /* THD_CDEV_INTEL_PSATATE_DRIVER_H_ */ thermal_daemon-1.1-rc2/src/thd_cdev_msr_rapl.cpp000066400000000000000000000055641225412722400220030ustar00rootroot00000000000000/* * thd_cdev_rapl_msr.cpp: thermal cooling class implementation * using RAPL MSRs. * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include #include #include #include "thd_cdev_msr_rapl.h" void cthd_cdev_rapl_msr::set_curr_state(int state, int arg) { int new_state; if (!control_start && state == inc_dec_val) { thd_log_debug("rapl state control begin\n"); rapl.store_pkg_power_limit(); control_start = true; } if (state < inc_dec_val) { new_state = 0; curr_state = 0; } else { new_state = phy_max - state; curr_state = state; thd_log_debug("rapl state = %d new_state = %d\n", state, new_state); rapl.set_pkg_power_limit(1, new_state); //thd_engine->def_poll_interval/1000 - 1, new_state); } if (state < inc_dec_val) { thd_log_debug("rapl state control end\n"); rapl.restore_pkg_power_limit(); control_start = false; } } int cthd_cdev_rapl_msr::get_max_state() { return max_state; } int cthd_cdev_rapl_msr::update() { int ret; if (!rapl.pkg_domain_present()) return THD_ERROR; if (!rapl.pp0_domain_present()) return THD_ERROR; if (rapl.get_power_unit() < 0) return THD_ERROR; if (rapl.get_time_unit() < 0) return THD_ERROR; ret = rapl.get_pkg_power_info(&thermal_spec_power, &max_power, &min_power, &max_time_window); if (ret < 0) return ret; thd_log_debug( "Pkg Power Info: Thermal spec %f watts, max %f watts, min %f watts, max time window %f seconds\n", thermal_spec_power, max_power, min_power, max_time_window); max_state = 0; if (thermal_spec_power > 0) phy_max = (int) thermal_spec_power * 1000; // change to milliwatts else if (max_power > 0) phy_max = (int) max_power * 1000; // // change to milliwatts else return THD_ERROR; set_inc_dec_value(phy_max * (float) rapl_power_dec_percent / 100); if (inc_dec_val == 0) set_inc_dec_value(phy_max * (float) rapl_power_dec_percent * 2 / 100); if (inc_dec_val == 0) // power limit is too small inc_dec_val = 1; max_state = phy_max - ((float) phy_max * rapl_low_limit_percent / 100); thd_log_debug("RAPL phy_max %d max_state %d inc_dec %d \n", phy_max, max_state, inc_dec_val); return THD_SUCCESS; } thermal_daemon-1.1-rc2/src/thd_cdev_msr_rapl.h000066400000000000000000000031031225412722400214330ustar00rootroot00000000000000/* * thd_cdev_mar_rapl.h: thermal cooling class interface * using RAPL MSRs. * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_CDEV_RAPL_MSR_H_ #define THD_CDEV_RAPL_MSR_H_ #include "thd_cdev.h" #include "thd_rapl_interface.h" class cthd_cdev_rapl_msr: public cthd_cdev { protected: int max_state; c_rapl_interface rapl; double thermal_spec_power; double max_power; double min_power; double max_time_window; int phy_max; bool control_start; public: static const int rapl_low_limit_percent = 25; static const int rapl_power_dec_percent = 5; cthd_cdev_rapl_msr(unsigned int _index, int _cpu_index) : cthd_cdev(_index, "/sys/devices/system/cpu/"), max_state(0), rapl( 0), control_start(false) { } void set_curr_state(int state, int arg); int get_max_state(); virtual int update(); }; #endif /* THD_CDEV_TSTATES_H_ */ thermal_daemon-1.1-rc2/src/thd_cdev_order_parser.cpp000066400000000000000000000051011225412722400226360ustar00rootroot00000000000000/* * thd_cdev_order_parser.cpp: Specify cdev order * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_cdev_order_parser.h" #include "thd_sys_fs.h" cthd_cdev_order_parse::cthd_cdev_order_parse() : doc(NULL), root_element(NULL) { std::string name = TDCONFDIR; filename = name + "/" "thermal-cpu-cdev-order.xml"; } int cthd_cdev_order_parse::parser_init() { doc = xmlReadFile(filename.c_str(), NULL, 0); if (doc == NULL) { thd_log_warn("error: could not parse file %s\n", filename.c_str()); return THD_ERROR; } root_element = xmlDocGetRootElement(doc); if (root_element == NULL) { thd_log_warn("error: could not get root element \n"); return THD_ERROR; } return THD_SUCCESS; } int cthd_cdev_order_parse::start_parse() { parse(root_element, doc); return THD_SUCCESS; } void cthd_cdev_order_parse::parser_deinit() { xmlFreeDoc(doc); } int cthd_cdev_order_parse::parse_new_cdev(xmlNode * a_node, xmlDoc *doc) { xmlNode *cur_node = NULL; char *tmp_value; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { tmp_value = (char *) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (tmp_value) { thd_log_info("node type: Element, name: %s value: %s\n", cur_node->name, tmp_value); cdev_order_list.push_back(tmp_value); xmlFree(tmp_value); } } } return THD_SUCCESS; } int cthd_cdev_order_parse::parse(xmlNode * a_node, xmlDoc *doc) { xmlNode *cur_node = NULL; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { if (!strcmp((const char*) cur_node->name, "CoolingDeviceOrder")) { parse_new_cdev(cur_node->children, doc); } } } return THD_SUCCESS; } int cthd_cdev_order_parse::get_order_list(std::vector &list) { list = cdev_order_list; return 0; } thermal_daemon-1.1-rc2/src/thd_cdev_order_parser.h000066400000000000000000000026221225412722400223100ustar00rootroot00000000000000/* * thd_cdev_order_parser.h: Specify cdev order * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_CDEV_ORDER_PARSE_H #define THD_CDEV_ORDER_PARSE_H #include #include #include #include #include "thermald.h" class cthd_cdev_order_parse { private: xmlDoc *doc; xmlNode *root_element; std::string filename; std::vector cdev_order_list; int parse(xmlNode * a_node, xmlDoc *doc); int parse_new_cdev(xmlNode * a_node, xmlDoc *doc); public: cthd_cdev_order_parse(); int parser_init(); void parser_deinit(); int start_parse(); int get_order_list(std::vector &list); }; #endif thermal_daemon-1.1-rc2/src/thd_cdev_rapl.cpp000066400000000000000000000116401225412722400211120ustar00rootroot00000000000000/* * cthd_cdev_rapl.cpp: thermal cooling class implementation * using RAPL * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_cdev_rapl.h" #include "thd_engine.h" /* This uses Intel RAPL driver to cool the system. RAPL driver show * mas thermal spec power in max_state. Each state can compensate * rapl_power_dec_percent, from the max state. * */ void cthd_sysfs_cdev_rapl::set_curr_state(int state, int arg) { std::stringstream tc_state_dev; std::stringstream state_str; int new_state; if (state < inc_dec_val) { new_state = 0; curr_state = 0; cdev_sysfs.write("enabled", "0"); new_state = phy_max; } else { new_state = phy_max - state; curr_state = state; cdev_sysfs.write("enabled", "1"); } state_str << new_state; thd_log_info("set cdev state index %d state %d wr:%d\n", index, state, new_state); tc_state_dev << "constraint_" << constraint_index << "_power_limit_uw"; if (cdev_sysfs.write(tc_state_dev.str(), state_str.str()) < 0) curr_state = (state == 0) ? 0 : max_state; } void cthd_sysfs_cdev_rapl::set_curr_state_raw(int state, int arg) { std::stringstream state_str; std::stringstream tc_state_dev; int new_state; if (state <= min_state) new_state = phy_max; else new_state = phy_max - state; curr_state = state; state_str << new_state; tc_state_dev << "constraint_" << constraint_index << "_power_limit_uw"; if (cdev_sysfs.write(tc_state_dev.str(), state_str.str()) < 0) curr_state = (state == 0) ? 0 : max_state; thd_log_info("set cdev state raw index %d state %d wr:%d\n", index, state, new_state); } int cthd_sysfs_cdev_rapl::get_curr_state() { return curr_state; } int cthd_sysfs_cdev_rapl::get_max_state() { return max_state; } int cthd_sysfs_cdev_rapl::update() { int i; std::stringstream temp_str; int _index = -1; unsigned long constraint_phy_max; for (i = 0; i < rapl_no_time_windows; ++i) { temp_str << "constraint_" << i << "_name"; if (cdev_sysfs.exists(temp_str.str())) { std::string type_str; cdev_sysfs.read(temp_str.str(), type_str); if (type_str == "long_term") { _index = i; break; } } } if (_index < 0) { thd_log_info("powercap RAPL no long term time window\n"); return THD_ERROR; } std::string power_limit; temp_str.str(std::string()); temp_str << "constraint_" << _index << "_max_power_uw"; if (!cdev_sysfs.exists(temp_str.str())) { thd_log_info("powercap RAPL no max power limit range %s \n", temp_str.str().c_str()); return THD_ERROR; } cdev_sysfs.read(temp_str.str(), power_limit); std::istringstream(power_limit) >> phy_max; std::stringstream temp_power_str; std::string current_power_limit; temp_power_str.str(std::string()); temp_power_str << "constraint_" << _index << "_power_limit_uw"; if (!cdev_sysfs.exists(temp_power_str.str())) { thd_log_info("powercap RAPL no power limit uw %s \n", temp_str.str().c_str()); return THD_ERROR; } cdev_sysfs.read(temp_power_str.str(), current_power_limit); std::istringstream(current_power_limit) >> constraint_phy_max; if (constraint_phy_max > phy_max) { thd_log_info( "Default constraint power limit is more than max power %lu:%lu\n", constraint_phy_max, phy_max); phy_max = constraint_phy_max; } thd_log_info("powercap RAPL max power limit range %lu \n", phy_max); set_inc_dec_value(phy_max * (float) rapl_power_dec_percent / 100); max_state = phy_max; max_state -= (float) max_state * rapl_low_limit_percent / 100; std::stringstream time_window; temp_str.str(std::string()); temp_str << "constraint_" << _index << "_time_window_us"; if (!cdev_sysfs.exists(temp_str.str())) { thd_log_info("powercap RAPL no time_window_us %s \n", temp_str.str().c_str()); return THD_ERROR; } time_window << def_rapl_time_window; cdev_sysfs.write(temp_str.str(), time_window.str()); std::stringstream enable; temp_str.str(std::string()); temp_str << "enabled"; if (!cdev_sysfs.exists(temp_str.str())) { thd_log_info("powercap RAPL no enabled %s \n", temp_str.str().c_str()); return THD_ERROR; } cdev_sysfs.write(temp_str.str(), "0"); thd_log_debug("RAPL max limit %d increment: %d\n", max_state, inc_dec_val); constraint_index = _index; set_pid_param(1000, 100, 10); //enable_pid(); return THD_SUCCESS; } thermal_daemon-1.1-rc2/src/thd_cdev_rapl.h000066400000000000000000000032661225412722400205640ustar00rootroot00000000000000/* * cthd_cdev_rapl.h: thermal cooling class interface * using RAPL * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_CDEV_RAPL_H_ #define THD_CDEV_RAPL_H_ #include "thd_cdev.h" #include "thd_sys_fs.h" class cthd_sysfs_cdev_rapl: public cthd_cdev { private: unsigned long phy_max; int package_id; int constraint_index; public: static const int rapl_no_time_windows = 6; static const long def_rapl_time_window = 1000000; // micro seconds static const int rapl_low_limit_percent = 25; static const int rapl_power_dec_percent = 5; cthd_sysfs_cdev_rapl(unsigned int _index, int package) : cthd_cdev(_index, "/sys/devices/virtual/powercap/intel-rapl/intel-rapl:0/"), package_id( package), constraint_index(0) { } virtual void set_curr_state(int state, int arg); virtual int get_curr_state(); virtual int get_max_state(); virtual int update(); virtual void set_curr_state_raw(int state, int arg); }; #endif /* THD_CDEV_RAPL_H_ */ thermal_daemon-1.1-rc2/src/thd_cdev_therm_sys_fs.cpp000066400000000000000000000062061225412722400226630ustar00rootroot00000000000000/* * cthd_sysfs_cdev.cpp: thermal cooling class implementation * for thermal sysfs * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_cdev_therm_sys_fs.h" #include "thd_engine.h" /* This uses ACPI style thermal sysfs interface to set states. * It expects, max_state. curr_state in thermal sysfs and uses * these sysfs-files to control. * */ int cthd_sysfs_cdev::update() { std::stringstream tc_state_dev; tc_state_dev << "cooling_device" << index << "/cur_state"; if (cdev_sysfs.exists(tc_state_dev.str())) { std::string state_str; cdev_sysfs.read(tc_state_dev.str(), state_str); std::istringstream(state_str) >> curr_state; } else curr_state = 0; std::stringstream tc_max_state_dev; tc_max_state_dev << "cooling_device" << index << "/max_state"; if (cdev_sysfs.exists(tc_max_state_dev.str())) { std::string state_str; cdev_sysfs.read(tc_max_state_dev.str(), state_str); std::istringstream(state_str) >> max_state; } else max_state = 0; std::stringstream tc_type_dev; tc_type_dev << "cooling_device" << index << "/type"; if (cdev_sysfs.exists(tc_type_dev.str())) { cdev_sysfs.read(tc_type_dev.str(), type_str); } thd_log_debug("cooling dev %d:%d:%d:%s\n", index, curr_state, max_state, type_str.c_str()); return THD_SUCCESS; } int cthd_sysfs_cdev::get_max_state() { std::stringstream tc_state_dev; tc_state_dev << "cooling_device" << index << "/max_state"; if (cdev_sysfs.exists(tc_state_dev.str())) { std::string state_str; cdev_sysfs.read(tc_state_dev.str(), state_str); std::istringstream(state_str) >> max_state; } else max_state = 0; return max_state; } void cthd_sysfs_cdev::set_curr_state(int state, int arg) { std::stringstream tc_state_dev; tc_state_dev << "cooling_device" << index << "/cur_state"; if (cdev_sysfs.exists(tc_state_dev.str())) { std::stringstream state_str; state_str << state; thd_log_debug("set cdev state index %d state %d\n", index, state); cdev_sysfs.write(tc_state_dev.str(), state_str.str()); curr_state = state; } else curr_state = 0; } int cthd_sysfs_cdev::get_curr_state() { if (!read_back) { return curr_state; } std::stringstream tc_state_dev; tc_state_dev << "cooling_device" << index << "/cur_state"; if (cdev_sysfs.exists(tc_state_dev.str())) { std::string state_str; cdev_sysfs.read(tc_state_dev.str(), state_str); std::istringstream(state_str) >> curr_state; } else curr_state = 0; return curr_state; } thermal_daemon-1.1-rc2/src/thd_cdev_therm_sys_fs.h000066400000000000000000000024371225412722400223320ustar00rootroot00000000000000/* * cthd_sysfs_cdev.cpp: thermal cooling class interface * for thermal sysfs * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_CDEV_THERM_SYS_FS_H_ #define THD_CDEV_THERM_SYS_FS_H_ #include "thd_cdev.h" class cthd_sysfs_cdev: public cthd_cdev { protected: public: cthd_sysfs_cdev(unsigned int _index, std::string control_path) : cthd_cdev(_index, control_path) { } virtual void set_curr_state(int state, int arg); virtual int get_curr_state(); virtual int get_max_state(); virtual int update(); }; #endif /* THD_CDEV_THERM_SYS_FS_H_ */ thermal_daemon-1.1-rc2/src/thd_common.h000066400000000000000000000016611225412722400201120ustar00rootroot00000000000000/* thd_msr.cpp: thermal engine common definitions * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_COMMON_H_ #define THD_COMMON_H_ #include "thermald.h" #endif /* THD_COMMON_H_ */ thermal_daemon-1.1-rc2/src/thd_engine.cpp000066400000000000000000000447431225412722400204320ustar00rootroot00000000000000/* * thd_engine.cpp: thermal engine class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ /* This class acts as the parent class of all thermal engines. Main functions are: * - Initialization * - Read cooling devices and thermal zones(sensors), which can be overridden in child * - Starts a poll loop, All the thermal processing happens in this thread's context * - Message processing loop * - If either a poll interval is expired or notified via netlink, it schedules a * a change notification on the associated cthd_zone to read and process. */ #include #include #include #include #include "thd_engine.h" #include "thd_cdev_therm_sys_fs.h" #include "thd_zone_therm_sys_fs.h" static void *cthd_engine_thread(void *arg); cthd_engine::cthd_engine() : cdev_cnt(0), zone_count(0), sensor_count(0), parse_thermal_zone_success( false), parse_thermal_cdev_success(false), poll_timeout_msec( -1), wakeup_fd(0), control_mode(COMPLEMENTRY), write_pipe_fd(0), preference( 0), status(true), thz_last_time(0), terminate(false), genuine_intel( 0), has_invariant_tsc(0), has_aperf(0), proc_list_matched( false), poll_interval_sec(0), poll_sensor_mask(0) { thd_engine = pthread_t(); thd_attr = pthread_attr_t(); thd_cond_var = pthread_cond_t(); thd_cond_mutex = pthread_mutex_t(); } cthd_engine::~cthd_engine() { unsigned int i; for (i = 0; i < sensors.size(); ++i) { delete sensors[i]; } sensors.clear(); for (i = 0; i < zones.size(); ++i) { delete zones[i]; } zones.clear(); for (i = 0; i < cdevs.size(); ++i) { delete cdevs[i]; } cdevs.clear(); } void cthd_engine::thd_engine_thread() { unsigned int i; int n; thd_log_info("thd_engine_thread begin\n"); for (;;) { if (terminate) break; n = poll(poll_fds, THD_NUM_OF_POLL_FDS, poll_timeout_msec); thd_log_debug("poll exit %d \n", n); if (n < 0) { thd_log_warn("Write to pipe failed \n"); continue; } if (n == 0) { if (!status) { thd_log_warn("Thermal Daemon is disabled \n"); continue; } // Polling mode enabled. Trigger a temp change message for (i = 0; i < zones.size(); ++i) { cthd_zone *zone = zones[i]; zone->zone_temperature_notification(0, 0); } } if (poll_fds[0].revents & POLLIN) { // Kobj uevent if (kobj_uevent.check_for_event()) { time_t tm; time(&tm); thd_log_debug("kobj uevent for thermal\n"); if ((tm - thz_last_time) >= thz_notify_debounce_interval) { for (i = 0; i < zones.size(); ++i) { cthd_zone *zone = zones[i]; zone->zone_temperature_notification(0, 0); } } else { thd_log_debug("IGNORE THZ kevent\n"); } thz_last_time = tm; } } if (poll_fds[wakeup_fd].revents & POLLIN) { message_capsul_t msg; thd_log_debug("wakeup fd event\n"); int result = read(poll_fds[wakeup_fd].fd, &msg, sizeof(message_capsul_t)); if (result < 0) { thd_log_warn("read on wakeup fd failed\n"); poll_fds[wakeup_fd].revents = 0; continue; } if (proc_message(&msg) < 0) { thd_log_debug("Terminating thread..\n"); } } } thd_log_debug("thd_engine_thread_end\n"); } bool cthd_engine::set_preference(const int pref) { return true; } int cthd_engine::thd_engine_start(bool ignore_cpuid_check) { int ret; int wake_fds[2]; if (ignore_cpuid_check) { thd_log_debug("Ignore CPU ID check for MSRs \n"); proc_list_matched = true; } else check_cpu_id(); // Pipe is used for communication between two processes ret = pipe(wake_fds); if (ret) { thd_log_error("Thermal sysfs: pipe creation failed %d: ", ret); return THD_FATAL_ERROR; } fcntl(wake_fds[0], F_SETFL, O_NONBLOCK); fcntl(wake_fds[1], F_SETFL, O_NONBLOCK); write_pipe_fd = wake_fds[1]; wakeup_fd = THD_NUM_OF_POLL_FDS - 1; memset(poll_fds, 0, sizeof(poll_fds)); poll_fds[wakeup_fd].fd = wake_fds[0]; poll_fds[wakeup_fd].events = POLLIN; poll_fds[wakeup_fd].revents = 0; poll_timeout_msec = -1; if (poll_interval_sec) { thd_log_warn("Polling mode is enabled: %d\n", poll_interval_sec); poll_timeout_msec = poll_interval_sec * 1000; } ret = read_thermal_sensors(); if (ret != THD_SUCCESS) { thd_log_error("Thermal sysfs Error in reading sensors"); // This is a fatal error and daemon will exit return THD_FATAL_ERROR; } ret = read_cooling_devices(); if (ret != THD_SUCCESS) { thd_log_error("Thermal sysfs Error in reading cooling devs"); // This is a fatal error and daemon will exit return THD_FATAL_ERROR; } ret = read_thermal_zones(); if (ret != THD_SUCCESS) { thd_log_error("No thermal sensors found"); // This is a fatal error and daemon will exit return THD_FATAL_ERROR; } // Check if polling is disabled and sensors don't support // async mode, in that enable force polling if (!poll_interval_sec) { unsigned int i; for (i = 0; i < zones.size(); ++i) { cthd_zone *zone = zones[i]; if (!zone->zone_active_status()) continue; if (!zone->check_sensor_async_status()) { thd_log_warn( "Polling will be enabled as some sensors are not capable to notify asynchnously \n"); poll_timeout_msec = def_poll_interval; break; } } if (i == zones.size()) { thd_log_info("Proceed without polling mode! \n"); } } poll_fds[0].fd = kobj_uevent.kobj_uevent_open(); if (poll_fds[0].fd < 0) { thd_log_warn("Invalid kobj_uevent handle\n"); goto skip_kobj; } thd_log_info("FD = %d\n", poll_fds[0].fd); kobj_uevent.register_dev_path( (char *) "/devices/virtual/thermal/thermal_zone"); poll_fds[0].events = POLLIN; poll_fds[0].revents = 0; skip_kobj: #ifndef DISABLE_PTHREAD // condition variable pthread_cond_init(&thd_cond_var, NULL); pthread_mutex_init(&thd_cond_mutex, NULL); // Create thread pthread_attr_init(&thd_attr); pthread_attr_setdetachstate(&thd_attr, PTHREAD_CREATE_DETACHED); ret = pthread_create(&thd_engine, &thd_attr, cthd_engine_thread, (void*) this); #else { pid_t childpid; if((childpid = fork()) == - 1) { perror("fork"); exit(1); } if(childpid == 0) { /* Child process closes up input side of pipe */ close(wake_fds[1]); cthd_engine_thread((void*)this); } else { /* Parent process closes up output side of pipe */ close(wake_fds[0]); } } #endif preference = thd_pref.get_preference(); thd_log_info("Current user preference is %d\n", preference); if (control_mode == EXCLUSIVE) { thd_log_info("Control is taken over from kernel\n"); takeover_thermal_control(); } return ret; } int cthd_engine::thd_engine_stop() { return THD_SUCCESS; } static void *cthd_engine_thread(void *arg) { cthd_engine *obj = (cthd_engine*) arg; obj->thd_engine_thread(); return NULL; } void cthd_engine::send_message(message_name_t msg_id, int size, unsigned char *msg) { message_capsul_t msg_cap; memset(&msg_cap, 0, sizeof(message_capsul_t)); msg_cap.msg_id = msg_id; msg_cap.msg_size = (size > MAX_MSG_SIZE) ? MAX_MSG_SIZE : size; if (msg) memcpy(msg_cap.msg, msg, msg_cap.msg_size); int result = write(write_pipe_fd, &msg_cap, sizeof(message_capsul_t)); if (result < 0) thd_log_warn("Write to pipe failed \n"); } void cthd_engine::process_pref_change() { int new_pref; thd_pref.refresh(); new_pref = thd_pref.get_preference(); if (new_pref == PREF_DISABLED) { status = false; return; } status = true; if (preference != new_pref) { thd_log_warn("Preference changed \n"); } preference = new_pref; for (unsigned int i = 0; i < zones.size(); ++i) { cthd_zone *zone = zones[i]; zone->update_zone_preference(); } if (control_mode == EXCLUSIVE) { thd_log_info("Control is taken over from kernel\n"); takeover_thermal_control(); } } void cthd_engine::thd_engine_terminate() { send_message(TERMINATE, 0, NULL); sleep(1); process_terminate(); } int cthd_engine::thd_engine_set_user_max_temp(const char *zone_type, const char *user_set_point) { std::string str(user_set_point); cthd_zone *zone; thd_log_debug("thd_engine_set_user_set_point %s\n", user_set_point); std::locale loc; if (std::isdigit(str[0], loc) == 0) { thd_log_warn("thd_engine_set_user_set_point Invalid set point\n"); return THD_ERROR; } zone = get_zone(zone_type); if (!zone) { thd_log_warn("thd_engine_set_user_set_point Invalid zone\n"); return THD_ERROR; } return zone->update_max_temperature(atoi(user_set_point)); } void cthd_engine::thermal_zone_change(message_capsul_t *msg) { thermal_zone_notify_t *pmsg = (thermal_zone_notify_t*) msg->msg; for (unsigned i = 0; i < zones.size(); ++i) { cthd_zone *zone = zones[i]; if (zone->zone_active_status()) zone->zone_temperature_notification(pmsg->type, pmsg->data); else { thd_log_debug("zone is not active\n"); } } } void cthd_engine::poll_enable_disable(bool status, message_capsul_t *msg) { unsigned int *sensor_id = (unsigned int*) msg->msg; if (status) { poll_sensor_mask |= (1 << (*sensor_id)); poll_timeout_msec = def_poll_interval; thd_log_debug("thd_engine polling enabled via %u \n", *sensor_id); } else { poll_sensor_mask &= ~(1 << (*sensor_id)); if (!poll_sensor_mask) { poll_timeout_msec = -1; thd_log_debug("thd_engine polling last disabled via %u \n", *sensor_id); } } } int cthd_engine::proc_message(message_capsul_t *msg) { int ret = 0; thd_log_debug("Receieved message %d\n", msg->msg_id); switch (msg->msg_id) { case WAKEUP: break; case TERMINATE: thd_log_warn("Terminating ...\n"); ret = -1; terminate = true; break; case PREF_CHANGED: process_pref_change(); break; case THERMAL_ZONE_NOTIFY: if (!status) { thd_log_warn("Thermal Daemon is disabled \n"); break; } thermal_zone_change(msg); break; case CALIBRATE: { //TO DO } break; case RELOAD_ZONES: thd_engine_reload_zones(); break; case POLL_ENABLE: if (!poll_interval_sec) { poll_enable_disable(true, msg); } break; case POLL_DISABLE: if (!poll_interval_sec) { poll_enable_disable(false, msg); } break; default: break; } return ret; } cthd_cdev *cthd_engine::thd_get_cdev_at_index(int index) { for (int i = 0; i < cdev_cnt; ++i) { if (cdevs[i]->thd_cdev_get_index() == index) return cdevs[i]; } return NULL; } void cthd_engine::takeover_thermal_control() { csys_fs sysfs("/sys/class/thermal/"); DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/thermal/"; thd_log_info("Taking over thermal control \n"); if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "thermal_zone", strlen("thermal_zone"))) { int i; i = atoi(entry->d_name + strlen("thermal_zone")); std::stringstream policy; std::stringstream type; std::string type_val; std::string curr_policy; type << "thermal_zone" << i << "/type"; if (sysfs.exists(type.str().c_str())) { sysfs.read(type.str(), type_val); } policy << "thermal_zone" << i << "/policy"; if (sysfs.exists(policy.str().c_str())) { sysfs.read(policy.str(), curr_policy); zone_preferences.push_back(curr_policy); sysfs.write(policy.str(), "user_space"); } } } closedir(dir); } } void cthd_engine::giveup_thermal_control() { if (control_mode != EXCLUSIVE) return; if (zone_preferences.size() == 0) return; thd_log_info("Giving up thermal control \n"); csys_fs sysfs("/sys/class/thermal/"); DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/thermal/"; int cnt = 0; if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "thermal_zone", strlen("thermal_zone"))) { int i; i = atoi(entry->d_name + strlen("thermal_zone")); std::stringstream policy; std::stringstream type; std::string type_val; type << "thermal_zone" << i << "/type"; if (sysfs.exists(type.str().c_str())) { sysfs.read(type.str(), type_val); } policy << "thermal_zone" << i << "/policy"; if (sysfs.exists(policy.str().c_str())) { sysfs.write(policy.str(), zone_preferences[cnt++]); } } } closedir(dir); } } void cthd_engine::process_terminate() { thd_log_warn("terminating on user request ..\n"); giveup_thermal_control(); exit(0); } void cthd_engine::thd_engine_poll_enable(int sensor_id) { send_message(POLL_ENABLE, (int) sizeof(sensor_id), (unsigned char*) &sensor_id); } void cthd_engine::thd_engine_poll_disable(int sensor_id) { send_message(POLL_DISABLE, (int) sizeof(sensor_id), (unsigned char*) &sensor_id); } void cthd_engine::thd_engine_reload_zones() { thd_log_warn(" Reloading zones\n"); for (unsigned int i = 0; i < zones.size(); ++i) { cthd_zone *zone = zones[i]; delete zone; } zones.clear(); int ret = read_thermal_zones(); if (ret != THD_SUCCESS) { thd_log_error("No thermal sensors found"); // This is a fatal error and daemon will exit return; } } // Add any tested platform ids in this table static supported_ids_t id_table[] = { { 6, 0x2a }, // Sandybridge { 6, 0x2d }, // Sandybridge { 6, 0x3a }, // IvyBridge { 6, 0x3c }, { 6, 0x3e }, { 6, 0x3f }, { 6, 0x45 }, // Haswell ULT */ { 6, 0x46 }, // Haswell ULT */ { 0, 0 } // Last Invalid entry }; int cthd_engine::check_cpu_id() { #ifndef ANDROID // Copied from turbostat program unsigned int ebx, ecx, edx, max_level; unsigned int fms, family, model, stepping; genuine_intel = 0; int i = 0; bool valid = false; proc_list_matched = false; ebx = ecx = edx = 0; asm("cpuid": "=a"(max_level), "=b"(ebx), "=c"(ecx), "=d"(edx): "a"(0)); if (ebx == 0x756e6547 && edx == 0x49656e69 && ecx == 0x6c65746e) genuine_intel = 1; if (genuine_intel == 0) { thd_log_warn("Not running on a genuine Intel CPU!\n"); } asm("cpuid": "=a"(fms), "=c"(ecx), "=d"(edx): "a"(1): "ebx"); family = (fms >> 8) & 0xf; model = (fms >> 4) & 0xf; stepping = fms & 0xf; if (family == 6 || family == 0xf) model += ((fms >> 16) & 0xf) << 4; thd_log_warn( "%d CPUID levels; family:model:stepping 0x%x:%x:%x (%d:%d:%d)\n", max_level, family, model, stepping, family, model, stepping); while (id_table[i].family) { if (id_table[i].family == family && id_table[i].model == model) { proc_list_matched = true; valid = true; break; } i++; } if (!valid) { thd_log_warn(" No support RAPL and Intel P state driver\n"); } if (!(edx & (1 << 5))) { thd_log_warn("No MSR supported on processor \n"); } #endif return THD_SUCCESS; } void cthd_engine::thd_read_default_thermal_sensors() { DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/thermal/"; thd_log_debug("thd_read_default_thermal_sensors \n"); if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "thermal_zone", strlen("thermal_zone"))) { int i; i = atoi(entry->d_name + strlen("thermal_zone")); cthd_sensor *sensor = new cthd_sensor(i, base_path + entry->d_name + "/", ""); if (sensor->sensor_update() != THD_SUCCESS) continue; sensors.push_back(sensor); } } closedir(dir); } sensor_count = sensors.size(); thd_log_info("thd_read_default_thermal_sensors loaded %d sensors \n", sensor_count); } void cthd_engine::thd_read_default_thermal_zones() { DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/thermal/"; thd_log_debug("thd_read_default_thermal_zones \n"); if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "thermal_zone", strlen("thermal_zone"))) { int i; i = atoi(entry->d_name + strlen("thermal_zone")); cthd_sysfs_zone *zone = new cthd_sysfs_zone(i, "/sys/class/thermal/thermal_zone"); if (zone->zone_update() != THD_SUCCESS) continue; if (control_mode == EXCLUSIVE) zone->set_zone_active(); zones.push_back(zone); } } closedir(dir); } zone_count = zones.size(); thd_log_info("thd_read_default_thermal_zones loaded %d zones \n", zone_count); } void cthd_engine::thd_read_default_cooling_devices() { DIR *dir; struct dirent *entry; const std::string base_path = "/sys/class/thermal/"; thd_log_debug("thd_read_default_cooling devices \n"); if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "cooling_device", strlen("thermal_zone"))) { int i; i = atoi(entry->d_name + strlen("cooling_device")); cthd_sysfs_cdev *cdev = new cthd_sysfs_cdev(i, "/sys/class/thermal/"); if (cdev->update() != THD_SUCCESS) continue; cdevs.push_back(cdev); } } closedir(dir); } cdev_cnt = cdevs.size(); thd_log_info("thd_read_default_cooling devices loaded %d cdevs \n", cdev_cnt); } cthd_zone* cthd_engine::search_zone(std::string name) { cthd_zone *zone; for (unsigned int i = 0; i < zones.size(); ++i) { zone = zones[i]; if (!zone) continue; if (zone->get_zone_type() == name) return zone; } return NULL; } cthd_cdev* cthd_engine::search_cdev(std::string name) { cthd_cdev *cdev; for (unsigned int i = 0; i < cdevs.size(); ++i) { cdev = cdevs[i]; if (!cdev) continue; if (cdev->get_cdev_type() == name) return cdev; } return NULL; } cthd_sensor* cthd_engine::search_sensor(std::string name) { cthd_sensor *sensor; for (unsigned int i = 0; i < sensors.size(); ++i) { sensor = sensors[i]; if (!sensor) continue; if (sensor->get_sensor_type() == name) return sensor; } return NULL; } cthd_sensor* cthd_engine::get_sensor(int index) { if (index < (int) sensors.size()) return sensors[index]; else return NULL; } cthd_zone* cthd_engine::get_zone(int index) { if (index == -1) return NULL; if (index < (int) zones.size()) return zones[index]; else return NULL; } cthd_zone* cthd_engine::get_zone(std::string type) { cthd_zone *zone; for (unsigned int i = 0; i < zones.size(); ++i) { zone = zones[i]; if (zone->get_zone_type() == type) return zone; } return NULL; } thermal_daemon-1.1-rc2/src/thd_engine.h000066400000000000000000000114561225412722400200720ustar00rootroot00000000000000/* * thd_engine.h: thermal engine class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_ENGINE_H_ #define THD_ENGINE_H_ #include #include #include #include "thd_common.h" #include "thd_sys_fs.h" #include "thd_preference.h" #include "thd_sensor.h" #include "thd_zone.h" #include "thd_cdev.h" #include "thd_parse.h" #include "thd_kobj_uevent.h" #define MAX_MSG_SIZE 512 #define THD_NUM_OF_POLL_FDS 10 typedef enum { WAKEUP, TERMINATE, PREF_CHANGED, THERMAL_ZONE_NOTIFY, CALIBRATE, RELOAD_ZONES, POLL_ENABLE, POLL_DISABLE } message_name_t; // This defines whether the thermal control is entirey done by // this daemon or it just complements, what is done in kernel typedef enum { COMPLEMENTRY, EXCLUSIVE, } control_mode_t; typedef struct { message_name_t msg_id; int msg_size; unsigned long msg[MAX_MSG_SIZE]; } message_capsul_t; typedef struct { unsigned int family; unsigned int model; } supported_ids_t; class cthd_engine { protected: std::vector zones; std::vector sensors; std::vector cdevs; int cdev_cnt; int zone_count; int sensor_count; bool parse_thermal_zone_success; bool parse_thermal_cdev_success; private: int poll_timeout_msec; int wakeup_fd; control_mode_t control_mode; int write_pipe_fd; int preference; bool status; time_t thz_last_time; bool terminate; int genuine_intel; int has_invariant_tsc; int has_aperf; bool proc_list_matched; int poll_interval_sec; cthd_preference thd_pref; unsigned int poll_sensor_mask; pthread_t thd_engine; pthread_attr_t thd_attr; pthread_cond_t thd_cond_var; pthread_mutex_t thd_cond_mutex; std::vector zone_preferences; static const int thz_notify_debounce_interval = 3; struct pollfd poll_fds[THD_NUM_OF_POLL_FDS]; cthd_kobj_uevent kobj_uevent; int proc_message(message_capsul_t *msg); void process_pref_change(); void thermal_zone_change(message_capsul_t *msg); void process_terminate(); public: static const int max_thermal_zones = 10; static const int max_cool_devs = 50; static const int def_poll_interval = 4000; static const int soft_cdev_start_index = 100; cthd_parse parser; cthd_engine(); virtual ~cthd_engine(); void set_control_mode(control_mode_t mode) { control_mode = mode; } void thd_engine_thread(); int thd_engine_start(bool ignore_cpuid_check); int thd_engine_stop(); int check_cpu_id(); bool set_preference(const int pref); void thd_engine_terminate(); void thd_engine_calibrate(); int thd_engine_set_user_max_temp(const char *zone_type, const char *user_set_point); void poll_enable_disable(bool status, message_capsul_t *msg); cthd_cdev *thd_get_cdev_at_index(int index); void send_message(message_name_t msg_id, int size, unsigned char *msg); void takeover_thermal_control(); void giveup_thermal_control(); void thd_engine_poll_enable(int sensor_id); void thd_engine_poll_disable(int sensor_id); void thd_read_default_thermal_sensors(); void thd_read_default_thermal_zones(); void thd_read_default_cooling_devices(); virtual int read_thermal_sensors() { return 0; } ; virtual int read_thermal_zones() { return 0; } ; virtual int read_cooling_devices() { return 0; } ; int use_custom_zones() { return parse_thermal_zone_success; } int use_custom_cdevs() { return parse_thermal_cdev_success; } static const int max_cpu_count = 64; time_t last_cpu_update[max_cpu_count]; virtual bool apply_cpu_operation(int cpu) { return false; } int get_poll_timeout_ms() { return poll_timeout_msec; } int get_poll_timeout_sec() { return poll_timeout_msec / 1000; } void thd_engine_reload_zones(); bool processor_id_match() { return proc_list_matched; } void set_poll_interval(int val) { poll_interval_sec = val; } int get_preference() { return preference; } cthd_zone *search_zone(std::string name); cthd_cdev *search_cdev(std::string name); cthd_sensor *search_sensor(std::string name); cthd_sensor *get_sensor(int index); cthd_zone *get_zone(int index); cthd_zone *get_zone(std::string type); ; }; #endif /* THD_ENGINE_H_ */ thermal_daemon-1.1-rc2/src/thd_engine_default.cpp000066400000000000000000000312161225412722400221250ustar00rootroot00000000000000/* * cthd_engine_defualt.cpp: Default thermal engine * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include #include #include #include "thd_engine_default.h" #include "thd_zone_cpu.h" #include "thd_zone_generic.h" #include "thd_cdev_gen_sysfs.h" #include "thd_cdev_cpufreq.h" #include "thd_cdev_rapl.h" #include "thd_cdev_intel_pstate_driver.h" #include "thd_zone_surface.h" #include "thd_cdev_msr_rapl.h" // Default CPU cooling devices, which are not part of thermal sysfs // Since non trivial initialization is not supported, we init all fields even if they are not needed static cooling_dev_t cpu_def_cooling_devices[] = { { .status = true, .mask = CDEV_DEF_BIT_UNIT_VAL | CDEV_DEF_BIT_READ_BACK | CDEV_DEF_BIT_MIN_STATE | CDEV_DEF_BIT_STEP, .index = 0, .unit_val = ABSOULUTE_VALUE, .min_state = 0, .max_state = 0, .inc_dec_step = 5, .read_back = false, .auto_down_control = false, .type_string = "intel_powerclamp", .path_str = "", .debounce_interval = 4, .pid_enable = false, .pid = {0.0, 0.0, 0.0}}, }; cthd_engine_default::~cthd_engine_default() { if (parser_init_done) parser.parser_deinit(); } int cthd_engine_default::parser_init() { if (parser_init_done) return THD_SUCCESS; if (parser.parser_init() == THD_SUCCESS) { if (parser.start_parse() == THD_SUCCESS) { parser.dump_thermal_conf(); parser_init_done = true; return THD_SUCCESS; } } return THD_ERROR; } void cthd_engine_default::parser_deinit() { if (parser_init_done) { parser.parser_deinit(); parser_init_done = false; } } int cthd_engine_default::read_thermal_sensors() { int index; DIR *dir; struct dirent *entry; int sensor_mask = 0x0f; cthd_sensor *sensor; const std::string base_path = "/sys/devices/platform/"; thd_read_default_thermal_sensors(); index = sensor_count; sensor = search_sensor("pkg-temp-0"); if (sensor) { // Force this to support async sensor->set_async_capable(true); } // Default CPU temperature zone // Find path to read DTS temperature if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "coretemp.", strlen("coretemp."))) { int cnt = 0; unsigned int mask = 0x1; do { if (sensor_mask & mask) { std::stringstream temp_input_str; std::string path = base_path + entry->d_name + "/"; csys_fs dts_sysfs(path.c_str()); temp_input_str << "temp" << cnt << "_input"; if (dts_sysfs.exists(temp_input_str.str())) { cthd_sensor *sensor = new cthd_sensor(index, base_path + entry->d_name + "/" + temp_input_str.str(), temp_input_str.str(), SENSOR_TYPE_RAW); if (sensor->sensor_update() != THD_SUCCESS) return THD_ERROR; sensors.push_back(sensor); ++index; } } mask = (mask << 1); cnt++; } while (mask != 0); } } closedir(dir); } if (index == sensor_count) { // No coretemp sysfs exist, try hwmon thd_log_warn("Thermal DTS: No coretemp sysfs, trying hwmon \n"); cthd_sensor *sensor = new cthd_sensor(index, "/sys/class/hwmon/hwmon0/temp1_input", "hwmon", SENSOR_TYPE_RAW); if (sensor->sensor_update() != THD_SUCCESS) return THD_ERROR; sensors.push_back(sensor); ++index; if (index == sensor_count) { thd_log_error("Thermal DTS or hwmon: No Zones present: \n"); return THD_FATAL_ERROR; } } // Add from XML sensor config if (!parser_init() && parser.platform_matched()) { for (int i = 0; i < parser.sensor_count(); ++i) { thermal_sensor_t *sensor_config = parser.get_sensor_dev_index(i); if (!sensor_config) continue; cthd_sensor *sensor = search_sensor(sensor_config->name); if (sensor) { if (sensor_config->mask & SENSOR_DEF_BIT_PATH) sensor->update_path(sensor_config->path); if (sensor_config->mask & SENSOR_DEF_BIT_ASYNC_CAPABLE) sensor->set_async_capable(sensor_config->async_capable); } else { cthd_sensor *sensor_new = new cthd_sensor(index, sensor_config->path, sensor_config->name, SENSOR_TYPE_RAW); if (sensor_new->sensor_update() != THD_SUCCESS) { delete sensor_new; continue; } sensors.push_back(sensor_new); ++index; } } sensor_count = index; } for (unsigned int i = 0; i < sensors.size(); ++i) { sensors[i]->sensor_dump(); } return THD_SUCCESS; } int cthd_engine_default::read_thermal_zones() { int count = 0; DIR *dir; struct dirent *entry; const std::string base_path = "/sys/devices/platform/"; thd_read_default_thermal_zones(); count = zone_count; // Add from XML cooling device config if (!parser_init() && parser.platform_matched()) { for (int i = 0; i < parser.zone_count(); ++i) { thermal_zone_t *zone_config = parser.get_zone_dev_index(i); if (!zone_config) continue; cthd_zone *zone = search_zone(zone_config->type); if (zone) { thd_log_info("Zone already present \n"); for (unsigned int k = 0; k < zone_config->trip_pts.size(); ++k) { trip_point_t &trip_pt_config = zone_config->trip_pts[k]; cthd_sensor *sensor = search_sensor( trip_pt_config.sensor_type); if (!sensor) { thd_log_error("XML zone: invalid sensor type \n"); continue; } zone->bind_sensor(sensor); cthd_trip_point trip_pt(zone->get_trip_count(), trip_pt_config.trip_pt_type, trip_pt_config.temperature, trip_pt_config.hyst, zone->get_zone_index(), sensor->get_index(), trip_pt_config.control_type); // bind cdev for (unsigned int j = 0; j < trip_pt_config.cdev_trips.size(); ++j) { cthd_cdev *cdev = search_cdev( trip_pt_config.cdev_trips[j].type); if (cdev) { trip_pt.thd_trip_point_add_cdev(*cdev, trip_pt_config.cdev_trips[j].influence); } } zone->add_trip(trip_pt); } zone->set_zone_active(); } else { cthd_zone_generic *zone = new cthd_zone_generic(count, i, zone_config->type); if (zone->zone_update() == THD_SUCCESS) { zones.push_back(zone); ++count; } zone->set_zone_active(); } } } zone_count = count; if (!search_zone("cpu")) { bool cpu_zone_created = false; thd_log_info("zone cpu will be created \n"); // Default CPU temperature zone // Find path to read DTS temperature if ((dir = opendir(base_path.c_str())) != NULL) { while ((entry = readdir(dir)) != NULL) { if (!strncmp(entry->d_name, "coretemp.", strlen("coretemp."))) { cthd_zone_cpu *zone = new cthd_zone_cpu(count, base_path + entry->d_name + "/", atoi(entry->d_name + strlen("coretemp."))); if (zone->zone_update() == THD_SUCCESS) { zone->set_zone_active(); zones.push_back(zone); ++count; cpu_zone_created = true; } } } closedir(dir); } if (!cpu_zone_created) { // No coretemp sysfs exist, try hwmon thd_log_warn("Thermal DTS: No coretemp sysfs, trying hwmon \n"); cthd_zone_cpu *zone = new cthd_zone_cpu(count, "/sys/class/hwmon/hwmon0/", 0); if (zone->zone_update() == THD_SUCCESS) { zone->set_zone_active(); zones.push_back(zone); ++count; cpu_zone_created = true; } if (!cpu_zone_created) { thd_log_error("Thermal DTS or hwmon: No Zones present: \n"); return THD_FATAL_ERROR; } } } #ifdef ACTIVATE_SURFACE // Enable when skin sensors are standardized cthd_zone *surface; surface = search_zone("TSKN"); if (!surface) surface = search_zone("Surface"); if (!surface || (surface && !surface->zone_active_status())) { cthd_zone_surface *zone = new cthd_zone_surface(count); if (zone->zone_update() == THD_SUCCESS) { zones.push_back(zone); ++count; } zone->set_zone_active(); } else { thd_log_info("TSKN sensor was activated by config \n"); } #endif for (unsigned int i = 0; i < zones.size(); ++i) { zones[i]->zone_dump(); } return THD_SUCCESS; } int cthd_engine_default::add_replace_cdev(cooling_dev_t *config) { cthd_cdev *cdev; bool cdev_present = false; int current_cdev_index = cdev_cnt; bool percent_unit = false; // Check if there is existing cdev with this name and path cdev = search_cdev(config->type_string); if (cdev) { cdev_present = true; // Also check for path, some device like FAN has multiple paths for same type_str std::string base_path = cdev->get_base_path(); if (config->path_str.size() && config->path_str != base_path) { cdev_present = false; } } if (!cdev_present) { // create new cdev = new cthd_gen_sysfs_cdev(current_cdev_index, config->path_str); if (!cdev) return THD_ERROR; cdev->set_cdev_type(config->type_string); if (cdev->update() != THD_SUCCESS) { delete cdev; return THD_ERROR; } cdevs.push_back(cdev); } if (config->mask & CDEV_DEF_BIT_UNIT_VAL) { if (config->unit_val == RELATIVE_PERCENTAGES) percent_unit = true; } if (config->mask & CDEV_DEF_BIT_AUTO_DOWN) cdev->set_down_adjust_control(config->auto_down_control); if (config->mask & CDEV_DEF_BIT_STEP) { if (percent_unit) cdev->set_inc_dec_value( cdev->get_curr_state() * config->inc_dec_step / 100); else cdev->set_inc_dec_value(config->inc_dec_step); } if (config->mask & CDEV_DEF_BIT_MIN_STATE) { if (percent_unit) cdev->thd_cdev_set_min_state_param( cdev->get_curr_state() * config->min_state / 100); else cdev->thd_cdev_set_min_state_param(config->min_state); } if (config->mask & CDEV_DEF_BIT_MAX_STATE) { if (percent_unit) cdev->thd_cdev_set_max_state_param( cdev->get_curr_state() * config->max_state / 100); else cdev->thd_cdev_set_max_state_param(config->max_state); } if (config->mask & CDEV_DEF_BIT_READ_BACK) cdev->thd_cdev_set_read_back_param(config->read_back); if (config->mask & CDEV_DEF_BIT_DEBOUNCE_VAL) cdev->set_debounce_interval(config->debounce_interval); if (config->mask & CDEV_DEF_BIT_PID_PARAMS) { cdev->enable_pid(); cdev->set_pid_param(config->pid.Kp, config->pid.Ki, config->pid.Kd); } //cdev->cdev_dump(); current_cdev_index++; return THD_SUCCESS; } int cthd_engine_default::read_cooling_devices() { int size; int i; // Read first all the default cooling devices added by kernel thd_read_default_cooling_devices(); int current_cdev_index = cdev_cnt; // Add RAPL cooling device cthd_sysfs_cdev_rapl *rapl_dev = new cthd_sysfs_cdev_rapl(cdev_cnt, 0); rapl_dev->set_cdev_type("rapl_controller"); if (rapl_dev->update() == THD_SUCCESS) { cdevs.push_back(rapl_dev); ++cdev_cnt; } else { delete rapl_dev; if (processor_id_match()) { // RAPL control via MSR cthd_cdev_rapl_msr *rapl_msr_dev = new cthd_cdev_rapl_msr(cdev_cnt, 0); rapl_msr_dev->set_cdev_type("rapl_controller"); if (rapl_msr_dev->update() == THD_SUCCESS) { cdevs.push_back(rapl_msr_dev); ++cdev_cnt; } else delete rapl_msr_dev; } } // Add Intel P state driver as cdev cthd_intel_p_state_cdev *pstate_dev = new cthd_intel_p_state_cdev(cdev_cnt); pstate_dev->set_cdev_type("intel_pstate"); if (pstate_dev->update() == THD_SUCCESS) { cdevs.push_back(pstate_dev); ++cdev_cnt; } else delete pstate_dev; // Add statically defined cooling devices size = sizeof(cpu_def_cooling_devices) / sizeof(cooling_dev_t); for (i = 0; i < size; ++i) { if (add_replace_cdev(&cpu_def_cooling_devices[i]) == THD_SUCCESS) current_cdev_index++; cdev_cnt = current_cdev_index; } // Add from XML cooling device config if (!parser_init() && parser.platform_matched()) { for (int i = 0; i < parser.cdev_count(); ++i) { cooling_dev_t *cdev_config = parser.get_cool_dev_index(i); if (!cdev_config) continue; if (add_replace_cdev(cdev_config) == THD_SUCCESS) { current_cdev_index++; } } cdev_cnt = current_cdev_index; } cthd_cdev_cpufreq *cpu_freq_dev = new cthd_cdev_cpufreq(cdev_cnt, -1); cpu_freq_dev->set_cdev_type("cpufreq"); if (cpu_freq_dev->update() == THD_SUCCESS) { cdevs.push_back(cpu_freq_dev); ++cdev_cnt; } else delete cpu_freq_dev; // Dump all cooling devices for (unsigned i = 0; i < cdevs.size(); ++i) { cdevs[i]->cdev_dump(); } return THD_SUCCESS; } thermal_daemon-1.1-rc2/src/thd_engine_default.h000066400000000000000000000026211225412722400215700ustar00rootroot00000000000000/* * cthd_engine_defualt.cpp: Default thermal engine * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_ENGINE_DEFAULT_H_ #define THD_ENGINE_DEFAULT_H_ #include "thd_engine.h" #include "thd_zone_surface.h" class cthd_engine_default: public cthd_engine { private: int parser_init(); void parser_deinit(); int add_replace_cdev(cooling_dev_t *config); bool parser_init_done; public: static const int power_clamp_reduction_percent = 5; cthd_engine_default() : cthd_engine(), parser_init_done(false) { } ~cthd_engine_default(); int read_thermal_zones(); int read_cooling_devices(); int read_thermal_sensors(); }; #endif /* THD_ENGINE_DEFAULT_H_ */ thermal_daemon-1.1-rc2/src/thd_kobj_uevent.cpp000066400000000000000000000037251225412722400214730ustar00rootroot00000000000000/* * thd_kobj_uevent.cpp: Get notification from kobj uevent * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_kobj_uevent.h" #include "thd_common.h" int cthd_kobj_uevent::kobj_uevent_open() { memset(&nls, 0, sizeof(struct sockaddr_nl)); nls.nl_family = AF_NETLINK; nls.nl_pid = getpid(); nls.nl_groups = -1; fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); if (fd < 0) return fd; if (bind(fd, (struct sockaddr*) &nls, sizeof(struct sockaddr_nl))) { thd_log_warn("kob_uevent bin failed \n"); close(fd); return -1; } return fd; } void cthd_kobj_uevent::kobj_uevent_close() { close(fd); } bool cthd_kobj_uevent::check_for_event() { int i = 0; int len; const char *dev_path = "DEVPATH="; unsigned int dev_path_len = strlen(dev_path); char buffer[max_buffer_size]; len = recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT); while (i < len) { if (strlen(buffer + i) > dev_path_len && !strncmp(buffer + i, dev_path, dev_path_len)) { if (!strncmp(buffer + i + dev_path_len, device_path, strlen(device_path))) { return true; } } i += strlen(buffer + i) + 1; } return false; } void cthd_kobj_uevent::register_dev_path(char *path) { strncpy(device_path, path, max_buffer_size); } thermal_daemon-1.1-rc2/src/thd_kobj_uevent.h000066400000000000000000000025641225412722400211400ustar00rootroot00000000000000/* * thd_kobj_uevent.h: Get notification from kobj uevent * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_KOBJ_UEVENT_H_ #define THD_KOBJ_UEVENT_H_ #include #include #include #include #include #include #include #include #include class cthd_kobj_uevent { private: static const int max_buffer_size = 512; struct sockaddr_nl nls; int fd; char device_path[max_buffer_size]; public: int kobj_uevent_open(); void kobj_uevent_close(); void register_dev_path(char *path); bool check_for_event(); }; #endif thermal_daemon-1.1-rc2/src/thd_model.cpp000066400000000000000000000167471225412722400202700ustar00rootroot00000000000000/* * thd_model.h: thermal model class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_model.h" #include "thd_engine.h" #include "thd_zone.h" #include #include #include #include #include #include #include /* * Dynamically adjust set point based on the amount of time spent on hot zone. */ cthd_model::cthd_model(std::string _zone_type, bool use_pid) : zone_type(_zone_type), trend_increase_start(0), max_temp( def_max_temperature), set_point(def_max_temperature), hot_zone( 0), last_temp(0), trend_decrease_start(0), max_temp_reached(0), current_angle( 0), set_point_reached(false), delay_cnt(0), max_temp_seen( false), updated_set_point(false), use_pid_param(use_pid), set_point_delay_start( 0), user_forced_set_point_change(false) { if (use_pid) { // set default pid parameters kp = 0.5; ki = kd = 0.0001; last_time = 0; err_sum = 0.0; last_err = 0.0; } } unsigned int cthd_model::update_set_point(unsigned int curr_temp) { if (!use_pid_param) { double slope; double delta_x = (max_temp_reached - trend_increase_start) * 1000; double delta_y = max_temp - hot_zone; int _setpoint; double radians; double arc_len; if (delta_y > 0 && delta_x > 0) { slope = delta_y / delta_x; radians = atan(slope); thd_log_info("current slope %g angle before %g (%g degree)\n", slope, radians, 57.2957795 * radians); radians += (0.01746 * (current_angle + angle_increment)); thd_log_info("current slope %g angle before %g (%g degree)\n", slope, radians, 57.2957795 * radians); arc_len = delta_x * tan(radians); _setpoint = max_temp - (unsigned int) (arc_len - delta_y); _setpoint = _setpoint - _setpoint % 1000; thd_log_info("** set point x:%g y:%g arc_len:%g set_point %u\n", delta_x, delta_y, arc_len, _setpoint); if ((_setpoint < 0) || (abs(set_point - _setpoint) > max_compensation)) set_point -= max_compensation; else set_point = _setpoint; if (set_point < hot_zone) set_point = hot_zone; current_angle += angle_increment; } return set_point; } else { double output; double d_err = 0; int _setpoint; time_t now; time(&now); if (last_time == 0) last_time = now; time_t timeChange = (now - last_time); int error = curr_temp - max_temp; err_sum += (error * timeChange); if (timeChange) d_err = (error - last_err) / timeChange; else d_err = 0.0; /*Compute PID Output*/ output = kp * error + ki * err_sum + kd * d_err; _setpoint = max_temp - (unsigned int) output; thd_log_info("update_pid %ld %ld %d %g %u\n", now, last_time, error, output, _setpoint); if ((_setpoint < 0) || (abs(set_point - _setpoint) > max_compensation)) set_point -= max_compensation; else set_point = _setpoint; /*Remember some variables for next time*/ last_err = error; last_time = now; return set_point; } } void cthd_model::add_sample(int temperature) { time_t tm; time(&tm); updated_set_point = false; if (trend_increase_start == 0 && temperature > hot_zone) { trend_increase_start = tm; thd_log_debug("Trend increase start %ld\n", trend_increase_start); } else if (trend_increase_start && temperature < hot_zone) { int _set_point; thd_log_debug("Trend increase stopped %ld\n", trend_increase_start); trend_increase_start = 0; _set_point = read_set_point(); // Restore set point to a calculated max if (_set_point > set_point) { set_point = _set_point; updated_set_point = true; current_angle = 0; // Reset PID params err_sum = last_err = 0.0; last_time = 0; } } if (temperature > max_temp) { max_temp_reached = tm; // Very first time when we reached max temp // then we need to start tuning if (!max_temp_seen) { int _set_point; update_set_point(temperature); _set_point = read_set_point(); // Update only if the current set point is more than // the stored one. if (_set_point == 0 || set_point > _set_point) store_set_point(); max_temp_seen = true; } // Give some time to cooling device to cool, after that set next set point if (set_point_delay_start && (tm - set_point_delay_start) >= set_point_delay_tm && (last_temp < temperature)) update_set_point(temperature); delay_cnt++; if (!set_point_delay_start) set_point_delay_start = tm; set_point_reached = true; } else { set_point_reached = false; delay_cnt = 0; set_point_delay_start = 0; } if (user_forced_set_point_change) { user_forced_set_point_change = false; set_point_reached = true; } last_temp = temperature; thd_log_debug("update_set_point %u,%d,%u\n", last_temp, current_angle, set_point); } void cthd_model::store_set_point() { std::stringstream filename; std::ofstream file; filename << TDRUNDIR << "/" << "thermal_set_point." << zone_type << "." << "conf"; std::ofstream fout(filename.str().c_str()); if (fout.good()) { fout << set_point; } thd_log_info("storing set point %u\n", set_point); fout.close(); } int cthd_model::read_set_point() { std::stringstream filename; unsigned int _set_point = 0; filename << TDRUNDIR << "/" << "thermal_set_point." << zone_type << "." << "conf"; std::ifstream ifs(filename.str().c_str(), std::ifstream::in); if (ifs.good()) { ifs >> _set_point; } ifs.close(); thd_log_info("Read set point %u\n", _set_point); return _set_point; } void cthd_model::set_max_temperature(int temp) { int _set_point; int user_defined_max; max_temp = temp - safety_margin; user_defined_max = read_user_set_max_temp(); if (user_defined_max > 0) max_temp = user_defined_max; _set_point = read_set_point(); if (_set_point > 0 && _set_point < max_temp) { set_point = _set_point; } else { set_point = max_temp; } hot_zone = max_temp - ((max_temp * hot_zone_percent) / 100); } bool cthd_model::update_user_set_max_temp() { std::stringstream filename; bool present = false; unsigned int temp; filename << TDRUNDIR << "/" << "thd_user_set_max." << zone_type << "." << "conf"; std::ifstream ifs(filename.str().c_str(), std::ifstream::in); if (ifs.good()) { ifs >> temp; if (temp > 1000) { max_temp = temp; set_point = max_temp; hot_zone = max_temp - ((max_temp * hot_zone_percent) / 100); store_set_point(); present = true; user_forced_set_point_change = true; thd_log_info("User forced maximum temperature is %u\n", max_temp); } } ifs.close(); return present; } int cthd_model::read_user_set_max_temp() { std::stringstream filename; unsigned int user_max = 0; filename << TDRUNDIR << "/" << "thd_user_set_max." << zone_type << "." << "conf"; std::ifstream ifs(filename.str().c_str(), std::ifstream::in); if (ifs.good()) { ifs >> user_max; thd_log_info("User defined max temperature %u\n", user_max); } ifs.close(); return user_max; } thermal_daemon-1.1-rc2/src/thd_model.h000066400000000000000000000044131225412722400177200ustar00rootroot00000000000000/* * thd_model.h: thermal model class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef _THD_MODEL_H #define _THD_MODEL_H #include "thermald.h" #include #include #include class cthd_model { private: static const int def_max_temperature = 100 * 1000; static const unsigned int safety_margin = 1 * 1000; static const int angle_increment = 1; static const int def_setpoint_delay_cnt = 3; static const int max_compensation = 5000; static const unsigned int hot_zone_percent = 20; //20% static const time_t set_point_delay_tm = 4 * 3; // 12 seconds std::string zone_type; time_t trend_increase_start; int max_temp; int set_point; int hot_zone; int last_temp; time_t trend_decrease_start; time_t max_temp_reached; int current_angle; bool set_point_reached; int delay_cnt; bool max_temp_seen; bool updated_set_point; bool use_pid_param; time_t set_point_delay_start; bool user_forced_set_point_change; unsigned int update_set_point(unsigned int curr_temp); void store_set_point(); int read_set_point(); int read_user_set_max_temp(); double kp, ki, kd, err_sum, last_err; time_t last_time; public: cthd_model(std::string _zone_type, bool use_pid = false); void add_sample(int temperature); void set_max_temperature(int temp); bool update_user_set_max_temp(); unsigned int get_set_point() { return set_point; } unsigned int get_hot_zone_trigger_point() { return hot_zone; } bool is_set_point_reached() { return (set_point_reached || updated_set_point); } ; }; #endif thermal_daemon-1.1-rc2/src/thd_msr.cpp000066400000000000000000000354461225412722400177660ustar00rootroot00000000000000/* thd_msr.cpp: thermal engine msr class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ /* Abstracts all the MSR accesses for the thermal daemon. There shouldn't be any * MSR access other module. * */ #include "thd_msr.h" // MSR defines used in this module #define MSR_IA32_MISC_ENABLE 0x000001a0 #define MSR_IA32_MISC_ENABLE_TURBO_DISABLE (1ULL << 38) #define MSR_IA32_THERM_CONTROL 0x0000019a #define MSR_IA32_CLK_MOD_ENABLE (1ULL << 4) #define MSR_IA32_CLK_MOD_DUTY_CYCLE_MASK (0xF) #define MSR_IA32_PERF_CTL 0x0199 #define TURBO_DISENGAGE_BIT (1ULL << 32) #define PERF_CTL_CLK_SHIFT (8) #define PERF_CTL_CLK_MASK (0xff00) #define IA32_ENERGY_PERF_BIAS 0x01B0 #define MSR_IA32_PLATFORM_INFO 0xCE #define MSR_IA32_PLATFORM_INFO_MIN_FREQ(value) ((value >> 40) & 0xFF) #define MSR_IA32_PLATFORM_INFO_MAX_FREQ(value) ((value >> 8) & 0xFF) #define MSR_TURBO_RATIO_LIMIT 0x1AD #define TURBO_RATIO_1C_MASK 0xFF #define TURBO_RATIO_2C_MASK 0xFF00 #define TURBO_RATIO_2C_SHIFT (8) #define TURBO_RATIO_3C_MASK 0xFF0000 #define TURBO_RATIO_3C_SHIFT (16) #define TURBO_RATIO_4C_MASK 0xFF000000 #define TURBO_RATIO_4C_SHIFT (24) #define MSR_IA32_APERF 0xE8 #define MSR_IA32_MPERF 0xE7 cthd_msr::cthd_msr() : msr_sysfs("/dev/cpu/"), no_of_cpus(0) { } int cthd_msr::read_msr(int cpu, unsigned int idx, unsigned long long *val) { int ret = -1; std::stringstream file_name_str; file_name_str << cpu << "/msr"; if (msr_sysfs.exists(file_name_str.str())) { ret = msr_sysfs.read(file_name_str.str(), idx, (char*) val, sizeof(*val)); } if (ret < 0) { thd_log_warn("MSR READ Failed \n"); } return ret; } int cthd_msr::write_msr(int cpu, unsigned int idx, unsigned long long val) { int ret = -1; std::stringstream file_name_str; file_name_str << cpu << "/msr"; if (msr_sysfs.exists(file_name_str.str())) { ret = msr_sysfs.write(file_name_str.str(), idx, val); } if (ret < 0) { thd_log_warn("MSR WRITE Failed \n"); } return ret; } int cthd_msr::get_no_cpus() { int count = 0; if (no_of_cpus) return no_of_cpus; for (int i = 0; i < 64; ++i) { std::stringstream file_name_str; file_name_str << i; if (msr_sysfs.exists(file_name_str.str())) { count++; } } no_of_cpus = count; return count; } bool cthd_msr::check_turbo_status() { int cpu_count = get_no_cpus(); unsigned long long val; int ret; // Return false even if one of the core is not enabled for (int i = 0; i < cpu_count; ++i) { ret = read_msr(i, MSR_IA32_MISC_ENABLE, &val); if (ret < 0) return false; if (val & MSR_IA32_MISC_ENABLE_TURBO_DISABLE) return false; } return true; } int cthd_msr::enable_turbo_per_cpu(int cpu) { unsigned long long val; int ret; ret = read_msr(cpu, MSR_IA32_PERF_CTL, &val); if (ret < 0) return THD_ERROR; val &= ~TURBO_DISENGAGE_BIT; ret = write_msr(cpu, MSR_IA32_PERF_CTL, val); if (ret < 0) return THD_ERROR; return THD_SUCCESS; } int cthd_msr::enable_turbo() { int cpu_count = get_no_cpus(); unsigned long long val; int ret; for (int i = 0; i < cpu_count; ++i) { /* This method is recommended to be used only in BIOS ret = read_msr(i, MSR_IA32_MISC_ENABLE, &val); if (ret < 0) return THD_ERROR; val &= ~MSR_IA32_MISC_ENABLE_TURBO_DISABLE; ret = write_msr(i, MSR_IA32_MISC_ENABLE, val); if (ret < 0) return THD_ERROR; */ ret = read_msr(i, MSR_IA32_PERF_CTL, &val); if (ret < 0) return THD_ERROR; val &= ~TURBO_DISENGAGE_BIT; ret = write_msr(i, MSR_IA32_PERF_CTL, val); if (ret < 0) return THD_ERROR; } thd_log_info("Turbo enabled \n"); return THD_SUCCESS; } int cthd_msr::disable_turbo_per_cpu(int cpu) { unsigned long long val; int ret; ret = read_msr(cpu, MSR_IA32_PERF_CTL, &val); if (ret < 0) return THD_ERROR; val |= TURBO_DISENGAGE_BIT; ret = write_msr(cpu, MSR_IA32_PERF_CTL, val); if (ret < 0) return THD_ERROR; return THD_SUCCESS; } int cthd_msr::disable_turbo() { int cpu_count = get_no_cpus(); unsigned long long val; int ret; for (int i = 0; i < cpu_count; ++i) { /* This method is recommended only for BIOS ret = read_msr(i, MSR_IA32_MISC_ENABLE, &val); if (ret < 0) return THD_ERROR; val |= MSR_IA32_MISC_ENABLE_TURBO_DISABLE; ret = write_msr(i, MSR_IA32_MISC_ENABLE, val); if (ret < 0) return THD_ERROR; */ ret = read_msr(i, MSR_IA32_PERF_CTL, &val); if (ret < 0) return THD_ERROR; val |= TURBO_DISENGAGE_BIT; ret = write_msr(i, MSR_IA32_PERF_CTL, val); if (ret < 0) return THD_ERROR; } thd_log_info("Turbo disabled \n"); return THD_SUCCESS; } int cthd_msr::set_clock_mod_duty_cycle_per_cpu(int cpu, int state) { unsigned long long val; int ret; // First bit is reserved state = state << 1; ret = read_msr(cpu, MSR_IA32_THERM_CONTROL, &val); if (ret < 0) return THD_ERROR; if (!state) { val &= ~MSR_IA32_CLK_MOD_ENABLE; } else { val |= MSR_IA32_CLK_MOD_ENABLE; } val &= ~MSR_IA32_CLK_MOD_DUTY_CYCLE_MASK; val |= (state & MSR_IA32_CLK_MOD_DUTY_CYCLE_MASK); ret = write_msr(cpu, MSR_IA32_THERM_CONTROL, val); if (ret < 0) { thd_log_warn("set_clock_mod_duty_cycle current set failed to write\n"); return THD_ERROR; } return THD_SUCCESS; } int cthd_msr::set_clock_mod_duty_cycle(int state) { int cpu_count = get_no_cpus(); unsigned long long val; int ret; thd_log_info("Set T stated %d \n", state); // First bit is reserved state = state << 1; for (int i = 0; i < cpu_count; ++i) { ret = read_msr(i, MSR_IA32_THERM_CONTROL, &val); thd_log_debug("set_clock_mod_duty_cycle current %x\n", (unsigned int) val); if (ret < 0) return THD_ERROR; if (!state) { val &= ~MSR_IA32_CLK_MOD_ENABLE; } else { val |= MSR_IA32_CLK_MOD_ENABLE; } val &= ~MSR_IA32_CLK_MOD_DUTY_CYCLE_MASK; val |= (state & MSR_IA32_CLK_MOD_DUTY_CYCLE_MASK); thd_log_debug("set_clock_mod_duty_cycle current set to %x\n", (unsigned int) val); ret = write_msr(i, MSR_IA32_THERM_CONTROL, val); if (ret < 0) { thd_log_warn( "set_clock_mod_duty_cycle current set failed to write\n"); return THD_ERROR; } } return THD_SUCCESS; } int cthd_msr::get_clock_mod_duty_cycle() { int ret, state = 0; unsigned long long val; // Just get for cpu 0 and return ret = read_msr(0, MSR_IA32_THERM_CONTROL, &val); thd_log_debug("get_clock_mod_duty_cycle current %x\n", (unsigned int) val); if (ret < 0) return THD_ERROR; if (val & MSR_IA32_CLK_MOD_ENABLE) { state = val & MSR_IA32_CLK_MOD_DUTY_CYCLE_MASK; state = state >> 1; thd_log_debug("current state %x\n", state); } return state; } int cthd_msr::get_min_freq() { int ret; unsigned long long val; // Just get for cpu 0 and return ret = read_msr(0, MSR_IA32_PLATFORM_INFO, &val); if (ret < 0) return THD_ERROR; return MSR_IA32_PLATFORM_INFO_MIN_FREQ(val); } int cthd_msr::get_min_turbo_freq() { int ret; unsigned long long val; // Read turbo ratios ret = read_msr(0, MSR_TURBO_RATIO_LIMIT, &val); if (ret < 0) return THD_ERROR; // We are in a thermal zone. that means we already running all cores at full speed // So take the value for all 4 cores running val &= TURBO_RATIO_4C_MASK; if (val) return val >> TURBO_RATIO_4C_SHIFT; return 0; } int cthd_msr::get_max_turbo_freq() { int ret; unsigned long long val; // Read turbo ratios ret = read_msr(0, MSR_TURBO_RATIO_LIMIT, &val); if (ret < 0) return THD_ERROR; // We are in a thermal zone. that means we already running all cores at full speed // So take the value for all 4 cores running val &= 0xff; return val; } int cthd_msr::get_max_freq() { int ret; unsigned long long val; ret = read_msr(0, MSR_IA32_PLATFORM_INFO, &val); if (ret < 0) return THD_ERROR; return MSR_IA32_PLATFORM_INFO_MAX_FREQ(val); } int cthd_msr::dec_freq_state_per_cpu(int cpu) { unsigned long long val; int ret; int current_clock; ret = read_msr(cpu, MSR_IA32_PERF_CTL, &val); thd_log_debug("perf_ctl current %x\n", (unsigned int) val); if (ret < 0) return THD_ERROR; current_clock = (val >> 8) & 0xff; current_clock--; val = (current_clock << PERF_CTL_CLK_SHIFT); thd_log_debug("perf_ctl write %x\n", (unsigned int) val); ret = write_msr(cpu, MSR_IA32_PERF_CTL, val); if (ret < 0) { thd_log_warn("per control msr failded to write\n"); return THD_ERROR; } ret = read_msr(cpu, MSR_IA32_PERF_CTL, &val); thd_log_debug("perf_ctl read back %x\n", (unsigned int) val); if (ret < 0) return THD_ERROR; return THD_SUCCESS; } int cthd_msr::dec_freq_state() { int cpu_count = get_no_cpus(); unsigned long long val; int ret; int current_clock; thd_log_info("dec freq \n"); for (int i = 0; i < cpu_count; ++i) { ret = read_msr(i, MSR_IA32_PERF_CTL, &val); thd_log_debug("perf_ctl current %x\n", (unsigned int) val); if (ret < 0) return THD_ERROR; current_clock = (val >> 8) & 0xff; current_clock--; val = (current_clock << PERF_CTL_CLK_SHIFT); thd_log_debug("perf_ctl write %x\n", (unsigned int) val); ret = write_msr(i, MSR_IA32_PERF_CTL, val); if (ret < 0) { thd_log_warn("per control msr failed to write\n"); return THD_ERROR; } ret = read_msr(i, MSR_IA32_PERF_CTL, &val); thd_log_debug("perf_ctl read back %x\n", (unsigned int) val); if (ret < 0) return THD_ERROR; } return THD_SUCCESS; } int cthd_msr::inc_freq_state_per_cpu(int cpu) { unsigned long long val; int ret; int current_clock; ret = read_msr(cpu, MSR_IA32_PERF_CTL, &val); if (ret < 0) return THD_ERROR; current_clock = (val >> 8) & 0xff; current_clock++; val = (current_clock << PERF_CTL_CLK_SHIFT); ret = write_msr(cpu, MSR_IA32_PERF_CTL, val); if (ret < 0) { thd_log_warn("per control msr failded to write\n"); return THD_ERROR; } ret = read_msr(cpu, MSR_IA32_PERF_CTL, &val); thd_log_debug("perf_ctl read back %x\n", (unsigned int) val); if (ret < 0) return THD_ERROR; return THD_SUCCESS; } int cthd_msr::inc_freq_state() { int cpu_count = get_no_cpus(); unsigned long long val; int ret; int current_clock; thd_log_info("inc freq state \n"); for (int i = 0; i < cpu_count; ++i) { ret = read_msr(i, MSR_IA32_PERF_CTL, &val); if (ret < 0) return THD_ERROR; thd_log_debug("perf_ctl current %x\n", (unsigned int) val); if (ret < 0) return THD_ERROR; current_clock = (val >> 8) & 0xff; current_clock++; val = (current_clock << PERF_CTL_CLK_SHIFT); thd_log_debug("perf_ctl write %x\n", (unsigned int) val); ret = write_msr(i, MSR_IA32_PERF_CTL, val); if (ret < 0) { thd_log_warn("per control msr failded to write\n"); return THD_ERROR; } ret = read_msr(i, MSR_IA32_PERF_CTL, &val); thd_log_debug("perf_ctl read back %x\n", (unsigned int) val); if (ret < 0) return THD_ERROR; } return THD_SUCCESS; } int cthd_msr::set_freq_state_per_cpu(int cpu, int state) { unsigned long long val; int ret; ret = read_msr(cpu, MSR_IA32_PERF_CTL, &val); if (ret < 0) return THD_ERROR; val &= ~PERF_CTL_CLK_MASK; val |= (state << PERF_CTL_CLK_SHIFT); thd_log_debug("perf_ctl current %x\n", (unsigned int) val); ret = write_msr(cpu, MSR_IA32_PERF_CTL, val); if (ret < 0) { thd_log_warn("per control msr failded to write\n"); return THD_ERROR; } #ifdef READ_BACK_VERIFY ret = read_msr(cpu, MSR_IA32_PERF_CTL, &val); thd_log_debug("perf_ctl read back %x\n", val); if(ret < 0) return THD_ERROR; #endif return THD_SUCCESS; } int cthd_msr::set_freq_state(int state) { int cpu_count = get_no_cpus(); unsigned long long val; int ret; thd_log_info("set_freq_state \n"); for (int i = 0; i < cpu_count; ++i) { ret = read_msr(i, MSR_IA32_PERF_CTL, &val); if (ret < 0) return THD_ERROR; val &= ~PERF_CTL_CLK_MASK; val |= (state << PERF_CTL_CLK_SHIFT); thd_log_debug("perf_ctl write %x\n", (unsigned int) val); ret = write_msr(i, MSR_IA32_PERF_CTL, val); if (ret < 0) { thd_log_warn("per control msr failded to write\n"); return THD_ERROR; } #ifdef READ_BACK_VERIFY ret = read_msr(i, MSR_IA32_PERF_CTL, &val); thd_log_debug("perf_ctl read back %x\n", val); if(ret < 0) return THD_ERROR; #endif } return THD_SUCCESS; } int cthd_msr::set_perf_bias_performace() { int cpu_count = get_no_cpus(); unsigned long long val; int ret; thd_log_info("set_perf_bias_performace \n"); val = 0; for (int i = 0; i < cpu_count; ++i) { ret = read_msr(i, IA32_ENERGY_PERF_BIAS, &val); if (ret < 0) { thd_log_warn("per control msr failed to read\n"); return THD_ERROR; } val &= ~0x0f; ret = write_msr(i, IA32_ENERGY_PERF_BIAS, val); if (ret < 0) { thd_log_warn("per control msr failed to write\n"); return THD_ERROR; } } return THD_SUCCESS; } int cthd_msr::set_perf_bias_balaced() { int cpu_count = get_no_cpus(); unsigned long long val; int ret; thd_log_info("set_perf_bias_balaced \n"); for (int i = 0; i < cpu_count; ++i) { ret = read_msr(i, IA32_ENERGY_PERF_BIAS, &val); if (ret < 0) { thd_log_warn("per control msr failed to read\n"); return THD_ERROR; } val &= ~0x0f; val |= 6; ret = write_msr(i, IA32_ENERGY_PERF_BIAS, val); if (ret < 0) { thd_log_warn("per control msr failed to write\n"); return THD_ERROR; } } return THD_SUCCESS; } int cthd_msr::set_perf_bias_energy() { int cpu_count = get_no_cpus(); unsigned long long val; int ret; thd_log_info("set_perf_bias_energy \n"); for (int i = 0; i < cpu_count; ++i) { ret = read_msr(i, IA32_ENERGY_PERF_BIAS, &val); if (ret < 0) { thd_log_warn("per control msr failed to read\n"); return THD_ERROR; } val &= ~0x0f; val |= 15; ret = write_msr(i, IA32_ENERGY_PERF_BIAS, val); if (ret < 0) { thd_log_warn("per control msr failed to write\n"); return THD_ERROR; } } return THD_SUCCESS; } int cthd_msr::get_mperf_value(int cpu, unsigned long long *value) { int ret; if (cpu >= get_no_cpus()) return THD_ERROR; ret = read_msr(cpu, MSR_IA32_MPERF, value); if (ret < 0) { thd_log_warn("get_mperf_value failed to read\n"); return THD_ERROR; } return THD_SUCCESS; } int cthd_msr::get_aperf_value(int cpu, unsigned long long *value) { int ret; if (cpu >= get_no_cpus()) return THD_ERROR; ret = read_msr(cpu, MSR_IA32_APERF, value); if (ret < 0) { thd_log_warn("get_Aperf_value failed to read\n"); return THD_ERROR; } return THD_SUCCESS; } thermal_daemon-1.1-rc2/src/thd_msr.h000066400000000000000000000036631225412722400174270ustar00rootroot00000000000000/* thd_msr.h: thermal engine msr class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_MSR_H #define THD_MSR_H #include "thd_common.h" #include "thd_sys_fs.h" class cthd_msr { private: csys_fs msr_sysfs; int no_of_cpus; public: cthd_msr(); int read_msr(int cpu, unsigned int idx, unsigned long long *val); int write_msr(int cpu, unsigned int idx, unsigned long long val); int get_no_cpus(); bool check_turbo_status(); int enable_turbo(); int disable_turbo(); int get_clock_mod_duty_cycle(); int set_clock_mod_duty_cycle(int state); int get_min_freq(); int get_max_freq(); int get_min_turbo_freq(); int get_max_turbo_freq(); int inc_freq_state(); int dec_freq_state(); int set_freq_state(int state); int set_perf_bias_performace(); int set_perf_bias_balaced(); int set_perf_bias_energy(); int get_mperf_value(int cpu, unsigned long long *value); int get_aperf_value(int cpu, unsigned long long *value); int set_freq_state_per_cpu(int cpu, int state); int inc_freq_state_per_cpu(int cpu); int dec_freq_state_per_cpu(int cpu); int set_clock_mod_duty_cycle_per_cpu(int cpu, int state); int disable_turbo_per_cpu(int cpu); int enable_turbo_per_cpu(int cpu); }; #endif thermal_daemon-1.1-rc2/src/thd_parse.cpp000066400000000000000000000577151225412722400203020ustar00rootroot00000000000000/* * thd_engine.cpp: thermal engine class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ /* Parser to parse thermal configuration file. This uses libxml2 API. * */ #include "thd_parse.h" #include #include "thd_sys_fs.h" #define DEBUG_PARSER_PRINT(x,...) void cthd_parse::string_trim(std::string &str) { std::string::size_type pos = str.find_last_not_of(' '); if (pos != std::string::npos) { str.erase(pos + 1); pos = str.find_first_not_of(' '); if (pos != std::string::npos) str.erase(0, pos); } else str.erase(str.begin(), str.end()); } #ifdef ANDROID // Very simple version just checking for 0x20 not other white space chars bool isspace(int c) { if (c == ' ') return true; else return false; } #endif char *cthd_parse::char_trim(char *str) { int i; if (!str) return NULL; if (str[0] == '\0') return str; while (isspace(*str)) str++; for (i = strlen(str) - 1; (isspace(str[i])); i--) ; str[i + 1] = '\0'; return str; } cthd_parse::cthd_parse() : matched_thermal_info_index(-1), doc(NULL), root_element(NULL) { std::string name = TDCONFDIR; filename = name + "/" "thermal-conf.xml"; } int cthd_parse::parser_init() { doc = xmlReadFile(filename.c_str(), NULL, 0); if (doc == NULL) { thd_log_warn("error: could not parse file %s\n", filename.c_str()); return THD_ERROR; } root_element = xmlDocGetRootElement(doc); if (root_element == NULL) { thd_log_warn("error: could not get root element \n"); return THD_ERROR; } return THD_SUCCESS; } int cthd_parse::parse_new_trip_cdev(xmlNode * a_node, xmlDoc *doc, trip_cdev_t *trip_cdev) { xmlNode *cur_node = NULL; char *tmp_value; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char *) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!strcasecmp((const char*) cur_node->name, "type")) { trip_cdev->type.assign((const char*) tmp_value); string_trim(trip_cdev->type); } else if (!strcasecmp((const char*) cur_node->name, "influence")) { trip_cdev->influence = atoi(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "SamplingPeriod")) { trip_cdev->sampling_period = atoi(tmp_value); } } } return THD_SUCCESS; } int cthd_parse::parse_new_trip_point(xmlNode * a_node, xmlDoc *doc, trip_point_t *trip_pt) { xmlNode *cur_node = NULL; char *tmp_value; trip_cdev_t trip_cdev; trip_pt->temperature = 0; trip_pt->trip_pt_type = ACTIVE; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char *) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!strcasecmp((const char*) cur_node->name, "Temperature")) { trip_pt->temperature = atoi(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "Hyst")) { trip_pt->hyst = atoi(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "CoolingDevice")) { trip_cdev.influence = 0; trip_cdev.sampling_period = 0; trip_cdev.type.clear(); parse_new_trip_cdev(cur_node->children, doc, &trip_cdev); trip_pt->cdev_trips.push_back(trip_cdev); } else if (!strcasecmp((const char*) cur_node->name, "SensorType")) { trip_pt->sensor_type.assign(tmp_value); string_trim(trip_pt->sensor_type); } else if (!strcasecmp((const char*) cur_node->name, "type")) { char *type_val = char_trim(tmp_value); if (!strcasecmp(type_val, "active")) trip_pt->trip_pt_type = ACTIVE; else if (!strcasecmp(type_val, "passive")) trip_pt->trip_pt_type = PASSIVE; else if (!strcasecmp(type_val, "critical")) trip_pt->trip_pt_type = CRITICAL; else if (!strcasecmp(type_val, "max")) trip_pt->trip_pt_type = MAX; } else if (!strcasecmp((const char*) cur_node->name, "ControlType")) { char *ctrl_val = char_trim(tmp_value); if (!strcasecmp(ctrl_val, "SEQUENTIAL")) trip_pt->control_type = SEQUENTIAL; else trip_pt->control_type = PARALLEL; } if (tmp_value) xmlFree(tmp_value); } } return THD_SUCCESS; } int cthd_parse::parse_trip_points(xmlNode * a_node, xmlDoc *doc, thermal_zone_t *info_ptr) { xmlNode *cur_node = NULL; trip_point_t trip_pt; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); if (!strcasecmp((const char*) cur_node->name, "TripPoint")) { trip_pt.hyst = trip_pt.temperature = 0; trip_pt.trip_pt_type = PASSIVE; trip_pt.control_type = PARALLEL; trip_pt.influence = 100; trip_pt.sensor_type.clear(); if (parse_new_trip_point(cur_node->children, doc, &trip_pt) == THD_SUCCESS) info_ptr->trip_pts.push_back(trip_pt); } } } return THD_SUCCESS; } int cthd_parse::parse_pid_values(xmlNode * a_node, xmlDoc *doc, pid_control_t *pid_ptr) { xmlNode *cur_node = NULL; char *tmp_value; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!strcasecmp((const char*) cur_node->name, "Kp")) { pid_ptr->Kp = atof(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "Kd")) { pid_ptr->Kd = atof(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "Ki")) { pid_ptr->Ki = atof(tmp_value); } if (tmp_value) xmlFree(tmp_value); } } return THD_SUCCESS; } int cthd_parse::parse_new_zone(xmlNode * a_node, xmlDoc *doc, thermal_zone_t *info_ptr) { xmlNode *cur_node = NULL; char *tmp_value; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!strcasecmp((const char*) cur_node->name, "TripPoints")) { parse_trip_points(cur_node->children, doc, info_ptr); } else if (!strcasecmp((const char*) cur_node->name, "Type")) { info_ptr->type.assign((const char*) tmp_value); string_trim(info_ptr->type); } if (tmp_value) xmlFree(tmp_value); } } return THD_SUCCESS; } int cthd_parse::parse_new_cooling_dev(xmlNode * a_node, xmlDoc *doc, cooling_dev_t *cdev) { xmlNode *cur_node = NULL; char *tmp_value; cdev->max_state = cdev->min_state = 0; cdev->mask = 0; cdev->inc_dec_step = 1; cdev->read_back = true; cdev->auto_down_control = false; cdev->status = 0; cdev->pid_enable = false; cdev->unit_val = ABSOULUTE_VALUE; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!strcasecmp((const char *) cur_node->name, "Index")) { cdev->index = atoi(tmp_value); } else if (!strcasecmp((const char *) cur_node->name, "Type")) { cdev->type_string.assign((const char*) tmp_value); string_trim(cdev->type_string); } else if (!strcasecmp((const char *) cur_node->name, "Path")) { cdev->mask |= CDEV_DEF_BIT_PATH; cdev->path_str.assign((const char*) tmp_value); string_trim(cdev->path_str); } else if (!strcasecmp((const char *) cur_node->name, "MinState")) { cdev->mask |= CDEV_DEF_BIT_MIN_STATE; cdev->min_state = atoi(tmp_value); } else if (!strcasecmp((const char *) cur_node->name, "MaxState")) { cdev->mask |= CDEV_DEF_BIT_MAX_STATE; cdev->max_state = atoi(tmp_value); } else if (!strcasecmp((const char *) cur_node->name, "IncDecStep")) { cdev->mask |= CDEV_DEF_BIT_STEP; cdev->inc_dec_step = atoi(tmp_value); } else if (!strcasecmp((const char *) cur_node->name, "ReadBack")) { cdev->mask |= CDEV_DEF_BIT_READ_BACK; cdev->read_back = atoi(tmp_value); } else if (!strcasecmp((const char *) cur_node->name, "DebouncePeriod")) { cdev->mask |= CDEV_DEF_BIT_DEBOUNCE_VAL; cdev->debounce_interval = atoi(tmp_value); } else if (!strcasecmp((const char*) cur_node->name, "PidControl")) { cdev->mask |= CDEV_DEF_BIT_PID_PARAMS; cdev->pid_enable = true; parse_pid_values(cur_node->children, doc, &cdev->pid); } else if (!strcasecmp((const char *) cur_node->name, "AutoOffMode")) { cdev->mask |= CDEV_DEF_BIT_AUTO_DOWN; if (atoi(tmp_value)) cdev->auto_down_control = true; else cdev->auto_down_control = false; } if (tmp_value) xmlFree(tmp_value); } } return THD_SUCCESS; } int cthd_parse::parse_cooling_devs(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr) { xmlNode *cur_node = NULL; cooling_dev_t cdev; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); if (!strcasecmp((const char*) cur_node->name, "CoolingDevice")) { cdev.index = cdev.max_state = cdev.min_state = 0; cdev.inc_dec_step = 1; cdev.auto_down_control = false; cdev.path_str.clear(); cdev.type_string.clear(); parse_new_cooling_dev(cur_node->children, doc, &cdev); info_ptr->cooling_devs.push_back(cdev); } } } return THD_SUCCESS; } int cthd_parse::parse_thermal_zones(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr) { xmlNode *cur_node = NULL; thermal_zone_t zone; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); if (!strcasecmp((const char*) cur_node->name, "ThermalZone")) { zone.trip_pts.clear(); parse_new_zone(cur_node->children, doc, &zone); info_ptr->zones.push_back(zone); } } } return THD_SUCCESS; } int cthd_parse::parse_new_sensor(xmlNode * a_node, xmlDoc *doc, thermal_sensor_t *info_ptr) { xmlNode *cur_node = NULL; char *tmp_value; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!strcasecmp((const char*) cur_node->name, "Type")) { info_ptr->name.assign(tmp_value); string_trim(info_ptr->name); } else if (!strcasecmp((const char*) cur_node->name, "Path")) { info_ptr->mask |= SENSOR_DEF_BIT_PATH; info_ptr->path.assign(tmp_value); string_trim(info_ptr->path); } else if (!strcasecmp((const char*) cur_node->name, "AsyncCapable")) { info_ptr->async_capable = atoi(tmp_value); info_ptr->mask |= SENSOR_DEF_BIT_ASYNC_CAPABLE; } if (tmp_value) xmlFree(tmp_value); } } return THD_SUCCESS; } int cthd_parse::parse_thermal_sensors(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr) { xmlNode *cur_node = NULL; thermal_sensor_t sensor; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); if (!strcasecmp((const char*) cur_node->name, "ThermalSensor")) { sensor.name.clear(); sensor.path.clear(); sensor.async_capable = false; sensor.mask = 0; parse_new_sensor(cur_node->children, doc, &sensor); info_ptr->sensors.push_back(sensor); } } } return THD_SUCCESS; } int cthd_parse::parse_new_platform_info(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr) { xmlNode *cur_node = NULL; char *tmp_value; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!strcasecmp((const char*) cur_node->name, "uuid")) { info_ptr->uuid.assign((const char*) tmp_value); string_trim(info_ptr->uuid); } else if (!strcasecmp((const char*) cur_node->name, "ProductName")) { info_ptr->product_name.assign((const char*) tmp_value); string_trim(info_ptr->product_name); } else if (!strcasecmp((const char*) cur_node->name, "Name")) { info_ptr->name.assign((const char*) tmp_value); string_trim(info_ptr->name); } else if (!strcasecmp((const char*) cur_node->name, "Preference")) { char *pref_val = char_trim(tmp_value); if (!strcasecmp(pref_val, "PERFORMANCE")) info_ptr->default_prefernce = PREF_PERFORMANCE; else info_ptr->default_prefernce = PREF_ENERGY_CONSERVE; } else if (!strcasecmp((const char*) cur_node->name, "ThermalZones")) { parse_thermal_zones(cur_node->children, doc, info_ptr); } else if (!strcasecmp((const char*) cur_node->name, "ThermalSensors")) { parse_thermal_sensors(cur_node->children, doc, info_ptr); } else if (!strcasecmp((const char*) cur_node->name, "CoolingDevices")) { parse_cooling_devs(cur_node->children, doc, info_ptr); } if (tmp_value) xmlFree(tmp_value); } } return THD_SUCCESS; } int cthd_parse::parse_new_platform(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr) { xmlNode *cur_node = NULL; unsigned char *tmp_value; thermal_info_t info; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); tmp_value = (unsigned char*) xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (!strcasecmp((const char*) cur_node->name, "Platform")) { info.cooling_devs.clear(); info.zones.clear(); parse_new_platform_info(cur_node->children, doc, &info); thermal_info_list.push_back(info); } if (tmp_value) xmlFree(tmp_value); } } return THD_SUCCESS; } int cthd_parse::parse_new_thermal_conf(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr) { xmlNode *cur_node = NULL; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); if (!strcasecmp((const char*) cur_node->name, "ThermalConfiguration")) { parse_new_platform(cur_node->children, doc, info_ptr); } } } return THD_SUCCESS; } int cthd_parse::parse(xmlNode * a_node, xmlDoc *doc) { xmlNode *cur_node = NULL; thermal_info_t info; for (cur_node = a_node; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { DEBUG_PARSER_PRINT("node type: Element, name: %s value: %s\n", cur_node->name, xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1)); if (!strcasecmp((const char*) cur_node->name, "ThermalConfiguration")) { parse_new_platform(cur_node->children, doc, &info); } } } return THD_SUCCESS; } int cthd_parse::start_parse() { parse(root_element, doc); return THD_SUCCESS; } void cthd_parse::parser_deinit() { for (unsigned int i = 0; i < thermal_info_list.size(); ++i) { thermal_info_list[i].sensors.clear(); for (unsigned int j = 0; j < thermal_info_list[i].zones.size(); ++j) { thermal_info_list[i].zones[j].trip_pts.clear(); } thermal_info_list[i].zones.clear(); thermal_info_list[i].cooling_devs.clear(); } xmlFreeDoc(doc); } void cthd_parse::dump_thermal_conf() { thd_log_info(" Dumping parsed XML Data\n"); for (unsigned int i = 0; i < thermal_info_list.size(); ++i) { thd_log_info(" *** Index %d ***\n", i); thd_log_info("Name: %s\n", thermal_info_list[i].name.c_str()); thd_log_info("UUID: %s\n", thermal_info_list[i].uuid.c_str()); thd_log_info("type: %d\n", thermal_info_list[i].default_prefernce); for (unsigned int j = 0; j < thermal_info_list[i].sensors.size(); ++j) { thd_log_info("\tSensor %d \n", j); thd_log_info("\t Name: %s\n", thermal_info_list[i].sensors[j].name.c_str()); thd_log_info("\t Path: %s\n", thermal_info_list[i].sensors[j].path.c_str()); thd_log_info("\t Async Capable: %d\n", thermal_info_list[i].sensors[j].async_capable); } for (unsigned int j = 0; j < thermal_info_list[i].zones.size(); ++j) { thd_log_info("\tZone %d \n", j); thd_log_info("\t Name: %s\n", thermal_info_list[i].zones[j].type.c_str()); for (unsigned int k = 0; k < thermal_info_list[i].zones[j].trip_pts.size(); ++k) { thd_log_info("\t\t Trip Point %d \n", k); thd_log_info("\t\t temp id %d \n", thermal_info_list[i].zones[j].trip_pts[k].temperature); thd_log_info("\t\t trip type %d \n", thermal_info_list[i].zones[j].trip_pts[k].trip_pt_type); thd_log_info("\t\t hyst id %d \n", thermal_info_list[i].zones[j].trip_pts[k].hyst); for (unsigned int l = 0; l < thermal_info_list[i].zones[j].trip_pts[k].cdev_trips.size(); ++l) { thd_log_info("\t\t Trip id %d \n", l); thd_log_info("\t\t\t type %s \n", thermal_info_list[i].zones[j].trip_pts[k].cdev_trips[l].type.c_str()); thd_log_info("\t\t\t influence %d \n", thermal_info_list[i].zones[j].trip_pts[k].cdev_trips[l].influence); thd_log_info("\t\t\t SamplingPeriod %d \n", thermal_info_list[i].zones[j].trip_pts[k].cdev_trips[l].sampling_period); } } } for (unsigned int l = 0; l < thermal_info_list[i].cooling_devs.size(); ++l) { thd_log_info("\tCooling Dev %d \n", l); thd_log_info("\t\tType: %s\n", thermal_info_list[i].cooling_devs[l].type_string.c_str()); thd_log_info("\t\tPath: %s\n", thermal_info_list[i].cooling_devs[l].path_str.c_str()); thd_log_info("\t\tMin: %d\n", thermal_info_list[i].cooling_devs[l].min_state); thd_log_info("\t\tMax: %d\n", thermal_info_list[i].cooling_devs[l].max_state); thd_log_info("\t\tStep: %d\n", thermal_info_list[i].cooling_devs[l].inc_dec_step); thd_log_info("\t\tAutoDownControl: %d\n", thermal_info_list[i].cooling_devs[l].auto_down_control); if (thermal_info_list[i].cooling_devs[l].pid_enable) { thd_log_info("\t PID: Kp %f\n", thermal_info_list[i].cooling_devs[l].pid.Kp); thd_log_info("\t PID: Ki %f\n", thermal_info_list[i].cooling_devs[l].pid.Ki); thd_log_info("\t PID: Kd %f\n", thermal_info_list[i].cooling_devs[l].pid.Kd); } } } } bool cthd_parse::platform_matched() { csys_fs thd_sysfs("/sys/class/dmi/id/"); if (thd_sysfs.exists(std::string("product_uuid"))) { thd_log_debug("checking UUID\n"); std::string str; if (thd_sysfs.read("product_uuid", str) >= 0) { thd_log_info("UUID is [%s]\n", str.c_str()); for (unsigned int i = 0; i < thermal_info_list.size(); ++i) { if (thermal_info_list[i].uuid == "*") { matched_thermal_info_index = i; thd_log_info("UUID matched [wildcard]\n"); return true; } if (thermal_info_list[i].uuid == str) { matched_thermal_info_index = i; thd_log_info("Product UUID matched \n"); return true; } } } } if (thd_sysfs.exists(std::string("product_name"))) { thd_log_debug("checking product name\n"); char product_name[128] = { }; // Use different read method as the product name contains spaces if (thd_sysfs.read("product_name", product_name, 127) >= 0) { product_name[127] = '\0'; int len = strlen(product_name); if (!len) return false; for (int i = 0; i < len; ++i) if (product_name[i] == '\n') product_name[i] = '\0'; thd_log_info("product name is[%s]\n", product_name); for (unsigned int i = 0; i < thermal_info_list.size(); ++i) { if (!thermal_info_list[i].product_name.size()) continue; thd_log_debug("config product name %s\n", thermal_info_list[i].product_name.c_str()); if (thermal_info_list[i].product_name == "*") { matched_thermal_info_index = i; thd_log_info("Product Name matched [wildcard]\n"); return true; } if (thermal_info_list[i].product_name.compare(0, strlen(product_name), product_name) == 0) { matched_thermal_info_index = i; thd_log_info("Product Name matched \n"); return true; } } } } return false; } int cthd_parse::trip_count(unsigned int zone_index) { if (zone_index < thermal_info_list[matched_thermal_info_index].zones.size()) { return thermal_info_list[matched_thermal_info_index].zones[zone_index].trip_pts.size(); } else return -1; } trip_point_t* cthd_parse::get_trip_point(unsigned int zone_index, unsigned int trip_index) { if (zone_index < thermal_info_list[matched_thermal_info_index].zones.size()) { if (trip_index < thermal_info_list[matched_thermal_info_index].zones[zone_index].trip_pts.size()) return &thermal_info_list[matched_thermal_info_index].zones[zone_index].trip_pts[trip_index]; return NULL; } else return NULL; } cooling_dev_t* cthd_parse::get_cool_dev_index(unsigned int cdev_index) { if (cdev_index < thermal_info_list[matched_thermal_info_index].cooling_devs.size()) return &thermal_info_list[matched_thermal_info_index].cooling_devs[cdev_index]; else return NULL; } thermal_sensor_t* cthd_parse::get_sensor_dev_index(unsigned int sensor_index) { if (sensor_index < thermal_info_list[matched_thermal_info_index].sensors.size()) return &thermal_info_list[matched_thermal_info_index].sensors[sensor_index]; else return NULL; } thermal_zone_t *cthd_parse::get_zone_dev_index(unsigned int zone_index) { if (zone_index < thermal_info_list[matched_thermal_info_index].zones.size()) return &thermal_info_list[matched_thermal_info_index].zones[zone_index]; else return NULL; } bool cthd_parse::pid_status(int cdev_index) { return thermal_info_list[matched_thermal_info_index].cooling_devs[cdev_index].pid_enable; } bool cthd_parse::get_pid_values(int cdev_index, int *Kp, int *Ki, int *Kd) { if (thermal_info_list[matched_thermal_info_index].cooling_devs[cdev_index].pid_enable) { *Kp = thermal_info_list[matched_thermal_info_index].cooling_devs[cdev_index].pid.Kp; *Kd = thermal_info_list[matched_thermal_info_index].cooling_devs[cdev_index].pid.Kd; *Ki = thermal_info_list[matched_thermal_info_index].cooling_devs[cdev_index].pid.Ki; return true; } return false; } int cthd_parse::set_default_preference() { cthd_preference thd_pref; int ret; if (thermal_info_list[matched_thermal_info_index].default_prefernce == PREF_PERFORMANCE) ret = thd_pref.set_preference("PERFORMANCE"); else ret = thd_pref.set_preference("ENERGY_CONSERVE"); return ret; } thermal_daemon-1.1-rc2/src/thd_parse.h000066400000000000000000000121051225412722400177270ustar00rootroot00000000000000/* * thd_engine.cpp: thermal engine class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_PARSE_H #define THD_PARSE_H #include #include #include #include #include "thermald.h" #include "thd_trip_point.h" #define CDEV_DEF_BIT_MIN_STATE 0x0001 #define CDEV_DEF_BIT_MAX_STATE 0x0002 #define CDEV_DEF_BIT_STEP 0x0004 #define CDEV_DEF_BIT_READ_BACK 0x0008 #define CDEV_DEF_BIT_AUTO_DOWN 0x0010 #define CDEV_DEF_BIT_PATH 0x0020 #define CDEV_DEF_BIT_STATUS 0x0040 #define CDEV_DEF_BIT_UNIT_VAL 0x0080 #define CDEV_DEF_BIT_DEBOUNCE_VAL 0x0100 #define CDEV_DEF_BIT_PID_PARAMS 0x0200 #define SENSOR_DEF_BIT_PATH 0x0001 #define SENSOR_DEF_BIT_ASYNC_CAPABLE 0x0002 typedef struct { double Kp; double Ki; double Kd; } pid_control_t; typedef struct { unsigned int mask; std::string name; std::string path; bool async_capable; } thermal_sensor_t; typedef struct { std::string type; int influence; int sampling_period; } trip_cdev_t; typedef struct { int temperature; int hyst; trip_point_type_t trip_pt_type; trip_control_type_t control_type; int influence; std::string sensor_type; std::vector cdev_trips; } trip_point_t; typedef struct { std::string type; std::vector trip_pts; } thermal_zone_t; typedef enum { ABSOULUTE_VALUE, RELATIVE_PERCENTAGES } unit_value_t; typedef struct { bool status; unsigned int mask; // Fields which are present in config int index; unit_value_t unit_val; int min_state; int max_state; int inc_dec_step; bool read_back; // For some device read back current state is not possible bool auto_down_control; std::string type_string; std::string path_str; int debounce_interval; bool pid_enable; pid_control_t pid; } cooling_dev_t; typedef struct { std::string name; std::string uuid; std::string product_name; int default_prefernce; std::vector sensors; std::vector zones; std::vector cooling_devs; } thermal_info_t; class cthd_parse { private: std::string filename; std::vector thermal_info_list; int matched_thermal_info_index; xmlDoc *doc; xmlNode *root_element; int parse(xmlNode * a_node, xmlDoc *doc); int parse_pid_values(xmlNode * a_node, xmlDoc *doc, pid_control_t *pid_ptr); int parse_new_trip_cdev(xmlNode * a_node, xmlDoc *doc, trip_cdev_t *trip_cdev); int parse_new_thermal_conf(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info); int parse_new_platform_info(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info); int parse_new_zone(xmlNode * a_node, xmlDoc *doc, thermal_zone_t *info_ptr); int parse_new_cooling_dev(xmlNode * a_node, xmlDoc *doc, cooling_dev_t *info_ptr); int parse_new_trip_point(xmlNode * a_node, xmlDoc *doc, trip_point_t *trip_pt); int parse_thermal_zones(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr); int parse_new_sensor(xmlNode * a_node, xmlDoc *doc, thermal_sensor_t *info_ptr); int parse_thermal_sensors(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr); int parse_cooling_devs(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info_ptr); int parse_trip_points(xmlNode * a_node, xmlDoc *doc, thermal_zone_t *info_ptr); int parse_new_platform(xmlNode * a_node, xmlDoc *doc, thermal_info_t *info); void string_trim(std::string &str); char *char_trim(char *trim); public: cthd_parse(); int parser_init(); void parser_deinit(); int start_parse(); void dump_thermal_conf(); bool platform_matched(); int zone_count() { return thermal_info_list[matched_thermal_info_index].zones.size(); } int cdev_count() { return thermal_info_list[matched_thermal_info_index].cooling_devs.size(); } int sensor_count() { return thermal_info_list[matched_thermal_info_index].sensors.size(); } int set_default_preference(); int trip_count(unsigned int zone_index); bool pid_status(int cdev_index); bool get_pid_values(int cdev_index, int *Kp, int *Ki, int *Kd); trip_point_t *get_trip_point(unsigned int zone_index, unsigned int trip_index); cooling_dev_t *get_cool_dev_index(unsigned int cdev_index); thermal_sensor_t *get_sensor_dev_index(unsigned int sensor_index); thermal_zone_t *get_zone_dev_index(unsigned int zone_index); // std::string get_sensor_path(int zone_index) { // return thermal_info_list[matched_thermal_info_index].zones[zone_index].path; // } }; #endif thermal_daemon-1.1-rc2/src/thd_pid.cpp000066400000000000000000000033761225412722400177360ustar00rootroot00000000000000/* * thd_pid.cpp: pid implementation * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_pid.h" cthd_pid::cthd_pid() { kp = 0.0005; ki = kd = 0.0001; last_time = 0; err_sum = 0.0; last_err = 0.0; target_temp = 0; } int cthd_pid::pid_output(unsigned int curr_temp) { double output; double d_err = 0; time_t now; time(&now); if (last_time == 0) last_time = now; time_t timeChange = (now - last_time); int error = curr_temp - target_temp; thd_log_debug("pid_output error %d %g:%g\n", error, kp, kp * error); err_sum += (error * timeChange); if (timeChange) d_err = (error - last_err) / timeChange; else d_err = 0.0; /*Compute PID Output*/ output = kp * error + ki * err_sum + kd * d_err; thd_log_debug("pid %d:%d:%d:%d\n", (int) output, (int) (kp * error), (int) (ki * err_sum), (int) (kd * d_err)); /*Remember some variables for next time*/ last_err = error; last_time = now; thd_log_debug("pid_output %d:%d %g:%d\n", curr_temp, target_temp, output, (int) output); return (int) output; } thermal_daemon-1.1-rc2/src/thd_pid.h000066400000000000000000000022371225412722400173760ustar00rootroot00000000000000/* * thd_pid.h: pid interface * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thermald.h" #include class cthd_pid { private: double err_sum, last_err; time_t last_time; unsigned int target_temp; public: cthd_pid(); double kp, ki, kd; int pid_output(unsigned int curr_temp); void set_target_temp(unsigned int temp) { target_temp = temp; } void reset() { err_sum = last_err = last_time = 0; } }; thermal_daemon-1.1-rc2/src/thd_preference.cpp000066400000000000000000000073661225412722400213030ustar00rootroot00000000000000/* * thd_preference.cpp: Thermal preference class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_preference.h" cthd_preference::cthd_preference() : old_preference(0) { std::stringstream filename; filename << TDRUNDIR << "/" << "thd_preference.conf"; std::ifstream ifs(filename.str().c_str(), std::ifstream::in); if (!ifs.good()) { preference = PREF_ENERGY_CONSERVE; } else { ifs >> preference; } ifs.close(); } std::string cthd_preference::int_pref_to_string(int pref) { std::string perf_str; switch (preference) { case PREF_PERFORMANCE: perf_str = "PERFORMANCE"; break; case PREF_ENERGY_CONSERVE: perf_str = "ENERGY_CONSERVE"; break; case PREF_DISABLED: perf_str = "DISABLE"; break; default: perf_str = "INVALID"; break; } return perf_str; } int cthd_preference::string_pref_to_int(std::string &pref_str) { int pref; if (pref_str == "PERFORMANCE") pref = PREF_PERFORMANCE; else if (pref_str == "ENERGY_CONSERVE") pref = PREF_ENERGY_CONSERVE; else if (pref_str == "DISABLE") pref = PREF_DISABLED; else pref = PREF_PERFORMANCE; return pref; } std::string cthd_preference::get_preference_str() { return int_pref_to_string(preference); } const char *cthd_preference::get_preference_cstr() { return int_pref_to_string(preference).c_str(); } int cthd_preference::get_preference() { return preference; } void cthd_preference::refresh() { std::stringstream filename; filename << TDRUNDIR << "/" << "thd_preference.conf"; std::ifstream ifs(filename.str().c_str(), std::ifstream::in); if (!ifs.good()) { preference = PREF_ENERGY_CONSERVE; } else { ifs >> preference; } ifs.close(); } bool cthd_preference::set_preference(const char *pref_str) { std::string str(pref_str); int pref = string_pref_to_int(str); std::stringstream filename; filename << TDRUNDIR << "/" << "thd_preference.conf"; std::ofstream fout(filename.str().c_str()); if (!fout.good()) { return false; } fout << pref; fout.close(); // Save the old preference old_preference = preference; std::stringstream filename_save; filename_save << TDRUNDIR << "/" << "thd_preference.conf.save"; std::ofstream fout_save(filename_save.str().c_str()); if (!fout_save.good()) { return false; } fout_save << old_preference; fout_save.close(); std::ifstream ifs(filename.str().c_str(), std::ifstream::in); if (!ifs.good()) { preference = PREF_PERFORMANCE; } else { //ifs.read(reinterpret_cast < char * > (&preference), sizeof(preference)); ifs >> preference; } ifs.close(); thd_log_debug("old_preference %d new preference %d\n", old_preference, preference); return true; } int cthd_preference::get_old_preference() { std::stringstream filename; filename << TDRUNDIR << "/" << "thd_preference.conf.save"; std::ifstream ifs(filename.str().c_str(), std::ifstream::in); if (!ifs.good()) { old_preference = PREF_PERFORMANCE; } else { //ifs.read(reinterpret_cast < char * > (&preference), sizeof(preference)); ifs >> old_preference; } ifs.close(); return old_preference; } thermal_daemon-1.1-rc2/src/thd_preference.h000066400000000000000000000027121225412722400207360ustar00rootroot00000000000000/* * thd_preference.h: Thermal preference class interface file * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_PREFERENCE_H #define THD_PREFERENCE_H #include "thd_common.h" #include #include #include #include #include enum { PREF_ENERGY_CONSERVE, PREF_PERFORMANCE, PREF_DISABLED }; class cthd_preference { private: int preference; int old_preference; int string_pref_to_int(std::string &pref_str); std::string int_pref_to_string(int pref); public: cthd_preference(); bool set_preference(const char *pref); std::string get_preference_str(); const char *get_preference_cstr(); int get_preference(); int get_old_preference(); void refresh(); }; #endif thermal_daemon-1.1-rc2/src/thd_rapl_interface.cpp000066400000000000000000000401361225412722400221330ustar00rootroot00000000000000/* rapl_interface.cpp: rapl interface for power top implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include #include #include #include #include #include #include #include "thd_common.h" #include "thd_rapl_interface.h" #if 1 #define RAPL_DBG_PRINT thd_log_debug #define RAPL_ERROR_PRINT thd_log_warn #else #define RAPL_DBG_PRINT(...) ((void) 0) #define RAPL_ERROR_PRINT(...) ((void) 0) #endif #define RAPL_INFO_PRINT printf #define MAX_TEMP_STR_SIZE 20 // RAPL interface #define MSR_RAPL_POWER_UNIT 0x606 #define MSR_PKG_POWER_LIMIT 0x610 #define MSR_PKG_ENERY_STATUS 0x611 #define MSR_PKG_POWER_INFO 0x614 #define MSR_PKG_PERF_STATUS 0x613 #define MSR_DRAM_POWER_LIMIT 0x618 #define MSR_DRAM_ENERY_STATUS 0x619 #define MSR_DRAM_PERF_STATUS 0x61B #define MSR_DRAM_POWER_INFO 0x61c #define MSR_PP0_POWER_LIMIT 0x638 #define MSR_PP0_ENERY_STATUS 0x639 #define MSR_PP0_POLICY 0x63A #define MSR_PP0_PERF_STATUS 0x63B #define MSR_PP1_POWER_LIMIT 0x640 #define MSR_PP1_ENERY_STATUS 0x641 #define MSR_PP1_POLICY 0x642 #define PKG_DOMAIN_PRESENT 0x01 #define DRAM_DOMAIN_PRESENT 0x02 #define PP0_DOMAIN_PRESENT 0x04 #define PP1_DOMAIN_PRESENT 0x08 c_rapl_interface::c_rapl_interface(int cpu) : measurment_interval(def_sampling_interval), first_cpu(cpu), last_pkg_energy_status( 0.0), last_dram_energy_status(0.0), last_pp0_energy_status(0.0), last_pp1_energy_status( 0.0), default_pkg_power_limit_msr_value(0) { unsigned long long value; int ret; RAPL_INFO_PRINT("RAPL device for cpu %d\n", cpu); rapl_domains = 0; // presence of each domain // Check presence of PKG domain ret = read_msr(first_cpu, MSR_PKG_ENERY_STATUS, &value); if (ret > 0) { rapl_domains |= PKG_DOMAIN_PRESENT; RAPL_DBG_PRINT("Domain : PKG present\n"); } else { RAPL_DBG_PRINT("Domain : PKG Not present\n"); } #ifdef RAPL_SERVER_CONFIG // Check presence of DRAM domain ret = read_msr(first_cpu, MSR_DRAM_ENERY_STATUS, &value); if (ret > 0) { rapl_domains |= DRAM_DOMAIN_PRESENT; RAPL_DBG_PRINT("Domain : DRAM present\n"); } else { RAPL_DBG_PRINT("Domain : DRAM Not present\n"); } #endif // Check presence of PP0 domain ret = read_msr(first_cpu, MSR_PP0_ENERY_STATUS, &value); if (ret > 0) { rapl_domains |= PP0_DOMAIN_PRESENT; RAPL_DBG_PRINT("Domain : PP0 present\n"); } else { RAPL_DBG_PRINT("Domain : PP0 Not present\n"); } // Check presence of PP1 domain ret = read_msr(first_cpu, MSR_PP1_ENERY_STATUS, &value); if (ret > 0) { rapl_domains |= PP1_DOMAIN_PRESENT; RAPL_DBG_PRINT("Domain : PP1 present\n"); } else { RAPL_DBG_PRINT("Domain : PP1 Not present\n"); } power_units = get_power_unit(); energy_status_units = get_energy_status_unit(); time_units = get_time_unit(); RAPL_DBG_PRINT("power_units %g\n", power_units); RAPL_DBG_PRINT("energy_units %g\n", energy_status_units); RAPL_DBG_PRINT("time_units %g\n", time_units); RAPL_DBG_PRINT("RAPL Domain mask: %x\n", rapl_domains); } bool c_rapl_interface::pkg_domain_present() { if ((rapl_domains & PKG_DOMAIN_PRESENT)) { return true; } return false; } bool c_rapl_interface::dram_domain_present() { if ((rapl_domains & DRAM_DOMAIN_PRESENT)) { return true; } return false; } bool c_rapl_interface::pp0_domain_present() { if ((rapl_domains & PP0_DOMAIN_PRESENT)) { return true; } return false; } bool c_rapl_interface::pp1_domain_present() { if ((rapl_domains & PP1_DOMAIN_PRESENT)) { return true; } return false; } int c_rapl_interface::read_msr(int cpu, unsigned int idx, unsigned long long *val) { return msr.read_msr(cpu, idx, val); } int c_rapl_interface::write_msr(int cpu, unsigned int idx, unsigned long long val) { RAPL_DBG_PRINT("write_msr %X\n", (unsigned int) val); return msr.write_msr(cpu, idx, val); } int c_rapl_interface::get_rapl_power_unit(unsigned long long *value) { int ret; ret = read_msr(first_cpu, MSR_RAPL_POWER_UNIT, value); return ret; } double c_rapl_interface::get_power_unit() { int ret; unsigned long long value; double units; ret = get_rapl_power_unit(&value); if (ret < 0) { return ret; } units = (double) 1 / pow(2.0, (int) value & 0xf); if (units == 0.0) return THD_ERROR; return units; } double c_rapl_interface::get_energy_status_unit() { int ret; unsigned long long value; double units; ret = get_rapl_power_unit(&value); if (ret < 0) { return ret; } units = (double) 1 / pow(2.0, (int) (value & 0x1f00) >> 8); if (units == 0.0) return THD_ERROR; return units; } double c_rapl_interface::get_time_unit() { int ret; unsigned long long value; double units; ret = get_rapl_power_unit(&value); if (ret < 0) { return ret; } units = (double) 1 / pow(2.0, (int) (value & 0xf0000) >> 16); if (units == 0.0) return THD_ERROR; return units; } int c_rapl_interface::get_pkg_energy_status(double *status) { int ret; unsigned long long value; if (!pkg_domain_present()) { return -1; } ret = read_msr(first_cpu, MSR_PKG_ENERY_STATUS, &value); if (ret < 0) { RAPL_ERROR_PRINT("get_pkg_energy_status failed\n"); return ret; } *status = (double) (value & 0xffffffff) * get_energy_status_unit(); return ret; } int c_rapl_interface::get_pkg_power_info(double *thermal_spec_power, double *max_power, double *min_power, double *max_time_window) { int ret; unsigned long long value; if (!pkg_domain_present()) { return -1; } ret = read_msr(first_cpu, MSR_PKG_POWER_INFO, &value); if (ret < 0) { RAPL_ERROR_PRINT("get_pkg_power_info failed\n"); return ret; } *thermal_spec_power = (value & 0x7FFF) * power_units; *min_power = ((value & 0x7FFF0000) >> 16) * power_units; *max_power = ((value & 0x7FFF00000000) >> 32) * power_units; *max_time_window = ((value & 0x3f000000000000) >> 48) * time_units; return ret; } int c_rapl_interface::get_pkg_power_limit_msr(unsigned long long *value) { int ret; if (!pkg_domain_present()) { return -1; } ret = read_msr(first_cpu, MSR_PKG_POWER_LIMIT, value); if (ret < 0) { RAPL_ERROR_PRINT("get_pkg_power_limit failed\n"); return ret; } return ret; } int c_rapl_interface::set_pkg_power_limit_msr(unsigned long long value) { int ret; RAPL_DBG_PRINT("set_pkg_power_limit_msr %X\n", (unsigned int) value); if (!pkg_domain_present()) { return -1; } ret = write_msr(first_cpu, MSR_PKG_POWER_LIMIT, value); if (ret < 0) { RAPL_ERROR_PRINT("set_pkg_power_limit failed\n"); return ret; } return ret; } // power is in milliwatts int c_rapl_interface::set_pkg_power_limit(int time_window, int power) { /* Package Power Limit #1(bits 14:0): Sets the average power usage limit of the package domain corresponding to time window # 1. The unit of this field is specified by the “Power Units” field of MSR_RAPL_POWER_UNIT. • Enable Power Limit #1(bit 15): 0 = disabled; 1 = enabled. • • Time Window for Power Limit #1 (bits 23:17): Indicates the time window for power limit #1 Package Clamping Limitation #1 (bit 16): Allow going below OS-requested P/T state setting during time window specified by bits 23:17. Time limit = 2^Y * (1.0 + Z/4.0) * Time_Unit Here “Y” is the unsigned integer value represented. by bits 21:17, “Z” is an unsigned integer represented by bits 23:22. “Time_Unit” is specified by the “Time Units” field of MSR_RAPL_POWER_UNIT. */ unsigned long long value = default_pkg_power_limit_msr_value; unsigned int power_limit_mask = 0x7fff; unsigned int power_limit_en_bit = 0x8000; unsigned int power_clamp_en_bit = 0x10000; double time_limit; // = 2^Y * (1.0 + Z/4.0) * Time_Unit unsigned int y, z; int time_limit_int; RAPL_DBG_PRINT("set_pkg_power_limit tm %d %d \n", time_window, power); power = (int) ((double) power / 1000 / power_units); RAPL_DBG_PRINT("power / power units %d \n", power); value = (value & ~power_limit_mask) | power; value |= (power_limit_en_bit | power_clamp_en_bit); RAPL_DBG_PRINT("Try Limit with power %X\n", (unsigned int) value); time_limit = time_window / time_units; #ifdef ANDROID y = (int)log(time_limit) * 1.442695; #else y = (int) log2(time_limit); #endif z = 4 * (time_limit - (1 << y)) / (1 << y); time_limit_int = ((y & 0x1f) | ((z & 0x3) << 5)); RAPL_DBG_PRINT("time_limit %g %d\n", time_limit, time_limit_int); value &= ~0xFE0000; value |= time_limit_int << 17; RAPL_DBG_PRINT("Try new Limit %X\n", (unsigned int) value); return set_pkg_power_limit_msr(value); } int c_rapl_interface::store_pkg_power_limit() { unsigned long long value; int ret; ret = get_pkg_power_limit_msr(&value); if (ret < 0) return ret; default_pkg_power_limit_msr_value = value; RAPL_DBG_PRINT("store: default_pkg_power_limit_msr_value %X\n", (unsigned int) default_pkg_power_limit_msr_value); return 0; } int c_rapl_interface::restore_pkg_power_limit() { if (default_pkg_power_limit_msr_value) set_pkg_power_limit_msr(default_pkg_power_limit_msr_value); RAPL_DBG_PRINT("restore: default_pkg_power_limit_msr_value %X\n", (unsigned int) default_pkg_power_limit_msr_value); return 0; } int c_rapl_interface::get_dram_energy_status(double *status) { int ret; unsigned long long value; if (!dram_domain_present()) { return -1; } ret = read_msr(first_cpu, MSR_DRAM_ENERY_STATUS, &value); if (ret < 0) { RAPL_ERROR_PRINT("get_dram_energy_status failed\n"); return ret; } *status = (double) (value & 0xffffffff) * get_energy_status_unit(); return ret; } int c_rapl_interface::get_dram_power_info(double *thermal_spec_power, double *max_power, double *min_power, double *max_time_window) { int ret; unsigned long long value; if (!dram_domain_present()) { return -1; } ret = read_msr(first_cpu, MSR_DRAM_POWER_INFO, &value); if (ret < 0) { RAPL_ERROR_PRINT("get_dram_power_info failed\n"); return ret; } *thermal_spec_power = (value & 0x7FFF) * power_units; *min_power = ((value & 0x7FFF0000) >> 16) * power_units; *max_power = ((value & 0x7FFF00000000) >> 32) * power_units; *max_time_window = ((value & 0x3f000000000000) >> 48) * time_units; return ret; } int c_rapl_interface::get_dram_power_limit(unsigned long long *value) { int ret; if (!dram_domain_present()) { return -1; } ret = read_msr(first_cpu, MSR_DRAM_POWER_LIMIT, value); if (ret < 0) { RAPL_ERROR_PRINT("get_dram_power_limit failed\n"); return ret; } return ret; } int c_rapl_interface::set_dram_power_limit(unsigned long long value) { int ret; if (!dram_domain_present()) { return -1; } ret = write_msr(first_cpu, MSR_DRAM_POWER_LIMIT, value); if (ret < 0) { RAPL_ERROR_PRINT("set_dram_power_limit failed\n"); return ret; } return ret; } int c_rapl_interface::get_pp0_energy_status(double *status) { int ret; unsigned long long value; if (!pp0_domain_present()) { return -1; } ret = read_msr(first_cpu, MSR_PP0_ENERY_STATUS, &value); if (ret < 0) { RAPL_ERROR_PRINT("get_pp0_energy_status failed\n"); return ret; } *status = (double) (value & 0xffffffff) * get_energy_status_unit(); return ret; } int c_rapl_interface::get_pp0_power_limit(unsigned long long *value) { int ret; if (!pp0_domain_present()) { return -1; } ret = read_msr(first_cpu, MSR_PP0_POWER_LIMIT, value); if (ret < 0) { RAPL_ERROR_PRINT("get_pp0_power_limit failed\n"); return ret; } return ret; } int c_rapl_interface::set_pp0_power_limit(unsigned long long value) { int ret; if (!pp0_domain_present()) { return -1; } ret = write_msr(first_cpu, MSR_PP0_POWER_LIMIT, value); if (ret < 0) { RAPL_ERROR_PRINT("set_pp0_power_limit failed\n"); return ret; } return ret; } int c_rapl_interface::get_pp0_power_policy(unsigned int *pp0_power_policy) { int ret; unsigned long long value; if (!pp0_domain_present()) { return -1; } ret = read_msr(first_cpu, MSR_PP0_POLICY, &value); if (ret < 0) { RAPL_ERROR_PRINT("get_pp0_power_policy failed\n"); return ret; } *pp0_power_policy = value & 0x0f; return ret; } int c_rapl_interface::get_pp1_energy_status(double *status) { int ret; unsigned long long value; if (!pp1_domain_present()) { return -1; } ret = read_msr(first_cpu, MSR_PP1_ENERY_STATUS, &value); if (ret < 0) { RAPL_ERROR_PRINT("get_pp1_energy_status failed\n"); return ret; } *status = (double) (value & 0xffffffff) * get_energy_status_unit(); return ret; } int c_rapl_interface::get_pp1_power_limit(unsigned long long *value) { int ret; if (!pp1_domain_present()) { return -1; } ret = read_msr(first_cpu, MSR_PP1_POWER_LIMIT, value); if (ret < 0) { RAPL_ERROR_PRINT("get_pp1_power_info failed\n"); return ret; } return ret; } int c_rapl_interface::set_pp1_power_limit(unsigned long long value) { int ret; if (!pp1_domain_present()) { return -1; } ret = write_msr(first_cpu, MSR_PP1_POWER_LIMIT, value); if (ret < 0) { RAPL_ERROR_PRINT("set_pp1_power_limit failed\n"); return ret; } return ret; } int c_rapl_interface::get_pp1_power_policy(unsigned int *pp1_power_policy) { int ret; unsigned long long value; if (!pp1_domain_present()) { return -1; } ret = read_msr(first_cpu, MSR_PP1_POLICY, &value); if (ret < 0) { RAPL_ERROR_PRINT("get_pp1_power_policy failed\n"); return ret; } *pp1_power_policy = value & 0x0f; return ret; } void c_rapl_interface::rapl_measure_energy() { #ifdef RAPL_TEST_MODE int ret; double energy_status; double thermal_spec_power; double max_power; double min_power; double max_time_window; double pkg_watts = 0; double dram_watts = 0; double pp0_watts = 0; double pp1_watts = 0; double pkg_joules = 0; double dram_joules = 0; double pp0_joules = 0; double pp1_joules = 0; ret = get_pkg_power_info(&thermal_spec_power, &max_power, &min_power, &max_time_window); RAPL_DBG_PRINT("Pkg Power Info: Thermal spec %f watts, max %f watts, min %f watts, max time window %f seconds\n", thermal_spec_power, max_power, min_power, max_time_window); ret = get_dram_power_info(&thermal_spec_power, &max_power, &min_power, &max_time_window); RAPL_DBG_PRINT("DRAM Power Info: Thermal spec %f watts, max %f watts, min %f watts, max time window %f seconds\n", thermal_spec_power, max_power, min_power, max_time_window); for (;;) { if (pkg_domain_present()) { ret = get_pkg_energy_status(&energy_status); if (last_pkg_energy_status == 0) last_pkg_energy_status = energy_status; if (ret > 0) { pkg_joules = energy_status; pkg_watts = (energy_status-last_pkg_energy_status)/measurment_interval; } last_pkg_energy_status = energy_status; } if (dram_domain_present()) { ret = get_dram_energy_status(&energy_status); if (last_dram_energy_status == 0) last_dram_energy_status = energy_status; if (ret > 0) { dram_joules = energy_status; dram_watts = (energy_status-last_dram_energy_status)/measurment_interval; } last_dram_energy_status = energy_status; } if (pp0_domain_present()) { ret = get_pp0_energy_status(&energy_status); if (last_pp0_energy_status == 0) last_pp0_energy_status = energy_status; if (ret > 0) { pp0_joules = energy_status; pp0_watts = (energy_status-last_pp0_energy_status)/measurment_interval; } last_pp0_energy_status = energy_status; } if (pp1_domain_present()) { ret = get_pp1_energy_status(&energy_status); if (last_pp1_energy_status == 0) last_pp1_energy_status = energy_status; if (ret > 0) { pp1_joules = energy_status; pp1_watts = (energy_status-last_pp1_energy_status)/measurment_interval; } last_pp1_energy_status = energy_status; } RAPL_DBG_PRINT("%f, %f, %f, %f\n", pkg_watts, dram_watts, pp0_watts, pp1_watts); sleep(measurment_interval); } #endif } thermal_daemon-1.1-rc2/src/thd_rapl_interface.h000066400000000000000000000055271225412722400216050ustar00rootroot00000000000000/* rapl_interface.h: rapl interface for power top * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef RAPL_INTERFACE_H #define RAPL_INTERFACE_H #include "thd_msr.h" class c_rapl_interface { private: static const int def_sampling_interval = 1; //In seconds unsigned char rapl_domains; int measurment_interval; int first_cpu; cthd_msr msr; double power_units; double energy_status_units; double time_units; double last_pkg_energy_status; double last_dram_energy_status; double last_pp0_energy_status; double last_pp1_energy_status; int read_msr(int cpu, unsigned int idx, unsigned long long *val); int write_msr(int cpu, unsigned int idx, unsigned long long val); public: c_rapl_interface(int cpu = 0); int get_rapl_power_unit(unsigned long long *value); double get_power_unit(); double get_energy_status_unit(); double get_time_unit(); int get_pkg_energy_status(double *status); int get_pkg_power_info(double *thermal_spec_power, double *max_power, double *min_power, double *max_time_window); int get_pkg_power_limit_msr(unsigned long long *value); int set_pkg_power_limit_msr(unsigned long long value); unsigned long long default_pkg_power_limit_msr_value; int set_pkg_power_limit(int time_window, int power); int store_pkg_power_limit(); int restore_pkg_power_limit(); int get_dram_energy_status(double *status); int get_dram_power_info(double *thermal_spec_power, double *max_power, double *min_power, double *max_time_window); int get_dram_power_limit(unsigned long long *value); int set_dram_power_limit(unsigned long long value); int get_pp0_energy_status(double *status); int get_pp0_power_limit(unsigned long long *value); int set_pp0_power_limit(unsigned long long value); int get_pp0_power_policy(unsigned int *pp0_power_policy); int get_pp1_energy_status(double *status); int get_pp1_power_limit(unsigned long long *value); int set_pp1_power_limit(unsigned long long value); int get_pp1_power_policy(unsigned int *pp1_power_policy); bool pkg_domain_present(); bool dram_domain_present(); bool pp0_domain_present(); bool pp1_domain_present(); void rapl_measure_energy(); }; #endif thermal_daemon-1.1-rc2/src/thd_sensor.cpp000066400000000000000000000062321225412722400204650ustar00rootroot00000000000000/* * thd_sensor.h: thermal sensor class implementation * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_sensor.h" #include "thd_engine.h" cthd_sensor::cthd_sensor(int _index, std::string control_path, std::string _type_str, int _type) : index(_index), type(_type), sensor_sysfs(control_path.c_str()), sensor_active( false), type_str(_type_str), async_capable(false), thresholds(0) { } int cthd_sensor::sensor_update() { if (type == SENSOR_TYPE_THERMAL_SYSFS) { if (sensor_sysfs.exists("type")) { sensor_sysfs.read("type", type_str); thd_log_info("sensor_update: type %s\n", type_str.c_str()); } else return THD_ERROR; if (sensor_sysfs.exists("temp")) { return THD_SUCCESS; } else { thd_log_warn("sensor id %d: No temp sysfs for reading temp\n", index); return THD_ERROR; } } if (type == SENSOR_TYPE_RAW) { if (sensor_sysfs.exists("")) { return THD_SUCCESS; } else { thd_log_warn("sensor id %d: No temp sysfs for reading raw temp\n", index); return THD_ERROR; } } return THD_SUCCESS; } unsigned int cthd_sensor::read_temperature() { csys_fs sysfs; std::string buffer; unsigned int temp; thd_log_debug("read_temperature sensor ID %d\n", index); if (type == SENSOR_TYPE_THERMAL_SYSFS) sensor_sysfs.read("temp", buffer); else sensor_sysfs.read("", buffer); std::istringstream(buffer) >> temp; thd_log_debug("Sensor %s :temp %u \n", type_str.c_str(), temp); return temp; } void cthd_sensor::enable_uevent() { csys_fs cdev_sysfs("/sys/class/thermal/"); std::stringstream policy_sysfs; policy_sysfs << "thermal_zone" << index << "/policy"; if (cdev_sysfs.exists(policy_sysfs.str().c_str())) { cdev_sysfs.write(policy_sysfs.str(), "user_space"); } } int cthd_sensor::set_threshold(int index, int temp) { if (type != SENSOR_TYPE_THERMAL_SYSFS) return THD_ERROR; std::stringstream tcdev; std::stringstream thres; int status = 0; if (!async_capable) { return THD_ERROR; } tcdev << "trip_point_" << index << "_temp"; thres << temp; if (sensor_sysfs.exists(tcdev.str().c_str())) { status = sensor_sysfs.write(tcdev.str(), thres.str()); } thd_log_debug("cthd_sensor::set_threshold: status %d\n", status); if (status > 0) { enable_uevent(); return THD_SUCCESS; } else return THD_ERROR; } void cthd_sensor::sensor_poll_trip(bool status) { if (status) thd_engine->thd_engine_poll_enable(index); else thd_engine->thd_engine_poll_disable(index); } thermal_daemon-1.1-rc2/src/thd_sensor.h000066400000000000000000000041421225412722400201300ustar00rootroot00000000000000/* * thd_sensor.h: thermal sensor class interface * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_SENSOR_H_ #define THD_SENSOR_H_ #include #include "thd_common.h" #include "thd_sys_fs.h" #define SENSOR_TYPE_THERMAL_SYSFS 0 #define SENSOR_TYPE_RAW 1 class cthd_sensor { protected: int index; int type; csys_fs sensor_sysfs; bool sensor_active; std::string type_str; bool async_capable; private: std::vector thresholds; void enable_uevent(); public: cthd_sensor(int _index, std::string control_path, std::string _type_str, int _type = SENSOR_TYPE_THERMAL_SYSFS); virtual ~cthd_sensor() { } int sensor_update(); std::string get_sensor_type() { return type_str; } unsigned int read_temperature(); int get_index() { return index; } int set_threshold(int index, int temp); ; void update_path(std::string str) { sensor_sysfs.update_path(str); } void set_async_capable(bool capable) { async_capable = capable; } bool check_async_capable() { return async_capable; } void sensor_dump() { thd_log_info("sensor index:%d %s Async:%d \n", index, type_str.c_str(), async_capable); } // Even if sensors are capable of async, it is possible that it is not reliable enough // at critical monitoring point. Sensors can be forced to go to poll mode at that temp void sensor_poll_trip(bool status); }; #endif /* THD_SENSOR_H_ */ thermal_daemon-1.1-rc2/src/thd_sys_fs.cpp000066400000000000000000000100421225412722400204540ustar00rootroot00000000000000/* * thd_sys_fs.cpp: sysfs class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_sys_fs.h" #include "thd_common.h" #include int csys_fs::write(const std::string &path, const std::string &buf) { std::string p = base_path + path; int fd = ::open(p.c_str(), O_WRONLY); if (fd < 0) { thd_log_warn("sysfs write failed %s\n", path.c_str()); return -errno; } int ret = ::write(fd, buf.c_str(), buf.size()); if (ret < 0) thd_log_warn("sysfs write failed %s\n", path.c_str()); close(fd); return ret; } int csys_fs::write(const std::string &path, unsigned int position, unsigned long long data) { std::string p = base_path + path; int fd = ::open(p.c_str(), O_WRONLY); if (fd < 0) { thd_log_warn("sysfs write failed %s\n", path.c_str()); return -errno; } if (::lseek(fd, position, SEEK_CUR) == -1) { thd_log_warn("sysfs write failed %s\n", path.c_str()); return -errno; } int ret = ::write(fd, &data, sizeof(data)); if (ret < 0) thd_log_warn("sysfs write failed %s\n", path.c_str()); close(fd); return ret; } int csys_fs::write(const std::string &path, unsigned int data) { std::ostringstream os; os << data; return csys_fs::write(path, os.str()); } int csys_fs::read(const std::string &path, char *buf, int len) { std::string p = base_path + path; int fd = ::open(p.c_str(), O_RDONLY); if (fd < 0) { thd_log_warn("sysfs read failed %s\n", path.c_str()); return -errno; } int ret = ::read(fd, buf, len); if (ret < 0) thd_log_warn("sysfs read failed %s\n", path.c_str()); close(fd); return ret; } int csys_fs::read(const std::string &path, unsigned int position, char *buf, int len) { std::string p = base_path + path; int fd = ::open(p.c_str(), O_RDONLY); if (fd < 0) { thd_log_warn("sysfs read failed %s\n", path.c_str()); return -errno; } if (::lseek(fd, position, SEEK_CUR) == -1) { thd_log_warn("sysfs read failed %s\n", path.c_str()); return -errno; } int ret = ::read(fd, buf, len); if (ret < 0) thd_log_warn("sysfs read failed %s\n", path.c_str()); close(fd); return ret; } int csys_fs::read(const std::string &path, unsigned int *ptr_val) { std::string p = base_path + path; char str[16]; int ret; int fd = ::open(p.c_str(), O_RDONLY); if (fd < 0) { thd_log_warn("sysfs read failed %s\n", path.c_str()); return -errno; } ret = ::read(fd, str, sizeof(str)); if (ret > 0) *ptr_val = atoi(str); else thd_log_warn("sysfs read failed %s\n", path.c_str()); close(fd); return ret; } int csys_fs::read(const std::string &path, std::string &buf) { std::string p = base_path + path; std::ifstream f(p.c_str(), std::fstream::in); if (f.fail()) { thd_log_warn("sysfs read failed %s\n", path.c_str()); return -EINVAL; } int ret = 0; f >> buf; if (f.bad()) { thd_log_warn("sysfs read failed %s\n", path.c_str()); ret = -EIO; } f.close(); return ret; } bool csys_fs::exists(const std::string &path) { struct stat s; return (bool) (stat((base_path + path).c_str(), &s) == 0); } bool csys_fs::exists() { return csys_fs::exists(""); } int csys_fs::read_symbolic_link_value(const std::string &path, char *buf, int len) { std::string p = base_path + path; int ret = ::readlink(p.c_str(), buf, len); if (ret < 0) { thd_log_warn("read_symbolic_link %s\n", path.c_str()); return -errno; } buf[ret] = '\0'; return 0; } thermal_daemon-1.1-rc2/src/thd_sys_fs.h000066400000000000000000000040251225412722400201250ustar00rootroot00000000000000/* * thd_sys_fs.h: sysfs class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_SYS_FS_H_ #define THD_SYS_FS_H_ #include #include #include #include #include #include #include #include #include class csys_fs { private: std::string base_path; public: csys_fs() : base_path("") { } ; csys_fs(const char *path) : base_path(path) { } /* write data to base path (dir) + provided path */ int write(const std::string &path, const std::string &buf); int write(const std::string &path, unsigned int data); int write(const std::string &path, unsigned int position, unsigned long long data); /* read data from base path (dir) + provided path */ int read(const std::string &path, char *buf, int len); int read(const std::string &path, std::string &buf); int read(const std::string &path, unsigned int *ptr_val); int read(const std::string &path, unsigned int position, char *buf, int len); const char *get_base_path() { return base_path.c_str(); } int read_symbolic_link_value(const std::string &path, char *buf, int len); bool exists(const std::string &path); bool exists(); void update_path(std::string path) { base_path = path; } }; #endif /* THD_SYS_FS_H_ */ thermal_daemon-1.1-rc2/src/thd_trip_point.cpp000066400000000000000000000136771225412722400213560ustar00rootroot00000000000000/* * thd_trip_point.cpp: thermal zone class implentation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include #include #include "thd_trip_point.h" #include "thd_engine.h" cthd_trip_point::cthd_trip_point(int _index, trip_point_type_t _type, unsigned int _temp, unsigned int _hyst, int _zone_id, int _sensor_id, trip_control_type_t _control_type) : index(_index), type(_type), temp(_temp), hyst(_hyst), control_type( _control_type), zone_id(_zone_id), sensor_id(_sensor_id), trip_on( false), poll_on(false) { thd_log_debug("Add trip pt %d:%d:0x%x:%d:%d\n", type, zone_id, sensor_id, temp, hyst); } bool cthd_trip_point::thd_trip_point_check(int id, unsigned int read_temp, int pref, bool *reset) { int on = -1; int off = -1; bool apply = false; *reset = false; if (sensor_id != DEFAULT_SENSOR_ID && sensor_id != id) return false; if (read_temp == 0) { thd_log_warn("TEMP == 0 pref: %d\n", pref); } if (type == CRITICAL) { if (read_temp >= temp) { thd_log_warn("critical temp reached \n"); sync(); reboot(RB_POWER_OFF); } } if (type == POLLING && sensor_id != DEFAULT_SENSOR_ID) { cthd_sensor *sensor = thd_engine->get_sensor(sensor_id); if (sensor && sensor->check_async_capable()) { if (!poll_on && read_temp >= temp) { thd_log_debug("polling trip reached, on \n"); sensor->sensor_poll_trip(true); poll_on = true; sensor->set_threshold(0, temp); } else if (poll_on && read_temp < temp) { thd_log_debug("polling trip reached, off \n"); sensor->sensor_poll_trip(false); thd_log_info("Dropped below poll threshold \n"); *reset = true; poll_on = false; sensor->set_threshold(0, temp); } } return true; } thd_log_debug("pref %d type %d temp %d trip %d \n", pref, type, read_temp, temp); switch (pref) { case PREF_DISABLED: return false; break; case PREF_PERFORMANCE: if (type == ACTIVE || type == MAX) { apply = true; thd_log_debug("Active Trip point applicable \n"); } break; case PREF_ENERGY_CONSERVE: if (type == PASSIVE || type == MAX) { apply = true; thd_log_debug("Passive Trip point applicable \n"); } break; default: break; } if (apply) { if (read_temp >= temp) { thd_log_debug("Trip point applicable > %d:%d \n", index, temp); on = 1; trip_on = true; } else if ((trip_on && (read_temp + hyst) < temp) || (!trip_on && read_temp < temp)) { thd_log_debug("Trip point applicable < %d:%d \n", index, temp); off = 1; trip_on = false; } } else return false; if (on != 1 && off != 1) return true; int i, ret; thd_log_debug("cdev size for this trippoint %lu\n", (unsigned long) cdevs.size()); if (on > 0) { for (unsigned i = 0; i < cdevs.size(); ++i) { cthd_cdev *cdev = cdevs[i].cdev; if (cdevs[i].sampling_priod) { time_t tm; time(&tm); if ((tm - cdevs[i].last_op_time) < cdevs[i].sampling_priod) { thd_log_info("Too early to act index %d tm %ld\n", cdev->thd_cdev_get_index(), tm - cdevs[i].last_op_time); continue; } cdevs[i].last_op_time = tm; } thd_log_debug("cdev at index %d:%s\n", cdev->thd_cdev_get_index(), cdev->get_cdev_type().c_str()); if (cdev->in_max_state()) { thd_log_debug("Need to switch to next cdev \n"); // No scope of control with this cdev continue; } ret = cdev->thd_cdev_set_state(temp, temp, read_temp, 1, zone_id); if (control_type == SEQUENTIAL && ret == THD_SUCCESS) { // Only one cdev activation break; } } } if (off > 0) { for (i = cdevs.size() - 1; i >= 0; --i) { cthd_cdev *cdev = cdevs[i].cdev; thd_log_debug("cdev at index %d:%s\n", cdev->thd_cdev_get_index(), cdev->get_cdev_type().c_str()); if (cdev->in_min_state()) { thd_log_debug("Need to switch to next cdev \n"); // No scope of control with this cdev continue; } cdev->thd_cdev_set_state(temp, temp, read_temp, 0, zone_id); if (control_type == SEQUENTIAL) { // Only one cdev activation break; } } } return true; } void cthd_trip_point::thd_trip_point_add_cdev(cthd_cdev &cdev, int influence, int sampling_period) { trip_pt_cdev_t thd_cdev; thd_cdev.cdev = &cdev; thd_cdev.influence = influence; thd_cdev.sampling_priod = sampling_period; thd_cdev.last_op_time = 0; trip_cdev_add(thd_cdev); } int cthd_trip_point::thd_trip_point_add_cdev_index(int _index, int influence) { cthd_cdev *cdev = thd_engine->thd_get_cdev_at_index(_index); if (cdev) { trip_pt_cdev_t thd_cdev; thd_cdev.cdev = cdev; thd_cdev.influence = influence; thd_cdev.sampling_priod = 0; thd_cdev.last_op_time = 0; trip_cdev_add(thd_cdev); return THD_SUCCESS; } else { thd_log_warn("thd_trip_point_add_cdev_index not present %d\n", _index); return THD_ERROR; } } void cthd_trip_point::thd_trip_cdev_state_reset() { thd_log_info("thd_trip_cdev_state_reset \n"); for (int i = cdevs.size() - 1; i >= 0; --i) { cthd_cdev *cdev = cdevs[i].cdev; thd_log_info("thd_trip_cdev_state_reset index %d:%s\n", cdev->thd_cdev_get_index(), cdev->get_cdev_type().c_str()); if (cdev->in_min_state()) { thd_log_debug("Need to switch to next cdev \n"); // No scope of control with this cdev continue; } cdev->thd_cdev_set_min_state(zone_id); } } thermal_daemon-1.1-rc2/src/thd_trip_point.h000066400000000000000000000070241225412722400210100ustar00rootroot00000000000000/* * thd_trip_point.h: thermal zone trip points class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_TRIP_POINT_H #define THD_TRIP_POINT_H #include "thd_common.h" #include "thd_sys_fs.h" #include "thd_preference.h" #include "thd_cdev.h" #include #include #include // std::sort typedef enum { CRITICAL, MAX, PASSIVE, ACTIVE, POLLING, INVALID_TRIP_TYPE } trip_point_type_t; typedef enum { PARALLEL, // All associated cdevs are activated together SEQUENTIAL // one after other once the previous cdev reaches its max state } trip_control_type_t; typedef struct { cthd_cdev *cdev; int influence; int sampling_priod; time_t last_op_time; } trip_pt_cdev_t; #define DEFAULT_SENSOR_ID 0xFFFF static bool trip_cdev_sort(trip_pt_cdev_t cdev1, trip_pt_cdev_t cdev2) { return (cdev1.influence > cdev2.influence); } class cthd_trip_point { private: int index; trip_point_type_t type; unsigned int temp; unsigned int hyst; std::vector cdevs; trip_control_type_t control_type; int zone_id; int sensor_id; bool trip_on; bool poll_on; public: static const int default_influence = 100; cthd_trip_point(int _index, trip_point_type_t _type, unsigned int _temp, unsigned int _hyst, int _zone_id, int _sensor_id, trip_control_type_t _control_type = PARALLEL); bool thd_trip_point_check(int id, unsigned int read_temp, int pref, bool *reset); void thd_trip_point_add_cdev(cthd_cdev &cdev, int influence, int sampling_period = 0); void thd_trip_cdev_state_reset(); int thd_trip_point_value() { return temp; } void thd_trip_update_set_point(unsigned int new_value) { temp = new_value; } int thd_trip_point_add_cdev_index(int _index, int influence); void thd_trip_point_set_control_type(trip_control_type_t type) { control_type = type; } trip_point_type_t get_trip_type() { return type; } unsigned int get_trip_temp() { return temp; } unsigned int get_trip_hyst() { return hyst; } void update_trip_temp(unsigned int _temp) { temp = _temp; } void update_trip_hyst(unsigned int _temp) { hyst = _temp; } int get_sensor_id() { return sensor_id; } void trip_cdev_add(trip_pt_cdev_t trip_cdev) { cdevs.push_back(trip_cdev); std::sort(cdevs.begin(), cdevs.end(), trip_cdev_sort); } void trip_dump() { std::string _type_str; if (type == CRITICAL) _type_str = "critical"; else if (type == MAX) _type_str = "max"; else if (type == PASSIVE) _type_str = "passive"; else if (type == ACTIVE) _type_str = "active"; else if (type == POLLING) _type_str = "polling"; else _type_str = "invalid"; thd_log_info( "index %d: type:%s temp:%u hyst:%u zone id:%d sensor id:%d cdev size:%lu\n", index, _type_str.c_str(), temp, hyst, zone_id, sensor_id, (unsigned long) cdevs.size()); } }; #endif thermal_daemon-1.1-rc2/src/thd_zone.cpp000066400000000000000000000202751225412722400201320ustar00rootroot00000000000000/* * thd_zone.cpp: thermal zone class implentation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ /* This class implements parent thermal zone/sensor. It is included * in a thermal engine. During initialization, it establishes a * relationship between cooling devices and trip points (where * some action needs to be taken). * When it gets a notification for a change, it reads the temperature * from sensors and uses cthd_trip point to schedule action on the event * if required. */ #include "thd_zone.h" #include "thd_engine.h" cthd_zone::cthd_zone(int _index, std::string control_path, sensor_relate_t rel) : index(_index), zone_sysfs(control_path.c_str()), zone_temp(0), zone_active( false), zone_cdev_binded_status(false), type_str(), sensor_rel( rel), thd_model(NULL) { thd_log_debug("Added zone index:%d \n", index); } cthd_zone::~cthd_zone() { trip_points.clear(); sensors.clear(); if (thd_model) delete thd_model; } void cthd_zone::thermal_zone_temp_change(int id, unsigned int temp, int pref) { int i, count; bool updated_max = false; bool reset = false; count = trip_points.size(); for (i = count - 1; i >= 0; --i) { cthd_trip_point &trip_point = trip_points[i]; if (trip_point.get_trip_type() == MAX) { thd_model->add_sample(zone_temp); if (thd_model->is_set_point_reached()) { int set_point; set_point = thd_model->get_set_point(); thd_log_debug("new set point %d \n", set_point); trip_point.thd_trip_update_set_point(set_point); updated_max = true; } } trip_point.thd_trip_point_check(id, temp, pref, &reset); // Force all cooling devices to min state if (reset) { zone_reset(); break; } } // Re-adjust polling thresholds if (updated_max) { for (i = count - 1; i >= 0; --i) { cthd_trip_point &trip_point = trip_points[i]; if (trip_point.get_trip_type() == POLLING) { thd_log_debug("new poll point %d \n", thd_model->get_hot_zone_trigger_point()); trip_point.thd_trip_update_set_point( thd_model->get_hot_zone_trigger_point()); trip_point.thd_trip_point_check(id, temp, pref, &reset); } } } } void cthd_zone::update_zone_preference() { if (!zone_active) return; thd_log_debug("update_zone_preference\n"); thd_model->update_user_set_max_temp(); for (unsigned int i = 0; i < sensors.size(); ++i) { cthd_sensor *sensor; sensor = sensors[i]; zone_temp = sensor->read_temperature(); thermal_zone_temp_change(sensor->get_index(), 0, thd_engine->get_preference()); } for (unsigned int i = 0; i < sensors.size(); ++i) { cthd_sensor *sensor; sensor = sensors[i]; zone_temp = sensor->read_temperature(); thermal_zone_temp_change(sensor->get_index(), zone_temp, thd_engine->get_preference()); } } int cthd_zone::zone_update() { int ret; thd_model = new cthd_model(type_str, true); if (zone_bind_sensors() != THD_SUCCESS) { thd_log_warn("Zone update failed: unable to bind \n"); return THD_ERROR; } ret = read_trip_points(); if (ret != THD_SUCCESS) return THD_ERROR; if (trip_points.size()) { unsigned int polling_trip = 0; unsigned int max_trip_temp = 0; // Set the lowest trip point as the threshold for sensor async mode // Use that the lowest point, after that we poll if (trip_points.size()) polling_trip = trip_points[0].get_trip_temp(); for (unsigned int i = 0; i < trip_points.size(); ++i) { if (polling_trip < trip_points[i].get_trip_temp()) polling_trip = trip_points[i].get_trip_temp(); if (trip_points[i].get_trip_type() == MAX) max_trip_temp = trip_points[i].get_trip_temp(); thd_log_info("trip type: %d temp: %d \n", trip_points[i].get_trip_type(), trip_points[i].get_trip_temp()); } for (unsigned int i = 0; i < sensors.size(); ++i) { cthd_sensor *sensor; sensor = sensors[i]; if (sensor->check_async_capable()) { if (max_trip_temp) { // We have to guarantee MAX, so we better // wake up before, so that by the time // we are notified, temp > max temp thd_model->set_max_temperature(max_trip_temp); polling_trip = thd_model->get_hot_zone_trigger_point(); } sensor->set_threshold(0, polling_trip); cthd_trip_point trip_pt_polling(trip_points.size(), POLLING, polling_trip, 0, index, sensor->get_index()); trip_pt_polling.thd_trip_point_set_control_type(PARALLEL); trip_points.push_back(trip_pt_polling); } } } ret = read_cdev_trip_points(); if (ret != THD_SUCCESS) return THD_ERROR; for (unsigned int i = 0; i < trip_points.size(); ++i) { cthd_trip_point &trip_point = trip_points[i]; unsigned int set_point; if (trip_point.get_trip_type() == MAX) { thd_model->set_max_temperature(trip_point.get_trip_temp()); set_point = thd_model->get_set_point(); if (set_point != thd_model->get_set_point()) { trip_point.thd_trip_update_set_point(set_point); } } } return THD_SUCCESS; } void cthd_zone::read_zone_temp() { if (zone_active) { unsigned int temp; zone_temp = 0; for (unsigned int i = 0; i < sensors.size(); ++i) { cthd_sensor *sensor; sensor = sensors[i]; temp = sensor->read_temperature(); if (zone_temp < temp) zone_temp = temp; if (sensor_rel == SENSOR_INDEPENDENT) thermal_zone_temp_change(sensor->get_index(), temp, thd_engine->get_preference()); } if (sensor_rel == SENSORS_CORELATED && zone_temp) thermal_zone_temp_change(sensors[0]->get_index(), zone_temp, thd_engine->get_preference()); } } void cthd_zone::zone_temperature_notification(int type, int data) { read_zone_temp(); } void cthd_zone::zone_reset() { int i, count; if (zone_active) { count = trip_points.size(); for (i = count - 1; i >= 0; --i) { cthd_trip_point &trip_point = trip_points[i]; trip_point.thd_trip_cdev_state_reset(); } } } int cthd_zone::bind_cooling_device(trip_point_type_t type, unsigned int trip_temp, cthd_cdev *cdev) { int i, count; bool added = false; // trip_temp = 0 is a special case, where it will add to first matched type count = trip_points.size(); for (i = 0; i < count; ++i) { cthd_trip_point &trip_point = trip_points[i]; if (trip_point.get_trip_type() == type && (trip_temp == 0 || trip_point.get_trip_temp() == trip_temp)) { trip_point.thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence); added = true; break; } } #if 0 // Check again, if we really need this logic if (!added && trip_temp) { // Create a new trip point and add only if trip_temp is valid cthd_trip_point trip_pt(count, type, trip_temp, 0, index, DEFAULT_SENSOR_ID); trip_points.push_back(trip_pt); added = true; } #endif if (added) return THD_SUCCESS; else return THD_ERROR; } int cthd_zone::update_max_temperature(int max_temp) { std::stringstream filename; std::stringstream temp_str; filename << TDRUNDIR << "/" << "thd_user_set_max." << type_str << "." << "conf"; std::ofstream fout(filename.str().c_str()); if (!fout.good()) { return THD_ERROR; } temp_str << max_temp; fout << temp_str.str(); fout.close(); return THD_SUCCESS; } void cthd_zone::add_trip(cthd_trip_point &trip) { bool add = true; for (unsigned int j = 0; j < trip_points.size(); ++j) { if (trip_points[j].get_trip_type() == trip.get_trip_type()) { thd_log_debug("updating existing trip temp \n"); trip_points[j] = trip; if (trip.get_trip_type() == MAX) { thd_model->set_max_temperature(trip.get_trip_temp()); // TODO: If sensor supports polling // update the polling threshold also. } add = false; break; } } if (add) trip_points.push_back(trip); } thermal_daemon-1.1-rc2/src/thd_zone.h000066400000000000000000000073511225412722400175770ustar00rootroot00000000000000/* * thd_zone.h: thermal zone class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_ZONE_H #define THD_ZONE_H #include #include "thd_common.h" #include "thd_sys_fs.h" #include "thd_preference.h" #include "thd_cdev.h" #include "thd_trip_point.h" #include "thd_sensor.h" #include "thd_model.h" typedef struct { int zone; int type; unsigned int data; } thermal_zone_notify_t; // If the zone has multiple sensors, there are two possibilities // Either the are independent, means that each has own set of trip points // Or related. In this case one trip point. Here we take max of the sensor reading // and then apply trip typedef enum { SENSOR_INDEPENDENT, SENSORS_CORELATED } sensor_relate_t; class cthd_zone { protected: int index; std::vector trip_points; std::string temperature_sysfs_path; csys_fs zone_sysfs; unsigned int zone_temp; bool zone_active; bool zone_cdev_binded_status; std::string type_str; std::vector sensors; sensor_relate_t sensor_rel; cthd_model *thd_model; virtual int zone_bind_sensors() = 0; void thermal_zone_temp_change(int id, unsigned int temp, int pref); private: public: cthd_zone(int _index, std::string control_path, sensor_relate_t rel = SENSOR_INDEPENDENT); virtual ~cthd_zone(); void zone_temperature_notification(int type, int data); int zone_update(); virtual void update_zone_preference(); void zone_reset(); virtual int read_trip_points() = 0; virtual int read_cdev_trip_points() = 0; virtual void read_zone_temp(); int get_zone_index() { return index; } void add_trip(cthd_trip_point &trip); void set_zone_active() { zone_active = true; } ; bool zone_active_status() { return zone_active; } bool zone_cdev_binded() { return zone_cdev_binded_status; } std::string get_zone_type() { return type_str; } void set_zone_type(std::string type) { type_str = type; } void bind_sensor(cthd_sensor *sensor) { for (unsigned int i = 0; i < sensors.size(); ++i) { if (sensors[i] == sensor) return; } sensors.push_back(sensor); } // Even if one sensor, it is using doesn't // provide async control, return false bool check_sensor_async_status() { for (unsigned int i = 0; i < sensors.size(); ++i) { cthd_sensor *sensor = sensors[i]; if (!sensor->check_async_capable()) { return false; } } return true; } unsigned int get_trip_count() { return trip_points.size(); } int update_max_temperature(int max_temp); int bind_cooling_device(trip_point_type_t type, unsigned int trip_temp, cthd_cdev *cdev); void zone_dump() { thd_log_info("Zone %d: %s, Active:%d Bind:%d Sensor_cnt:%lu\n", index, type_str.c_str(), zone_active, zone_cdev_binded_status, (unsigned long) sensors.size()); thd_log_info("..sensors.. \n"); for (unsigned int i = 0; i < sensors.size(); ++i) { sensors[i]->sensor_dump(); } thd_log_info("..trips.. \n"); for (unsigned int i = 0; i < trip_points.size(); ++i) { trip_points[i].trip_dump(); } } ; }; #endif thermal_daemon-1.1-rc2/src/thd_zone_cpu.cpp000066400000000000000000000163231225412722400210000ustar00rootroot00000000000000/* * thd_zone_dts.cpp: thermal engine DTS class implementation * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * * This implementation allows to use core temperature interface. */ /* Implementation of DTS sensor Zone. This * - Identifies DTS sensors, and use maximum reported temperature to control * - Prioritize the cooling device order * - Sets one control point starting from target temperature (max) not critical. * Very first time it reaches this temperature cthd_model, calculates a set point when * cooling device needs to be activated. * */ #include #include "thd_zone_cpu.h" #include "thd_engine_default.h" #include "thd_cdev_order_parser.h" const char *def_cooling_devices[] = { "rapl_controller", "intel_pstate", "intel_powerclamp", "cpufreq", "Processor", NULL }; cthd_zone_cpu::cthd_zone_cpu(int index, std::string path, int package_id) : cthd_zone(index, path, SENSORS_CORELATED), dts_sysfs(path.c_str()), critical_temp( 0), max_temp(0), set_point(0), prev_set_point(0), trip_point_cnt( 0), sensor_mask(0), phy_package_id(package_id), pkg_thres_th_zone( -1), pkg_temp_poll_enable(false) { type_str = "cpu"; thd_log_debug("zone dts syfs: %s, package id %d \n", path.c_str(), package_id); } int cthd_zone_cpu::init() { critical_temp = 0; int temp = 0; bool found = false; max_temp = 0; critical_temp = 0; // Calculate the temperature trip points using the settings in coretemp for (int i = 0; i < max_dts_sensors; ++i) { std::stringstream temp_crit_str; std::stringstream temp_max_str; temp_crit_str << "temp" << i << "_crit"; temp_max_str << "temp" << i << "_max"; if (dts_sysfs.exists(temp_crit_str.str())) { std::string temp_str; dts_sysfs.read(temp_crit_str.str(), temp_str); std::istringstream(temp_str) >> temp; if (critical_temp == 0 || temp < critical_temp) critical_temp = temp; } if (dts_sysfs.exists(temp_max_str.str())) { // Set which index is present sensor_mask = sensor_mask | (1 << i); std::string temp_str; dts_sysfs.read(temp_max_str.str(), temp_str); std::istringstream(temp_str) >> temp; if (max_temp == 0 || temp < max_temp) max_temp = temp; found = true; } else if (dts_sysfs.exists(temp_crit_str.str())) { thd_log_info( "DTS: MAX target temp not found, using (crit - offset) \n"); // Set which index is present sensor_mask = sensor_mask | (1 << i); std::string temp_str; dts_sysfs.read(temp_crit_str.str(), temp_str); std::istringstream(temp_str) >> temp; // Adjust offset from critical (target temperature for cooling) temp = temp - def_offset_from_critical; if (max_temp == 0 || temp < max_temp) max_temp = temp; found = true; } } if (!found) { thd_log_error("DTS temperature path not found \n"); return THD_ERROR; } thd_log_info("Core temp DTS :critical %d, max %d\n", critical_temp, max_temp); if (critical_temp == 0) critical_temp = def_critical_temp; if (max_temp == 0) { max_temp = def_critical_temp - def_offset_from_critical; thd_log_info("Force max temp to %d\n", max_temp); } else if ((critical_temp - max_temp) < def_offset_from_critical) { max_temp = critical_temp - def_offset_from_critical; thd_log_info("Buggy max temp: to close to critical %d\n", max_temp); } thd_model->set_max_temperature(max_temp); prev_set_point = set_point = thd_model->get_set_point(); return THD_SUCCESS; } int cthd_zone_cpu::load_cdev_xml(cthd_trip_point &trip_pt, std::vector &list) { cthd_cdev *cdev; for (unsigned int i = 0; i < list.size(); ++i) { thd_log_debug("- %s\n", list[i].c_str()); cdev = thd_engine->search_cdev(list[i]); if (cdev) { trip_pt.thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence); } } return THD_SUCCESS; } int cthd_zone_cpu::parse_cdev_order() { cthd_cdev_order_parse parser; std::vector order_list; int ret = THD_ERROR; if ((ret = parser.parser_init()) == THD_SUCCESS) { if ((ret = parser.start_parse()) == THD_SUCCESS) { ret = parser.get_order_list(order_list); if (ret == THD_SUCCESS) { cthd_trip_point trip_pt(trip_point_cnt, MAX, set_point, def_hystersis, index, DEFAULT_SENSOR_ID); trip_pt.thd_trip_point_set_control_type(SEQUENTIAL); load_cdev_xml(trip_pt, order_list); trip_points.push_back(trip_pt); trip_point_cnt++; } } parser.parser_deinit(); return ret; } return ret; } int cthd_zone_cpu::read_trip_points() { int ret; cthd_cdev *cdev; int i; ret = parse_cdev_order(); if (ret == THD_SUCCESS) { thd_log_info("CDEVS order specified in thermal-cpu-cdev-order.xml\n"); return THD_SUCCESS; } cthd_trip_point trip_pt_passive(trip_point_cnt, MAX, set_point, def_hystersis, index, DEFAULT_SENSOR_ID); trip_pt_passive.thd_trip_point_set_control_type(SEQUENTIAL); i = 0; while (def_cooling_devices[i]) { cdev = thd_engine->search_cdev(def_cooling_devices[i]); if (cdev) { trip_pt_passive.thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence); } ++i; } trip_points.push_back(trip_pt_passive); trip_point_cnt++; // Add active trip point at the end // This is required for setting up async trip point cthd_trip_point trip_pt_active(trip_point_cnt, ACTIVE, set_point, def_hystersis, index, DEFAULT_SENSOR_ID); trip_pt_active.thd_trip_point_set_control_type(SEQUENTIAL); cdev = thd_engine->search_cdev("Fan"); if (cdev) { trip_pt_active.thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence); trip_points.push_back(trip_pt_active); trip_point_cnt++; } return THD_SUCCESS; } int cthd_zone_cpu::zone_bind_sensors() { cthd_sensor *sensor; int status = THD_ERROR; if (init() != THD_SUCCESS) return THD_ERROR; sensor = thd_engine->search_sensor("pkg-temp-0"); if (sensor) { bind_sensor(sensor); return THD_SUCCESS; } // No package temp sensor fallback to core temp int cnt = 0; unsigned int mask = 0x1; do { if (sensor_mask & mask) { std::stringstream temp_input_str; temp_input_str << "temp" << cnt << "_input"; cthd_sensor *sensor; sensor = thd_engine->search_sensor(temp_input_str.str()); if (sensor) { bind_sensor(sensor); status = THD_SUCCESS; } } mask = (mask << 1); cnt++; } while (mask != 0); if (status != THD_SUCCESS) { thd_log_info("Trying to bind hwmon sensor \n"); sensor = thd_engine->search_sensor("hwmon"); if (sensor) { bind_sensor(sensor); status = THD_SUCCESS; thd_log_info("Bind hwmon sensor \n"); } } return status; } int cthd_zone_cpu::read_cdev_trip_points() { return THD_SUCCESS; } thermal_daemon-1.1-rc2/src/thd_zone_cpu.h000066400000000000000000000034061225412722400204430ustar00rootroot00000000000000/* * thd_zone_dts.h: thermal engine DTS class interface * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * * This interface allows to overide per zone read data from sysfs for buggy BIOS. */ #ifndef THD_ZONE_DTS_H #define THD_ZONE_DTS_H #include "thd_zone.h" #include class cthd_zone_cpu: public cthd_zone { protected: csys_fs dts_sysfs; int critical_temp; int max_temp; int set_point; int prev_set_point; int trip_point_cnt; unsigned int sensor_mask; int phy_package_id; std::vector sensor_sysfs; int init(); int parse_cdev_order(); int pkg_thres_th_zone; bool pkg_temp_poll_enable; public: static const int max_dts_sensors = 16; static const int def_hystersis = 0; static const int def_offset_from_critical = 10000; static const int def_critical_temp = 100000; cthd_zone_cpu(int count, std::string path, int package_id); int load_cdev_xml(cthd_trip_point &trip_pt, std::vector &list); virtual int read_trip_points(); int read_cdev_trip_points(); int zone_bind_sensors(); }; #endif thermal_daemon-1.1-rc2/src/thd_zone_generic.cpp000066400000000000000000000071541225412722400216270ustar00rootroot00000000000000/* * thd_zone_generic.cpp: zone implementation for xml conf * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_zone_generic.h" #include "thd_engine.h" cthd_zone_generic::cthd_zone_generic(int index, int _config_index, std::string type) : cthd_zone(index, ""), trip_point_cnt(0), config_index(_config_index), zone( NULL) { type_str = type; } int cthd_zone_generic::read_trip_points() { thermal_zone_t *zone_config = thd_engine->parser.get_zone_dev_index( config_index); int trip_point_cnt = 0; if (!zone_config) return THD_ERROR; for (unsigned int i = 0; i < zone_config->trip_pts.size(); ++i) { trip_point_t &trip_pt_config = zone_config->trip_pts[i]; cthd_sensor *sensor = thd_engine->search_sensor( trip_pt_config.sensor_type); if (!sensor) { thd_log_error("XML zone: invalid sensor type \n"); continue; } sensor_list.push_back(sensor); cthd_trip_point *trip_ptr = NULL; bool add = false; for (unsigned int j = 0; j < trip_points.size(); ++j) { if (trip_points[j].get_trip_type() == trip_pt_config.trip_pt_type) { thd_log_debug("updating existing trip temp \n"); trip_points[j].update_trip_temp(trip_pt_config.temperature); trip_points[j].update_trip_hyst(trip_pt_config.hyst); trip_ptr = &trip_points[j]; break; } } if (!trip_ptr) { trip_ptr = new cthd_trip_point(trip_point_cnt, trip_pt_config.trip_pt_type, trip_pt_config.temperature, trip_pt_config.hyst, index, sensor->get_index(), trip_pt_config.control_type); if (!trip_ptr) { thd_log_warn("Mem alloc error for new trip \n"); return THD_ERROR; } if (trip_pt_config.trip_pt_type == MAX) { thd_model->set_max_temperature(trip_pt_config.temperature); if (thd_model->get_set_point()) { trip_ptr->update_trip_temp(thd_model->get_set_point()); } } add = true; } // bind cdev for (unsigned int j = 0; j < trip_pt_config.cdev_trips.size(); ++j) { cthd_cdev *cdev = thd_engine->search_cdev( trip_pt_config.cdev_trips[j].type); if (cdev) { trip_ptr->thd_trip_point_add_cdev(*cdev, trip_pt_config.cdev_trips[j].influence, trip_pt_config.cdev_trips[j].sampling_period); } } if (add) { trip_points.push_back(*trip_ptr); ++trip_point_cnt; } } return 0; } int cthd_zone_generic::read_cdev_trip_points() { return 0; } int cthd_zone_generic::zone_bind_sensors() { cthd_sensor *sensor; thermal_zone_t *zone_config = thd_engine->parser.get_zone_dev_index( config_index); if (!zone_config) return THD_ERROR; sensor = NULL; for (unsigned int i = 0; i < zone_config->trip_pts.size(); ++i) { trip_point_t &trip_pt_config = zone_config->trip_pts[i]; sensor = thd_engine->search_sensor(trip_pt_config.sensor_type); if (!sensor) { thd_log_error("XML zone: invalid sensor type \n"); continue; } bind_sensor(sensor); } if (!sensor) return THD_ERROR; return THD_SUCCESS; } thermal_daemon-1.1-rc2/src/thd_zone_generic.h000066400000000000000000000024331225412722400212670ustar00rootroot00000000000000/* * thd_zone_generic.h: zone interface for xml conf * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_ZONE_GENERIC_H_ #define THD_ZONE_GENERIC_H_ #include "thd_zone.h" class cthd_zone_generic: public cthd_zone { private: int trip_point_cnt; int config_index; cthd_zone *zone; std::vector sensor_list; public: cthd_zone_generic(int index, int _config_index, std::string type); virtual int read_trip_points(); virtual int read_cdev_trip_points(); virtual int zone_bind_sensors(); }; #endif /* THD_ZONE_GENERIC_H_ */ thermal_daemon-1.1-rc2/src/thd_zone_surface.cpp000066400000000000000000000051361225412722400216410ustar00rootroot00000000000000/* * thd_zone_surface.cpp: zone implementation for external surface * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_zone_surface.h" const char *skin_sensors[] = { "TSKN", "SKIN", "skin", "tskin", "TSKIN", "cover", NULL }; cthd_zone_surface::cthd_zone_surface(int index) : cthd_zone(index, ""), sensor(NULL) { type_str = "Surface"; } int cthd_zone_surface::read_trip_points() { cthd_trip_point *trip_ptr = NULL; bool add = false; if (!sensor) return THD_ERROR; for (unsigned int j = 0; j < trip_points.size(); ++j) { if (trip_points[j].get_trip_type() == PASSIVE) { thd_log_debug("updating existing trip temp \n"); trip_points[j].update_trip_temp(passive_trip_temp); trip_points[j].update_trip_hyst(passive_trip_hyst); trip_ptr = &trip_points[j]; break; } } if (!trip_ptr) { trip_ptr = new cthd_trip_point(trip_points.size(), PASSIVE, passive_trip_temp, passive_trip_hyst, index, sensor->get_index(), SEQUENTIAL); if (!trip_ptr) { thd_log_warn("Mem alloc error for new trip \n"); return THD_ERROR; } add = true; } cthd_cdev *cdev = thd_engine->search_cdev("rapl_controller"); if (cdev) { trip_ptr->thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence, surface_sampling_period); } cdev = thd_engine->search_cdev("intel_powerclamp"); if (cdev) { trip_ptr->thd_trip_point_add_cdev(*cdev, cthd_trip_point::default_influence, surface_sampling_period); } if (add) { trip_points.push_back(*trip_ptr); } return THD_SUCCESS; } int cthd_zone_surface::zone_bind_sensors() { int i = 0; while (skin_sensors[i]) { sensor = thd_engine->search_sensor(skin_sensors[i]); if (sensor) break; ++i; } if (!sensor) { thd_log_error("No SKIN sensor \n"); return THD_ERROR; } bind_sensor(sensor); return THD_SUCCESS; } int cthd_zone_surface::read_cdev_trip_points() { return THD_SUCCESS; } thermal_daemon-1.1-rc2/src/thd_zone_surface.h000066400000000000000000000025071225412722400213050ustar00rootroot00000000000000/* * thd_zone_surface.h: zone interface for external surface * * Copyright (C) 2013 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_ZONE_SURFACE_H_ #define THD_ZONE_SURFACE_H_ #include "thd_zone_therm_sys_fs.h" #include "thd_engine.h" class cthd_zone_surface: public cthd_zone { private: cthd_sensor *sensor; public: static const int passive_trip_temp = 45000; static const int passive_trip_hyst = 1000; static const int surface_sampling_period = 12; cthd_zone_surface(int count); int read_trip_points(); int read_cdev_trip_points(); int zone_bind_sensors(); }; #endif /* THD_ZONE_SURFACE_H_ */ thermal_daemon-1.1-rc2/src/thd_zone_therm_sys_fs.cpp000066400000000000000000000116371225412722400227210ustar00rootroot00000000000000/* * thd_zone_therm_sys_fs.cpp: thermal zone class implementation * for thermal sysfs * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #include "thd_zone_therm_sys_fs.h" #include "thd_engine.h" #include cthd_sysfs_zone::cthd_sysfs_zone(int count, std::string path) : cthd_zone(count, path), trip_point_cnt(0), zone(NULL) { std::stringstream tc_type_dev; tc_type_dev << index << "/type"; thd_log_debug("Thermal Zone look for %s\n", tc_type_dev.str().c_str()); if (zone_sysfs.exists(tc_type_dev.str())) { zone_sysfs.read(tc_type_dev.str(), type_str); } thd_log_debug("Thermal Zone %d:%s\n", index, type_str.c_str()); } int cthd_sysfs_zone::zone_bind_sensors() { cthd_sensor *sensor; sensor = thd_engine->search_sensor(type_str); if (sensor) { bind_sensor(sensor); } else return THD_ERROR; return THD_SUCCESS; } int cthd_sysfs_zone::read_trip_points() { // Gather all trip points std::stringstream trip_sysfs; trip_sysfs << index << "/" << "trip_point_"; for (int i = 0; i < max_trip_points; ++i) { std::stringstream type_stream; std::stringstream temp_stream; std::stringstream hist_stream; std::string _type_str; std::string _temp_str; std::string _hist_str; trip_point_type_t trip_type; unsigned int temp = 0, hyst = 1; type_stream << trip_sysfs.str() << i << "_type"; if (zone_sysfs.exists(type_stream.str())) { zone_sysfs.read(type_stream.str(), _type_str); thd_log_debug("read_trip_points %s:%s \n", type_stream.str().c_str(), _type_str.c_str()); } temp_stream << trip_sysfs.str() << i << "_temp"; if (zone_sysfs.exists(temp_stream.str())) { zone_sysfs.read(temp_stream.str(), _temp_str); std::istringstream(_temp_str) >> temp; thd_log_debug("read_trip_points %s:%s \n", temp_stream.str().c_str(), _temp_str.c_str()); } hist_stream << trip_sysfs.str() << i << "_hyst"; if (zone_sysfs.exists(hist_stream.str())) { zone_sysfs.read(hist_stream.str(), _hist_str); std::istringstream(_hist_str) >> hyst; thd_log_debug("read_trip_points %s:%s \n", hist_stream.str().c_str(), _hist_str.c_str()); } if (_type_str == "critical") trip_type = CRITICAL; else if (_type_str == "hot") trip_type = MAX; else if (_type_str == "active") trip_type = ACTIVE; else if (_type_str == "passive") trip_type = PASSIVE; else trip_type = INVALID_TRIP_TYPE; if (temp > 0 && trip_type != INVALID_TRIP_TYPE) { cthd_sensor *sensor; sensor = thd_engine->search_sensor(type_str); if (sensor) { cthd_trip_point trip_pt(trip_point_cnt, trip_type, temp, hyst, index, sensor->get_index()); trip_points.push_back(trip_pt); ++trip_point_cnt; } } } thd_log_debug("read_trip_points Added %d trips \n", trip_point_cnt); if (trip_point_cnt == 0) return THD_ERROR; else return THD_SUCCESS; } int cthd_sysfs_zone::read_cdev_trip_points() { thd_log_debug(" >> read_cdev_trip_points for \n"); // Gather all Cdevs // Gather all trip points std::stringstream cdev_sysfs; cdev_sysfs << index << "/" << "cdev"; for (int i = 0; i < max_cool_devs; ++i) { std::stringstream trip_pt_stream, cdev_stream; std::string trip_pt_str; int trip_cnt = -1; char buf[50], *ptr; trip_pt_stream << cdev_sysfs.str() << i << "_trip_point"; if (zone_sysfs.exists(trip_pt_stream.str())) { zone_sysfs.read(trip_pt_stream.str(), trip_pt_str); std::istringstream(trip_pt_str) >> trip_cnt; } else continue; thd_log_debug("cdev trip point: %s contains %d\n", trip_pt_str.c_str(), trip_cnt); cdev_stream << cdev_sysfs.str() << i; if (zone_sysfs.exists(cdev_stream.str())) { thd_log_debug("cdev%d present\n", i); int ret = zone_sysfs.read_symbolic_link_value(cdev_stream.str(), buf, 50); if (ret == 0) { ptr = strstr(buf, "cooling_device"); if (ptr) { ptr += strlen("cooling_device"); thd_log_debug("symbolic name %s:%s\n", buf, ptr); if (trip_cnt >= 0) { trip_points[trip_cnt].thd_trip_point_add_cdev_index( atoi(ptr), cthd_trip_point::default_influence); zone_cdev_binded_status = true; } } } } } thd_log_debug( "cthd_sysfs_zone::read_cdev_trip_points: ZONE bound to CDEV status %d \n", zone_cdev_binded_status); return THD_SUCCESS; } thermal_daemon-1.1-rc2/src/thd_zone_therm_sys_fs.h000066400000000000000000000024761225412722400223670ustar00rootroot00000000000000/* * thd_zone_therm_sys_fs.h: thermal zone class interface * for thermal sysfs * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_ZONE_THERM_SYS_FS_H_ #define THD_ZONE_THERM_SYS_FS_H_ #include "thd_zone.h" class cthd_sysfs_zone: public cthd_zone { private: int trip_point_cnt; cthd_zone *zone; public: static const int max_trip_points = 50; static const int max_cool_devs = 50; cthd_sysfs_zone(int count, std::string path); virtual int read_trip_points(); virtual int read_cdev_trip_points(); virtual int zone_bind_sensors(); }; #endif /* THD_ZONE_THERM_SYS_FS_H_ */ thermal_daemon-1.1-rc2/src/thermald.h000066400000000000000000000051131225412722400175570ustar00rootroot00000000000000/* * thermald.h: Thermal Daemon common header file * * Copyright (C) 2012 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 or later as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * * Author Name * */ #ifndef THD_THERMALD_H #define THD_THERMALD_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #ifdef ANDROID #define LOG_NDEBUG 1 #undef LOG_TAG #define LOG_TAG "THERMALD" #include #include #include #define thd_log_fatal ALOGE #define thd_log_error ALOGE #define thd_log_warn ALOGW #define thd_log_info ALOGD #define thd_log_debug ALOGV #else #define LOCKF_SUPPORT #ifdef GLIBC_SUPPORT #include #include #include #include #include #include // Log macros #define thd_log_fatal g_error // Print error and terminate #define thd_log_error g_critical #define thd_log_warn g_warning #define thd_log_debug g_debug #define thd_log_info(...) g_log(NULL, G_LOG_LEVEL_INFO, __VA_ARGS__) #else static int dummy_printf(const char *__restrict __format, ...) { return 0; } #define thd_log_fatal printf #define thd_log_error printf #define thd_log_warn printf #define thd_log_debug dummy_printf #define thd_log_info printf #endif #endif // Common return value defines #define THD_SUCCESS 0 #define THD_ERROR -1 #define THD_FATAL_ERROR -2 // Dbus related /* Well-known name for this service. */ #define THD_SERVICE_NAME "org.freedesktop.thermald" #define THD_SERVICE_OBJECT_PATH "/org/freedesktop/thermald" #define THD_SERVICE_INTERFACE "org.freedesktop.thermald" class cthd_engine; class cthd_engine_therm_sysfs; extern cthd_engine *thd_engine; extern int thd_poll_interval; #endif thermal_daemon-1.1-rc2/thermal_daemon_usage.txt000066400000000000000000000000551225412722400217230ustar00rootroot00000000000000Thermal Daemon Usage: Use: man thermald thermal_daemon-1.1-rc2/tools/000077500000000000000000000000001225412722400161575ustar00rootroot00000000000000thermal_daemon-1.1-rc2/tools/thermald_set_pref.sh000077500000000000000000000025601225412722400222100ustar00rootroot00000000000000#!/bin/sh echo "****thermald preference****" echo "0 : DEFAULT" echo "1 : PERFORMANCE" echo "2 : ENERGY_CONSERVE" echo "3 : DISABLED" echo "4 : CALIBRATE" echo "5 : SET USER DEFINED CPU MAX temp" echo "6 : TERMINATE" echo -n " Enter thermald preference [1..6]: " read opt_no case $opt_no in 0) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.SetCurrentPreference string:"FALLBACK" ;; 1) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.SetCurrentPreference string:"PERFORMANCE" ;; 2) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.SetCurrentPreference string:"ENERGY_CONSERVE" ;; 3) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.SetCurrentPreference string:"DISABLE" ;; 4) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Calibrate ;; 5) echo -n " Enter valid max temp in mill degree celsius " read max_temp dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.SetUserMaxTemperature string:cpu string:$max_temp ;; 6) dbus-send --system --dest=org.freedesktop.thermald /org/freedesktop/thermald org.freedesktop.thermald.Terminate ;; *) echo "Invalid option" esac