Linuxの電源管理:熱管理(thermal)と温度制御の分析(デバイスツリーとASLを活用)

Linuxシステムの電源管理と熱管理に関する詳細な内容は、CSDNブログの「Linux電源管理、消費電力管理、熱管理(CPUFreq、CPUIdle、RPM、thermal、スリープ、 wakeup)」を参照してください。

本分析は、Linuxカーネル5.4.18バージョンのソースコードを基にしています。

1. 熱管理の概要

1.1 ハードウェアの基本知識

CPUなどのチップは動作中に大量の熱を発生させ、ハードウェアの温度を上昇させます。各チップには動作可能な温度範囲が定義されています(例:FT-2000四核プロセッサの動作温度範囲は下図を参照)。温度範囲外になると、チップが正常に動作できなくなります。

「FT-2000/4 シリーズプロセッサデータマニュアル」1. 概要

ハードウェアでの温度制御は以下の2つの方法があります:

  • アクティブクーリング:ファンなどの冷却装置を制御します。
  • パッシブクーリング:チップ自体の動作を調整し、発熱を抑えます。

1.2 温度制御の仕組み

「ACPI仕様書」3.10 熱管理の概念

多くのハードウェア設計では、CPUの温度センサーとファンが埋め込み制御装置(EC)によって直接制御されます。これに伴い、2つの温度管理の方法が存在します:

  1. ECがハードウェアの温度を完全に制御し、Linuxシステムが非介入。
  2. LinuxシステムがECを介してハードウェアの温度を制御。

本稿では、主に第2の方法を分析します。

2. 熱ゾーンデバイス(thermal_zone_device)

2.1 概要

温度を取得できるデバイスは、熱ゾーンデバイスとして抽象化されます。例:Temperature Sensor。

「SoC低消費電力システム設計と実装」 9.1.2 モジュール機能の詳細

2.2 データ構造

2.2.1 struct thermal_zone_device

//include/linux/thermal.h
struct thermal_zone_device {
    int 現在の温度;                 //現在の温度。
    int 前回の温度;            //前回の温度センサー読み取り値
    int 低温トリガー点;               //前回の低温制御トリガー点
    int 高温トリガー点;              //前回の高温制御トリガー点
    struct thermal_zone_device_ops *ops;
    struct thermal_zone_params *tzp;
    struct thermal_governor *governor;
    struct delayed_work poll_queue;
    ......
};

**登録と解除**

struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
        void *, struct thermal_zone_device_ops *,
        struct thermal_zone_params *, int, int);

void thermal_zone_device_unregister(struct thermal_zone_device *);

2.2.2 struct thermal_zone_device_ops

//include/linux/thermal.h
struct thermal_zone_device_ops {
    int (*bind) (struct thermal_zone_device *,
             struct thermal_cooling_device *);
    int (*unbind) (struct thermal_zone_device *,
               struct thermal_cooling_device *);
    int (*get_temp) (struct thermal_zone_device *, int *);
    int (*set_trips) (struct thermal_zone_device *, int, int);
    int (*get_mode) (struct thermal_zone_device *,
             enum thermal_device_mode *);
    int (*set_mode) (struct thermal_zone_device *,
        enum thermal_device_mode);
    int (*get_trip_type) (struct thermal_zone_device *, int,
        enum thermal_trip_type *);
    int (*get_trip_temp) (struct thermal_zone_device *, int, int *);
    int (*set_trip_temp) (struct thermal_zone_device *, int, int);
    int (*get_trip_hyst) (struct thermal_zone_device *, int, int *);
    int (*set_trip_hyst) (struct thermal_zone_device *, int, int);
    int (*get_crit_temp) (struct thermal_zone_device *, int *);
    int (*set_emul_temp) (struct thermal_zone_device *, int);
    int (*get_trend) (struct thermal_zone_device *, int,
              enum thermal_trend *); 
    int (*notify) (struct thermal_zone_device *, int,
               enum thermal_trip_type);
};

2.3 温度状態と温度トリガー点(trip point)

2.3.1 概要

ソフトウェア側には4つの温度トリガー点があります。

  • アクティブ:ファンなどの冷却装置を起動します。
  • パッシブ:チップの動作を調整して発熱を抑えます。
  • ホット:ACPI対応デバイスの場合、S4状態(ディスクへのスリープ)を実行します。
  • クリティカル:システムをシャットダウンします。

温度がトリガー点を超過した場合、具体的な冷却操作は以下の「4. 熱ゾーンデバイスのトリガー点とクーリングデバイスのバインド」で説明します。

2.3.2 データ構造

//include/linux/thermal.h
struct thermal_trip {
    struct device_node *np;
    int 温度;
    int ヒスタリシス;
    enum thermal_trip_type type;
};

