Plan 9とGo言語のブログ

主にPlan 9やGo言語の日々気づいたことを書きます。

systemd-logindにSuspend key pressedと記録されてサスペンドする問題とthermal-conf.xmlの書き方

12インチMacBookLinuxをインストールして使っていたが、負荷が上がったときにMacBookサスペンドする問題に困っていた。サスペンドが発生した時刻には、systemd-logindのログに

systemd-logind[299]: Suspend key pressed.

のようなイベントが記録されていた。このログは、LinuxカーネルKEY_SLEEPと定義されたキーが押されたときにsystemd-logindbutton_dispatch関数が記録しているものだった。button_dispatchの主な処理を引用する。

static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata)
{
    Button *b = userdata;
    struct input_event ev;
    ssize_t l;

    l = read(b->fd, &ev, sizeof ev);
    switch(ev.code){
    case KEY_SLEEP:
        log_struct(LOG_INFO, "Suspend key pressed.", ...);
        manager_handle_action(b->manager,
            INHIBIT_HANDLE_SUSPEND_KEY,
            b->manager->handle_suspend_key,
            b->manager->suspend_key_ignore_inhibited,
            true);
        break;
    case KEY_SUSPEND:
        log_struct(LOG_INFO, "Hibernate key pressed.", ...);
        ...
    }
}

KEY_SLEEP定数はLinuxのヘッダファイルで定義されたもので、カーネルsystemdではSuspendが意味するものが異なっているところは少し難しいが、とにかくスリープのようなキーが押されたことを意味する。

#define KEY_SLEEP 142 /* SC System Sleep */
#define KEY_SUSPEND 205

上のコードを読む限りでは、button_dispatchはキーイベントをread(2)しているだけなので、少なくともなんらかのハードウェアが原因だろうと思ったが、MacBookには電源ボタンはあるけれど押していないし、なんならリッドクローズドで使っている時は電源ボタンもないキーボードを使っているので、誤って物理キーを押したわけではない。

systemd-logindでサスペンドを無効化してみる

systemd-logindサスペンドキーが押されたときの動作を/etc/systemd/logind.confで変更できる。例えば以下のようにignoreを設定するとキーイベントを無視できる。

-#HandleSuspendKey=suspend
+HandleSuspendKey=ignore

これは実際に、systemd-logindmanager_handle_actionで、HANDLE_IGNOREの場合はすぐに関数を抜けているところからもイベントを無視している様子が読み取れる。

 /* If the key handling is turned off, don't do anything */
if (handle == HANDLE_IGNORE) {
    log_debug("Handling of %s (%s) is disabled, taking no action.",
        inhibit_key == 0 ? "idle timeout" : inhibit_what_to_string(inhibit_key),
        is_edge ? "edge" : "level");
    return 0;
}

しかし試したけれど残念ながら、状況は改善されなかった。負荷をかけるとすぐにサスペンドしてしまった。

デスクトップ環境でサスペンドを無効にする

GNOMEKDEなどのデスクトップ環境は独自の電源管理を行っているので、これを無効化してみる。GNOMEの場合は、関連する設定は以下の通り。

$ gsettings get org.gnome.settings-daemon.plugins.power power-button-action
'suspend'
$ gsettings get  org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type
'suspend'
$ gsettings get org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type
'suspend'
$ gsettings get org.gnome.settings-daemon.plugins.power sleep-inactive-battery-timeout
200
$ gsettings get org.gnome.settings-daemon.plugins.power sleep-inactive-ac-timeout
1200

これらの設定を無効にする。

$ gsettings set org.gnome.settings-daemon.plugins.power power-button-action nothing
$ gsettings set  org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type nothing
$ gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type nothing

これも、設定したあとで負荷をかけて様子をみたが、再発してしまったので関係がなかった。

CPU周波数を落とす

12インチMacBookファンレスなので、熱の問題だろうと当たりをつけて対策する。このハードウェアのデフォルトはintel_pstateドライバのpowersaveガバナー(Governor)だった。

$ grep . /sys/devices/system/cpu/cpu?/cpufreq/scaling_driver
/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver:intel_pstate
/sys/devices/system/cpu/cpu1/cpufreq/scaling_driver:intel_pstate
/sys/devices/system/cpu/cpu2/cpufreq/scaling_driver:intel_pstate
/sys/devices/system/cpu/cpu3/cpufreq/scaling_driver:intel_pstate

$ grep . /sys/devices/system/cpu/cpu?/cpufreq/scaling_governor
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor:powersave
/sys/devices/system/cpu/cpu1/cpufreq/scaling_governor:powersave
/sys/devices/system/cpu/cpu2/cpufreq/scaling_governor:powersave
/sys/devices/system/cpu/cpu3/cpufreq/scaling_governor:powersave

