<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Linux Sin Humo (Publicaciones sobre power-management)</title><link>https://sergiobelkin.com/</link><description></description><atom:link href="https://sergiobelkin.com/categories/power-management.xml" rel="self" type="application/rss+xml"></atom:link><language>es</language><copyright>Contents © 2026 &lt;a href="mailto:sebelk@gmail.com"&gt;sebelk&lt;/a&gt; 
&lt;a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/"&gt;
&lt;img alt="Creative Commons License BY-NC-SA"
style="border-width:0; margin-bottom:12px;"
src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png"&gt;&lt;/a&gt;
</copyright><lastBuildDate>Sat, 30 May 2026 23:08:48 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Gestión térmica en notebook clamshell con RAPL y tuned</title><link>https://sergiobelkin.com/posts/gestion-termica-clamshell-rapl-tuned/</link><dc:creator>sebelk</dc:creator><description>&lt;p&gt;Tapa cerrada, monitor portátil apoyado encima, notebook como CPU box debajo de todo: lo que se llama modo &lt;em&gt;clamshell&lt;/em&gt; —la notebook cerrada como una almeja, usada solo como unidad de cómputo con monitor externo—. El objetivo: evitar que suba la temperatura dañando el hardware.&lt;/p&gt;
&lt;h3 id="el-problema-brevemente"&gt;El problema, brevemente&lt;/h3&gt;
&lt;p&gt;Los notebooks toman aire por abajo y/o expulsan calor por arriba del teclado. Un monitor portátil apoyado en clamshell rompe ese flujo. Como no se puede recuperar la disipación sin tocar hardware, lo que queda es &lt;strong&gt;bajar voluntariamente el techo de potencia&lt;/strong&gt; para que el CPU jamás llegue a temperaturas de throttling.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mejor 12W tibios sostenidos que un loop entre 28W ardiendo y 5W throttleado.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Eso es todo. El resto del post es la pelea para implementarlo en Fedora 43 (el setup no cambió en Fedora 44).&lt;/p&gt;
&lt;h3 id="las-tres-palancas"&gt;Las tres palancas&lt;/h3&gt;
&lt;p&gt;Linux expone tres puntos donde tocar, y cada uno opera en un plano distinto del sistema:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RAPL&lt;/strong&gt; (&lt;em&gt;Running Average Power Limit&lt;/em&gt;). El CPU Intel se autoimpone límites de potencia: PL1 sostenido, PL2 boost. Están en &lt;code&gt;/sys/class/powercap/intel-rapl/&lt;/code&gt;. Si los bajás, el CPU físicamente no puede consumir más. Es el techo duro, aplicado por el hardware del procesador (el PCU, &lt;em&gt;Power Control Unit&lt;/em&gt;): no depende ni del scheduler ni del usuario.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;tuned&lt;/strong&gt;. Daemon de gestión de perfiles de energía. Desde Fedora 41 es el default del sistema (vía &lt;code&gt;tuned-ppd&lt;/code&gt;), reemplazando a &lt;code&gt;power-profiles-daemon&lt;/code&gt;. Maneja governor, EPP, runtime PM, sysctls, sysfs. &lt;em&gt;No baja literalmente el consumo: configura los subsistemas del kernel para que ellos lo hagan&lt;/em&gt;. Opera en el plano de las políticas, no del hardware — por eso no se solapa con RAPL.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;thermald&lt;/strong&gt;. Reactivo: lee zonas térmicas y aplica medidas correctivas cuando se calienta. En este equipo nunca funcionó, y de eso también va el post.&lt;/p&gt;
&lt;h3 id="rapl-primer-round"&gt;RAPL, primer round&lt;/h3&gt;
&lt;p&gt;Bajar PL1 a 12W y PL2 a 20W es un &lt;code&gt;echo&lt;/code&gt; a un archivo en &lt;code&gt;/sys/&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12000000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;tee&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;powercap&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;intel&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rapl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;constraint_0_power_limit_uw&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Tres segundos, listo. Después de verificar que el valor quedó aplicado y que las temperaturas en stress test caían a una zona razonable, vino la primera trampa con la que choco cada tanto y siempre me tomo unos minutos para acordarme:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;/sys&lt;/code&gt; no persiste. Lo que escribís ahí desaparece al reboot.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Solución: un service unit propio (&lt;code&gt;rapl-limits.service&lt;/code&gt;) que reaplica los valores al boot. Heredoc, &lt;code&gt;daemon-reload&lt;/code&gt;, &lt;code&gt;enable --now&lt;/code&gt;. Hecho.&lt;/p&gt;
&lt;h3 id="thermald-el-daemon-zombi"&gt;thermald, el daemon zombi&lt;/h3&gt;
&lt;p&gt;Acá viene la parte que me costó descubrir.&lt;/p&gt;
&lt;p&gt;Habilito &lt;code&gt;thermald&lt;/code&gt;, escribo un &lt;code&gt;/etc/thermald/thermal-conf.xml&lt;/code&gt; custom, lo arranco. &lt;code&gt;systemctl status&lt;/code&gt; lo reporta activo. &lt;code&gt;ps -ef | grep thermald&lt;/code&gt; muestra el proceso vivo. Todo bien.&lt;/p&gt;
&lt;p&gt;Solo que no estaba haciendo absolutamente nada.&lt;/p&gt;
&lt;p&gt;Días después, ya con el sistema funcionando bien y con thermald deshabilitado por sospechoso, abro &lt;code&gt;journalctl -u thermald&lt;/code&gt; para confirmar la sospecha. Encuentro esto, mezclado entre los logs:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nx"&gt;thermald&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1010&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Thermal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;coretemp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sysfs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;found&lt;/span&gt;
&lt;span class="nx"&gt;thermald&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1010&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Thermal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;DTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hwmon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Zones&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;present&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Need&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;configure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;manually&lt;/span&gt;
&lt;span class="nx"&gt;thermald&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1010&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;XML&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;invalid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sensor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x86_pkg_temp&lt;/span&gt;
&lt;span class="nx"&gt;thermald&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1010&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Zone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;unable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;bind&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Una sola causa raíz, dos síntomas en el log:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;No había un sensor térmico del CPU que descubrir.&lt;/strong&gt; El módulo que expone la temperatura de paquete no estaba cargado (&lt;code&gt;No coretemp sysfs found&lt;/code&gt;), así que thermald arrancó sin ninguna zona del CPU a la vista. Lo venía diciendo desde el primer boot del histórico, mucho antes de que yo empezara a tocar nada.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;El &lt;code&gt;invalid sensor type x86_pkg_temp&lt;/code&gt; no significa lo que parece.&lt;/strong&gt; &lt;code&gt;x86_pkg_temp&lt;/code&gt; es el nombre correcto del sensor de paquete; thermald no lo rechaza por schema. Resuelve el tipo en runtime contra los sensores que efectivamente descubrió, y como no había ninguno, la búsqueda volvió vacía y el bind falló. El mensaje dice "invalid type" pero quiere decir "no encontré un sensor de ese tipo en este sistema".&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Resultado: el daemon arrancaba, intentaba bindear las zonas definidas en el XML, no encontraba el sensor, y se quedaba corriendo sin nada que monitorear. Vivo, ocupando memoria, sin función.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;systemctl status&lt;/code&gt; te dice si systemd cree que el servicio está bien. No te dice si el servicio está haciendo lo que tiene que hacer. Para eso está &lt;code&gt;journalctl&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="validacion-empirica"&gt;Validación empírica&lt;/h3&gt;
&lt;p&gt;Antes de tomar la decisión final de desactivar thermald, corrí un baseline de 6 minutos midiendo Package temp y frecuencia, después un stress test de 6 minutos exactos midiendo lo mismo, y los comparé. Lo que vi fue lo que esperaba: con RAPL fijo en 12W y &lt;code&gt;tuned&lt;/code&gt; manejando el resto en su profile balanced, las temperaturas se mantenían en una zona estable y la frecuencia se sostenía sin las caídas abruptas del clamshell sin tunear.&lt;/p&gt;
&lt;p&gt;La pregunta que la medición respondía no era &lt;em&gt;"¿thermald aporta?"&lt;/em&gt; — eso ya estaba contestado de antemano por los logs. Era la otra: &lt;em&gt;"¿con solo RAPL+tuned alcanza para clamshell?"&lt;/em&gt;. La respuesta fue sí, y eso permitió cerrar el setup con tranquilidad.&lt;/p&gt;
&lt;h3 id="estado-final"&gt;Estado final&lt;/h3&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;tuned                 → active (profile balanced)
RAPL PL1=12W PL2=20W  → fijo vía rapl-limits.service
thermald              → disabled (no podía operar en este hardware)
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Filosofía: &lt;strong&gt;un techo duro de potencia (RAPL), un orquestador de estado (tuned), nada reactivo encima&lt;/strong&gt;. Tres piezas, ninguna pelea con la otra, todas debuggeables.&lt;/p&gt;
&lt;h3 id="una-nota-sobre-como-empece"&gt;Una nota sobre cómo empecé&lt;/h3&gt;
&lt;p&gt;Originalmente armé el setup con &lt;code&gt;tlp&lt;/code&gt; + RAPL, siguiendo una sugerencia de Claude (vía claude.ai) que asumía &lt;code&gt;power-profiles-daemon&lt;/code&gt; como el daemon por defecto en Fedora. No contrasté la premisa. Cuando fui a verificarla, descubrí que desde Fedora 41 el default pasó a &lt;code&gt;tuned&lt;/code&gt; (vía &lt;code&gt;tuned-ppd&lt;/code&gt;), no PPD. Y para este escenario — clamshell estático, siempre AC, sin diferenciar AC/BAT, sin que &lt;code&gt;tlp-rdw&lt;/code&gt; aporte nada — &lt;code&gt;tlp&lt;/code&gt; no resolvía nada que &lt;code&gt;tuned&lt;/code&gt; no resuelva. Migré. El post documenta el setup resultante, no el camino original.&lt;/p&gt;
&lt;p&gt;La lección no es "no preguntarle a una IA". Es &lt;strong&gt;verificar la premisa que la IA está usando, no solo la recomendación que te entrega&lt;/strong&gt;. Un asistente puede recomendar una herramienta correctamente &lt;em&gt;condicional a&lt;/em&gt; un default que ya no es el actual; sin contrastar esa premisa, terminás con un setup más complicado del necesario.&lt;/p&gt;
&lt;h3 id="lo-que-me-llevo"&gt;Lo que me llevo&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Un daemon puede estar "running" y no estar haciendo nada. La verdad está en los logs de aplicación, no en &lt;code&gt;systemctl status&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Un unit file propio de pocas líneas a veces es la decisión correcta y que no requiere demasiado esfuerzo.&lt;/li&gt;
&lt;li&gt;El consejo técnico de una IA puede ser internamente coherente y aún así basarse en premisas obsoletas (defaults, versiones, comandos). El costo de chequear la premisa antes de implementar es bajo; el costo de no hacerlo se paga cada vez que volvés a tocar el equipo.&lt;/li&gt;
&lt;/ul&gt;</description><category>monitoreo</category><category>power-management</category><category>sysadmin</category><guid>https://sergiobelkin.com/posts/gestion-termica-clamshell-rapl-tuned/</guid><pubDate>Sat, 30 May 2026 22:56:08 GMT</pubDate></item></channel></rss>