enum thermal_trip_type {
    THERMAL_TRIP_ACTIVE = 0,
    THERMAL_TRIP_PASSIVE,
    THERMAL_TRIP_HOT,
    THERMAL_TRIP_CRITICAL,
};

2.3.3 デバイスツリーでのトリガー点の指定

        trips {
            cpu_alert0: cpu-alert0 {
                温度 = <90000>; /* 十分位の攝氏温度 */
                ヒスタリシス = <2000>; /* 十分位の攝氏温度 */
                type = "active";
            };  
            cpu_alert1: cpu-alert1 {
                温度 = <100000>; /* 十分位の攝氏温度 */
                ヒスタリシス = <2000>; /* 十分位の攝氏温度 */
                type = "passive";
            };  
            cpu_crit: cpu-crit {
                温度 = <125000>; /* 十分位の攝氏温度 */
                ヒスタリシス = <2000>; /* 十分位の攝氏温度 */
                type = "critical";
            };  
        };

上記のデバイスツリー情報は以下を示しています:

トリガー点ノードは、温度ドメイン内の温度に応じてシステムがアクションを実行する温度点を説明します。

必須プロパティ:

  • 温度:整数値でトリガー温度を表します。
  • ヒスタリシス:温度プロパティの低域ヒスタリシス値。
  • type:トリガー点の種類。

「Documentation/devicetree/bindings/thermal/thermal.txt」

2.3.4 ASLでのトリガー点の指定

**熱管理オブジェクト**

アクティブクーリングのトリガー点を設定します。

パッシブクーリングのトリガー点を設定します。

クリティカル温度を設定します。

ホット温度を設定します。

「ACPI仕様書」11.4 熱管理オブジェクト

例:

ThermalZone (TZ0) {
    Method(_PSV) { Return(_C2K(70)) }   /* パッシブ冷却温度 */
    Method(_HOT) { Return(_C2K(85)) }   /* ホット温度 */
    Method(_CRT) { Return(_C2K(95)) }   /* クリティカル温度 */
    ......
} /* ThermalZone(TZ0) */

2.3.5 トリガー点の情報確認

/sys/class/thermal/thermal_zoneX/trip_point_0_type
/sys/class/thermal/thermal_zoneX/trip_point_0_temp
/sys/class/thermal/thermal_zoneX/trip_point_0_hyst

例:

# cat /sys/class/thermal/thermal_zone4/trip_point_0_type 
critical
# cat /sys/class/thermal/thermal_zone4/trip_point_0_temp
110050
# cat /sys/class/thermal/thermal_zone4/trip_point_0_hyst 
0

2.4 熱ゾーンデバイスの情報確認

/sys/class/thermal/thermal_zoneX/

3. クーリングデバイス(cooling_device)

3.1 概要

温度を制御するデバイスは、クーリングデバイスとして抽象化されます。

「SoC低消費電力システム設計と実装」 9.1.2 モジュール機能の詳細

3.2.1 struct thermal_cooling_device

//include/linux/thermal.h
struct thermal_cooling_device {
    int id; 
    char type[THERMAL_NAME_LENGTH];
    struct device device;
    struct device_node *np;
    void *devdata;
    void *stats;
    const struct thermal_cooling_device_ops *ops;
    bool updated; /* 更新済みフラグ */
    struct mutex lock; /* 熱インスタンスリストの保護 */
    struct list_head thermal_instances;
    struct list_head node;
};

3.2.2 struct thermal_cooling_device_ops

//include/linux/thermal.h
struct thermal_cooling_device_ops {
    int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
    int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
    int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
    int (*get_requested_power)(struct thermal_cooling_device *,
                   struct thermal_zone_device *, u32 *);
    int (*state2power)(struct thermal_cooling_device *,
               struct thermal_zone_device *, unsigned long, u32 *);
    int (*power2state)(struct thermal_cooling_device *,
               struct thermal_zone_device *, u32, unsigned long *);
};

3.3 クーリング状態(state)

クーリングデバイスは冷却状態(state)を維持します。状態が高いほど、冷却措置が積極的になります。

3.3.2.1 デバイスツリーでのPWMファンの状態指定

//arch/arm/boot/dts/exynos5410-odroidxu.dts
    fan0: pwm-fan {
        compatible = "pwm-fan";
        pwms = <&pwm 0 20972 0>;
        #cooling-cells = <2>;
        cooling-levels = <0 130 170 230>;
    };

cooling-levels:PWM duty cycle値(0~255)を冷却状態に割り当てます。

3.3.2.2 デバイスツリーでのCPUの状態指定