Intel Turbo Boostを無効にする

Turbo Boostは使っていないコアの電源を落とす替わりに、特定コアのクロックを上げる仕様らしい。

Turbo Boostはno_turboファイルに1を書き込むことで無効にできる。

$ echo 1 | sudo tee /sys/devices/system/cpu/intel_pstate/no_turbo

TurboBoostを無効にすると、突然サスペンドすることはなくなったが、クロックが最大1.4GHzまで落ちてしまうので普段使いするには少々厳しい性能になってしまった。

cpuの最大クロック数を制限する

cpupowerを使ってクロックを落とす方法。

$ sudo pacman -S cpupower
$ sudo cpupower frequency-set -u '2.2GHz'

これで全てのコアが最大2.2GHzに制限される。ただしこの設定は再起動すると消えるので、永続化したい場合はsystemdで実行する。cpupowerをインストールしていれば、/etc/default/cpupowerに設定ファイルがあるので利用するといい。

-#max_freq="3GHz"
+max_freq="2.2GHz"

その後、systemctlでサービスを有効にする。

$ systemctl enable cpupower.service

クロックを変更して実験した結果、2.2GHzまで落とさなければサスペンドが発生していた。実験したときは5月だけど、夏はもっと下げなければだめかもしれない。もっと下げると、TurboBoost無効時の1.4GHzとそれほど変らず、性能の劣化が気になる。

Thermaldで冷却する

一般的にはインストールして起動するだけで適切な動作をする。Ask Ubuntuの回答によると、このモードはZero Configuration Modeといって、DTS温度測定センサーやIntel P-stateを使って適切な調整をする。

coretempドライバのドキュメントは以下のURLにあった。

だけれども、MacBookの場合はZero Configuration Modeでは冷却されている様子がなかったので、/etc/thermald/thermal-conf.xmlで設定することにした。この方法は、上記Ask Ubuntuの回答によるとUser defined configuration modeにあたる。Thermaldに関係するマニュアルは以下の2つ。

thermal-conf.xml(5)には設定例もあるので、読めばだいたい雰囲気で分かるのだけど、難しかったところを少しまとめる。thermal-conf.xmlは大きく分けて3つの要素で構成される。

  • ThermalSensor
  • CoolingDevice
  • TripPoint

ThermalSensor

センサーは温度を計測する場所のことで、たとえばCPUコアなどがそれに該当する。センサーを扱うためには/sys以下のファイルなどを<ThermalSensor>要素で設定が必要になるが、/sys/class/thermal以下のデバイスはデフォルトで組み込まれているので、特に設定を追加せずとも利用できる。12インチMacBookの場合は以下の通り。

$ grep . /sys/class/thermal/thermal_zone*/type
/sys/class/thermal/thermal_zone0/type:BAT0
/sys/class/thermal/thermal_zone1/type:x86_pkg_temp

BAT0はバッテリーで、x86_pkg_tempはCPUパッケージかな。/sys/class/thermal以下のファイル郡はACPI由来のもので、マニュアルではThermal sysfsと呼ばれている。/sys/class/thermalカーネルドキュメントは以下のURLにある。

Thermal sysfs以外にも、MacBookには/sys/devices/platform/coretemp.0/subsystem/devices/applesmc.768などのセンサーがある。これらThermal sysfs以外のセンサーを参照して温度管理をする場合は、<ThermalSensor>要素を使った設定が必要になる。sensorsコマンドを実行すると、他にどのようなセンサーがあるのか調べられる。

$ sensors
BAT0-acpi-0
Adapter: ACPI interface
in0:           8.58 V  
temp:         +32.8°C  
curr1:         0.00 A  (avg =  +0.00 A)

coretemp-isa-0000
Adapter: ISA adapter
Package id 0:  +44.0°C  (high = +100.0°C, crit = +100.0°C)
Core 0:        +43.0°C  (high = +100.0°C, crit = +100.0°C)
Core 1:        +43.0°C  (high = +100.0°C, crit = +100.0°C)

applesmc-isa-0300
Adapter: ISA adapter
TA0V:         +29.0°C  
TB0T:         +32.5°C  
TB1T:         +32.0°C  
TB2T:         +32.5°C  
TBXT:         +32.5°C  
TC0E:         +43.8°C  
TC0F:         +46.0°C  
TC0P:         +39.5°C  
TC1C:         +43.0°C  
TC2C:         +43.0°C  
TCGC:         +43.0°C  
TCHP:         +37.0°C  
TCSA:         +44.0°C  
TCXC:         +43.5°C  
TH0A:         +37.2°C  
TH0B:        -127.0°C  
TH0C:        -127.0°C  
TH0F:         -47.8°C  
TH0R:         -47.8°C  
TKBV:         +36.8°C  
TM0P:         +38.8°C  
TPCD:         +39.0°C  
TPMV:         +31.8°C  
TW0P:         +37.2°C  
Th0N:         +36.2°C  
Th0R:         +31.8°C  
Ts0P:         +32.2°C  
Ts0S:         +36.0°C  
Ts1P:         +30.5°C  
TsCH:         +47.5°C  
TsFD:         +60.0°C  
TsFS:         +53.0°C  
TsHS:          +2.0°C  
TsTH:          +3.0°C  
TsTP:         +50.0°C  
TsWS:         +49.0°C  