//Documentation/devicetree/bindings/thermal/thermal.txt
cpus {
    /* 
     * DVFS対応CPUの冷却デバイスを説明する例。
     * CPUノードは4つの OPP を説明します。
     */
    cpu0: cpu@0 {
        ...
        operating-points = <                                                
            /* kHz    uV */                                                 
            970000  1200000                                                 
            792000  1100000                                                 
            396000  950000
            198000  850000
        >;
        #cooling-cells = <2>; /* 最小値と最大値 */
    };
    ...     
};

3.3.3.3 ASLでのファンの状態指定

ACPI 1.0では、ファンの冷却状態はオン(D0)とオフ(D3)の2種類です。

ACPI 4.0では、_FPS(ファン性能状態)を用いて複数の状態を指定できます。

3.3.3.4 ASLでのCPUの状態指定

_TSS(スロットルサポートステート)はプラットフォームがサポートするプロセッサースロットル状態の数をOSPに伝えます。

「ACPI仕様書」8.4.5.2 _TSS(スロットルサポートステート)

例:

Scope(\_SB) {
    Device(CPU0) {
        Name(_HID, "ACPI0007")
        Name(_UID, 0)
        ......
        Name(_TSS, Package() {
            Package() {0x64, 1000, 0x0, 0x7, 0x0},
            Package() {0x58, 800, 0x0, 0xF, 0x0},
            Package() {0x4B, 600, 0x0, 0xE, 0x0},
            Package() {0x3F, 400, 0x0, 0xD, 0x0}
        })
        ......
    }
    ......
}

3.4 クーリングデバイスの状態情報確認

/sys/class/thermal/cooling_device0/cur_state  
/sys/class/thermal/cooling_device0/max_state

4. 熱ゾーンデバイスのトリガー点とクーリングデバイスのバインド

4.1 概要

thermal_zone_device_register()関数は、熱ゾーンデバイスを登録します。登録時、bind_tz()関数が呼ばれます。bind_tz()は、登録済みのクーリングデバイスを走査し、現在の熱ゾーンデバイスとクーリングデバイスをバインドします。

「SoC低消費電力システム設計と実装」 9.1.2 モジュール機能の詳細

4.2 データ構造

struct thermal_instance {
    int id;
    char name[THERMAL_NAME_LENGTH];
    struct thermal_zone_device *tz;
    struct thermal_cooling_device *cdev;
    int trip;
    bool initialized;
    unsigned long upper;    /* このトリガー点の最高冷却状態 */
    unsigned long lower;    /* このトリガー点の最低冷却状態 */
    unsigned long target;   /* 目標冷却状態 */
    char attr_name[THERMAL_NAME_LENGTH];
    struct device_attribute attr;
    char weight_attr_name[THERMAL_NAME_LENGTH];
    struct device_attribute weight_attr;
    struct list_head tz_node; /* tz->thermal_instancesのノード */
    struct list_head cdev_node; /* cdev->thermal_instancesのノード */
    unsigned int weight; /* 冷却デバイスの重み */
};

4.3 バインド関数

このインターフェース関数は、熱ゾーンデバイスのトリガー点とクーリングデバイスをバインドします。

「include/linux/thermal.h」

int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, 
                     int trip,
                     struct thermal_cooling_device *cdev,
                     unsigned long upper, unsigned long lower,
                     unsigned int weight);

4.4 熱ゾーンデバイスのトリガー点とクーリングデバイスのバインド関係指定