BAT0-virtual-0
Adapter: Virtual device
temp1:        +32.8°C  

nvme-pci-0100
Adapter: PCI adapter
Composite:    +39.9°C  

sensorsコマンドで出力された結果の意味は、以下の記事が参考になると思う。

CoolingDevice

冷却デバイスは温度を下げるための機構またはハードウェアのこと。空冷ファンは当然だけど、CPUのクロックを落とすしくみのような機構もCoolingDeviceに該当する。これもセンサーと同様に、/sys/class/thermal以下のデバイスは何も書かなくても利用できる。12インチMacBookの場合は以下の通り。

$ grep . /sys/class/thermal/cooling_device*/type
/sys/class/thermal/cooling_device0/type:Processor
/sys/class/thermal/cooling_device1/type:Processor
/sys/class/thermal/cooling_device2/type:Processor
/sys/class/thermal/cooling_device3/type:Processor
/sys/class/thermal/cooling_device4/type:intel_powerclamp
/sys/class/thermal/cooling_device5/type:LCD

また、マニュアルによると、以下のデバイスもCoolingDeviceとして使えるらしい。

  • rapl_controller
  • intel_pstate (CPU周波数ドライバ)
  • cpufreq
  • LCD (モニターのバックライトを薄暗くする)

それぞれがどんな動作をするのかは、ArchWikiのCPU 周波数スケーリングが詳しい。

TripPoint

この要素で、センサーと冷却デバイスをまとめて温度管理を行う。例えば以下のような設定になる。

<TripPoint>
    <SensorType>x86_pkg_temp</SensorType>
    <Temperature>75000</Temperature>
    <type>passive</type>
    <ControlType>PARALLEL</ControlType>
    <CoolingDevice>
      <index>1</index>
      <type>rapl_controller</type>
      <influence>50</influence>
      <SamplingPeriod>1</SamplingPeriod>
    </CoolingDevice>
</TripPoint>

<Temperature>は冷却を開始するセンサーの温度を設定する。75000の場合は75℃以上になったら開始する。<type>は以下の3種類。

  • active
  • passive
  • max

activeは、空冷ファンなどコストのかかる(追加の電源やノイズなどが発生する)を使う。passiveならパフォーマンスを落として温度を下げる。maxについてはよくわからない。

<SamplingPeriod>を設定すると、前回の変化から設定した秒が経過するまでは温度の変化を検出しなくなる。未設定または0なら常に検出する。

設定例

現在設定している/etc/thermald/thermal-conf.xmlを貼っておく。だいたい期待どおりに動いているが、長時間の負荷を与えたときはまだ稀にサスペンドする場合があるので、もう少し調整は必要だと思う。

<?xml version="1.0"?>
<ThermalConfiguration>
  <Platform>
    <Name>Macbook 2017</Name>
    <ProductName>*</ProductName>
    <Preference>QUIET</Preference>   <!-- 空冷ファンなどactiveなデバイスを使わず冷却する -->
    <ThermalZones>
      <ThermalZone>
        <Type>x86_pkg_temp</Type>
        <TripPoints>
          <TripPoint>
            <SensorType>x86_pkg_temp</SensorType>
            <Temperature>75000</Temperature>
            <type>passive</type>
            <ControlType>PARALLEL</ControlType>
            <CoolingDevice>
              <index>1</index>
              <type>rapl_controller</type>
              <influence>50</influence>
            </CoolingDevice>
            <CoolingDevice>
              <index>2</index>
              <type>intel_pstate</type>
              <influence>40</influence>
            </CoolingDevice>
            <CoolingDevice>
              <index>3</index>
              <type>intel_powerclamp</type>
              <influence>30</influence>
            </CoolingDevice>
            <CoolingDevice>
              <index>4</index>
              <type>cpufreq</type>
              <influence>20</influence>
            </CoolingDevice>
            <CoolingDevice>
              <index>5</index>
              <type>Processor</type>
              <influence>10</influence>
            </CoolingDevice>
          </TripPoint>
        </TripPoints>
      </ThermalZone>
    </ThermalZones>
  </Platform>
</ThermalConfiguration>

以下のURLにも設定例が書かれているので、参考になる。