4.4.1 デバイスツリー

        trips {
            cpu_alert0: cpu-alert0 {            //トリガー点
                温度 = <90000>; /* 十分位の攝氏温度 */
                ヒスタリシス = <2000>; /* 十分位の攝氏温度 */
                type = "active";
            };
        ......
        cooling-maps {
            map0 {
                trip = <&cpu_alert0>;
                cooling-device = <&fan0 THERMAL_NO_LIMIT 4>; 
            };
            ......

4.4.2 ASL

_AL0(アクティブリスト)

_PSL(パッシブリスト)

_CRT(クリティカル温度)

_HOT(ホット温度)

_PSV(パッシブ温度)

「ACPI仕様書」11.4 熱管理オブジェクト

4.5 バインド関係の確認

# ls /sys/class/thermal/thermal_zone7/cdev* -l
lrwxrwxrwx 1 root root    0 5月  11 18:51 /sys/class/thermal/thermal_zone7/cdev0 -> ../cooling_device9
-r--r--r-- 1 root root 4096 5月  11 18:51 /sys/class/thermal/thermal_zone7/cdev0_trip_point
-rw-r--r-- 1 root root 4096 5月  11 18:51 /sys/class/thermal/thermal_zone7/cdev0_weight
lrwxrwxrwx 1 root root    0 5月  11 18:51 /sys/class/thermal/thermal_zone7/cdev1 -> ../cooling_device10
-r--r--r-- 1 root root 4096 5月  11 18:51 /sys/class/thermal/thermal_zone7/cdev1_trip_point
-rw-r--r-- 1 root root 4096 5月  11 18:51 /sys/class/thermal/thermal_zone7/cdev1_weight

上記の情報は、熱ゾーン7の1号トリガー点(60℃)とcooling_device10がバインドされていることを示しています。2号トリガー点(80℃)はcooling_device9とバインドされています。

5. 温度管理ポリシー(governors)

5.1 概要

温度管理のポリシーは、ステップワイズやバンバンなど、複数の方法があります。

「SoC低消費電力システム設計と実装」 9.1.2 モジュール機能の詳細

5.2 現在のgovernors

5.2.1 step_wise

温度がトリガー点を超えると、アクティブクーリングを実行します。

温度がトリガー点を下回ると、パッシブクーリングを実行します。

「drivers/thermal/step_wise.c」

5.2.2 bang_bang

温度がトリガー点を超えるとファンをオンにします。

温度がトリガー点を下回るとファンをオフにします。

「drivers/thermal/gov_bang_bang.c」

5.3 governorの確認と設定

/sys/class/thermal/thermal_zoneX/available_policies

/sys/class/thermal/thermal_zoneX/policy

6. カーネルコードのフロー

6.1 概要

カーネルコードは、温度データをループで読み取り、トリガー点を超えると関連付けられたクーリングデバイスを起動します。

6.2 代码フロー

ループ監視は、作業アイテムを用いて実行されます。

「drivers/thermal/step_wise.c」

thermal_zone_device_register();
    -> INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);

作業アイテム処理関数thermal_zone_device_check()のフロー:

monitor_thermal_zone()関数は、作業アイテムを再登録し、監視を継続します。

「SoC低消費電力システム設計と実装」9.1.6 criticalイベントと非criticalイベントの処理フロー

熱ゾーンデバイスのgovernorが制御を実行します。

「drivers/thermal/step_wise.c」

7. ACPIを用いた温度管理ドライバーの分析

7.1 熱ゾーンデバイス

7.1.1 データ構造

7.1.2 イニシャライゼーションフロー

「drivers/acpi/thermal.c」

7.2.2.1 簡単なファンデバイスの制御

ACPI 1.0仕様のファンデバイスは、D0状態(オン)とD3状態(オフ)の2種類です。

「drivers/acpi/fan.c」

static int fan_set_state(struct acpi_device *device, unsigned long state)
{
    if (state != 0 && state != 1)
        return -EINVAL;

    return acpi_device_set_power(device,
                     state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
}

7.2.2.2 ACPI 4.0仕様のファンデバイス制御

ACPI 4.0では、_FPS(ファン性能状態)を用いて複数の回転数を指定できます。

「ACPI仕様書」11.3.1 ファンオブジェクト

例:

Package {
    Revision,        // 現在のリビジョン:0
    FanPState[0],    // FanPStateパッケージ
    ......
    FanPState[n]     // FanPStateパッケージ
}

7.3.2 CPUのイニシャライゼーションフロー

「drivers/acpi/processor_thermal.c」

8. デバイスツリー(DTS)を用いたハードウェア情報の温度管理ドライバー分析

8.1 熱ゾーンデバイス

8.1.1 データ構造

「drivers/thermal/of-thermal.c」

8.1.2 イニシャライゼーションフロー

「drivers/thermal/of-thermal.c」

8.2 PWMファンのクーリングデバイス

8.2.1 データ構造

「drivers/hwmon/pwm-fan.c」

8.2.2 イニシャライゼーション

「drivers/hwmon/pwm-fan.c」

8.3 CPUのクーリングデバイス

8.3.1 データ構造

「drivers/thermal/cpu_cooling.c」

8.3.2 イニシャライゼーションフロー

「drivers/thermal/cpu_cooling.c」

9. デバッグ

9.1 /sys/kernel/debug/tracing/events/thermal

9.2 /sys/kernel/debug/tracing/events/thermal_power_allocator/

9.3 tmonツール

tmonツールは以下の情報を表示します:

  • 熱ゾーン(温度センサー)の温度
  • 熱ゾーンのトリガー点
  • クーリングデバイスの状態(現在の状態と最大状態)
  • クーリングデバイスと熱ゾーンのバインド関係

ツールソースコード:<kernel_src>/tools/thermal/tmon

タグ: linux 電源管理 熱管理 デバイスツリー ASL

7月12日 18:27 投稿