00 cpuidle nedir — C-states ve idle driver
CPU boşta kaldığında — çalışacak görev yokken — enerji tüketmeye devam eder. cpuidle subsystem, boşta geçen süreyi güce çevirme fırsatı olarak görür ve CPU'yu farklı "uyku derinliği" kademelerine sokar.
Bu kademelere C-state (veya idle state) denir. C0 tamamen aktif, C1'den itibaren giderek daha derin uyku. Her uyku derinliği daha az güç tüketir ama uyanma için daha uzun süre harcar.
clock gating vs power gating
idle driver türleri
# aktif cpuidle driver
cat /sys/devices/system/cpu/cpuidle/current_driver
# arm_idle (veya psci, acpi_idle, intel_idle)
# mevcut governor
cat /sys/devices/system/cpu/cpuidle/current_governor
# menu
# kullanılabilir governor'lar
cat /sys/devices/system/cpu/cpuidle/available_governors
# ladder menu teo
01 C-state anatomisi — latency, residency, tradeoff
Her C-state dört temel parametreyle tanımlanır. Governor, bu parametrelerle beklenen boş kalma süresini karşılaştırarak en uygun uyku derinliğini seçer.
Örnek: Cortex-A53 C-state tablosu
State Name Exit Lat Residency Power
C0 WFI (wait) 1 µs 2 µs ~80% aktif
C1 CPU idle 10 µs 20 µs ~30% aktif
C2 Cluster idle 200 µs 400 µs ~5% aktif
C3 SoC sleep 1000 µs 2000 µs ~1% aktif
Tradeoff: derin uyku her zaman daha iyi değil
Eğer CPU 50 µs sonra yeniden uyanacaksa, 200 µs exit latency'li C2'ye girmenin anlamı yoktur. Governor bu kararı beklenen idle süresini tahmin ederek verir. Yanlış tahmin performansı düşürür.
# Senaryo: 300 µs boş kalma beklentisi
# C1: exit_lat=10µs, power_saving=50µW × 300µs = 15nJ tasarruf
# C2: exit_lat=200µs, power_saving=75µW × (300-200)µs = 7.5nJ tasarruf
# Sonuç: C1 daha kârlı, C2'ye girme!
# Senaryo: 5000 µs boş kalma beklentisi
# C2: 75µW × (5000-200)µs = 360nJ tasarruf — C2 mantıklı
02 idle governor — menu, ladder, TEO
idle governor, CPU'nun boşta kalma süresini tahmin eder ve en uygun C-state'i seçer. Üç ana governor mevcuttur.
menu governor (varsayılan)
Linux'un varsayılan idle governor'ıdır. Tahmin için iki girdi kullanır: (1) bir sonraki zamanlayıcı olayına kalan süre, (2) geçmişteki boşta kalma sürelerinin istatistiksel ortalaması. Gerçek zamanlı olmayan sistemlerin büyük çoğunluğu için en iyi seçenektir.
echo menu > /sys/devices/system/cpu/cpuidle/current_governor
ladder governor
ACPI sistemler için tasarlanmış basit bir governor. Her idle geçişinde bir sonraki derinlik kademesine iner (ladder = merdiven). Çok kısa idle sürelerinde aşırı derin uyku girme riski vardır; tickful HZ=100/250 sistemlerde kullanılır.
TEO — Timer Events Oriented governor
Linux 5.1'de eklendi. Sadece timer'a değil, interrupt gelme örüntüsüne de bakar. NO_HZ_IDLE (tickless) sistemlerde timer dışı wakeup'ları daha iyi modelleyerek menu'den daha isabetli kararlar verebilir.
# TEO governor'a geç
echo teo > /sys/devices/system/cpu/cpuidle/current_governor
# governor değişikliğini doğrula
cat /sys/devices/system/cpu/cpuidle/current_governor
# teo
Governor seçim rehberi
Genel amaçlı (HZ=250/CONFIG_HZ_250) → menu
Tickless (CONFIG_NO_HZ_IDLE) → teo (dene, karşılaştır)
ACPI tabanlı eski x86 → ladder (kernel default)
Gerçek zamanlı (PREEMPT_RT) → menu + pm_qos kısıtı
03 PSCI — Power State Coordination Interface
PSCI (ARM Specification IHI0054), birden fazla CPU'lu ARM sistemlerde uyku/uyanma işlemlerini güvenli biçimde koordine eden bir firmware arayüzüdür. Linux cpuidle bu arayüzü kullanarak ARM Trusted Firmware veya U-Boot SPL ile iletişim kurar.
PSCI sürümleri ve fonksiyonlar
DT PSCI node tanımı
/ {
psci {
compatible = "arm,psci-1.0";
method = "smc"; /* Secure Monitor Call; bazı SoC'larda "hvc" */
};
cpus {
cpu@0 {
compatible = "arm,cortex-a53";
enable-method = "psci"; /* PSCI kullan */
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
};
idle-states {
entry-method = "arm,psci";
CPU_SLEEP_0: cpu-sleep-0 {
compatible = "arm,idle-state";
arm,psci-suspend-param = <0x0010000>; /* power state encoding */
entry-latency-us = <40>;
exit-latency-us = <100>;
min-residency-us = <150>;
};
CLUSTER_SLEEP_0: cluster-sleep-0 {
compatible = "arm,idle-state";
arm,psci-suspend-param = <0x1010000>;
entry-latency-us = <500>;
exit-latency-us = <1000>;
min-residency-us = <2500>;
local-timer-stop; /* cluster sleep sırasında timer durur */
};
};
};
# PSCI versiyon ve özellikleri
cat /sys/kernel/debug/psci
# PSCI version: 1.1
# SYSTEM_SUSPEND: supported
# ya da dmesg'de
dmesg | grep -i psci
# [ 0.000000] psci: probing for conduit method from DT.
# [ 0.000000] psci: PSCIv1.1 detected in firmware.
04 sysfs arayüzü — state kontrolü
/sys/devices/system/cpu/cpu0/cpuidle/ altında her C-state için bir dizin bulunur. Bu arayüzden state istatistikleri izlenebilir ve bireysel state'ler devre dışı bırakılabilir.
# CPU0 idle state'leri listele
ls /sys/devices/system/cpu/cpu0/cpuidle/
# state0 state1 state2
# state1 bilgilerini oku
cat /sys/devices/system/cpu/cpu0/cpuidle/state1/name
# cpu-sleep-0
cat /sys/devices/system/cpu/cpu0/cpuidle/state1/latency
# 100 (µs)
cat /sys/devices/system/cpu/cpu0/cpuidle/state1/residency
# 150 (µs)
cat /sys/devices/system/cpu/cpu0/cpuidle/state1/usage
# 42831 (kaç kez girildi)
cat /sys/devices/system/cpu/cpu0/cpuidle/state1/time
# 1847392 (toplam geçirilen süre, µs)
# state1'i devre dışı bırak
echo 1 > /sys/devices/system/cpu/cpu0/cpuidle/state1/disable
# tüm CPU'larda state2'yi kapat (yüksek gecikme durumunu engelle)
for cpu in /sys/devices/system/cpu/cpu*/cpuidle/state2/disable; do
echo 1 > "$cpu"
done
# tüm state'leri görüntüle (tüm CPU)
for state in /sys/devices/system/cpu/cpu0/cpuidle/state*/; do
name=$(cat "${state}name")
lat=$(cat "${state}latency")
usage=$(cat "${state}usage")
echo " $name latency=${lat}µs usage=${usage}"
done
Derin C-state'leri (cluster sleep) devre dışı bırakmak güç tüketimini artırır ama wakeup latency'yi garanti altına alır. Gerçek zamanlı görevlerin bulunduğu sistemlerde pm_qos ile daha kontrollü bir yaklaşım tercih edilmelidir.
05 Ölçüm araçları — cpupower, powertop, perf
C-state kullanım oranlarını ve etkinliğini ölçmek için birden fazla araç mevcuttur. Her biri farklı bir perspektif sunar.
cpupower idle-info
cpupower idle-info
# CPUIdle driver: arm_idle
# CPUIdle governor: menu
# Number of idle states: 3
#
# Idle state name : WFI
# Flags/Description : ARM WFI
# Exit latency : 1 us
# Power usage : 1 mW
# Residency : 1 us
# Usage : 2847391
# Duration : 8291827 us
#
# Idle state name : cpu-sleep-0
# Exit latency : 100 us
# Usage : 47821
# Duration : 9182736 us
powertop — C-state dağılımı
# interaktif arayüz (Tab ile sekmeler arası geç)
powertop
# 30 saniye CSV raporu
powertop --csv=idle_report.csv --time=30
# HTML raporu
powertop --html=idle_report.html --time=30
perf ile C-state geçişleri
# cpu-cycles sayacı (idle'da 0 olması gerekir)
perf stat -e cpu/cpu-cycles/,cpu/instructions/ sleep 5
# idle tracepoint'leri izle
perf trace -e cpu_idle sleep 5
# cpu_idle: state=1 cpu_id=0
# cpu_idle: state=4294967295 cpu_id=0 (wakeup: state=-1)
# C-state istatistiklerini sysfs'ten hızlı oku
for cpu in 0 1 2 3; do
total=0
for st in /sys/devices/system/cpu/cpu${cpu}/cpuidle/state*/time; do
t=$(cat "$st")
total=$((total + t))
done
echo "CPU$cpu toplam idle süre: ${total} µs"
done
06 Latency kısıtları — pm_qos ve real-time
Bazı uygulamalar (gerçek zamanlı kontrol döngüleri, ağ paket işleme) öngörülemeyen wakeup gecikmelerine tahammül edemez. pm_qos (Power Management Quality of Service) bu uygulamaların maksimum kabul edilebilir gecikmeyi sisteme bildirmesine olanak tanır.
cpu_latency_qos_request API
#include <linux/pm_qos.h>
/* Driver içinde */
static struct pm_qos_request latency_req;
/* Başlatma: maksimum 100 µs wakeup latency isteği */
cpu_latency_qos_add_request(&latency_req, 100); /* µs */
/* Kısıtı güncelle */
cpu_latency_qos_update_request(&latency_req, 50);
/* Kısıtı kaldır */
cpu_latency_qos_remove_request(&latency_req);
Userspace'den pm_qos
# /dev/cpu_dma_latency'e istenen max latency'yi yaz (µs, binary int32)
# Dosya açık olduğu sürece kısıt aktif kalır
python3 -c "
import struct, time
# 100 µs max latency iste
fd = open('/dev/cpu_dma_latency', 'wb', buffering=0)
fd.write(struct.pack('i', 100))
print('pm_qos kısıtı aktif (100 µs)')
time.sleep(30) # 30 saniye boyunca derin C-state'ler engellenir
fd.close()
print('kısıt kaldırıldı')
"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
int main(void) {
int fd = open("/dev/cpu_dma_latency", O_RDWR);
if (fd < 0) { perror("open"); return 1; }
int32_t target_us = 50; /* 50 µs max wakeup latency */
write(fd, &target_us, sizeof(target_us));
/* ... gerçek zamanlı işlem döngüsü ... */
close(fd); /* kısıt otomatik kaldırılır */
return 0;
}
Latency kısıtlarını izle
# mevcut CPU latency QoS kısıtlarını görüntüle
cat /sys/kernel/debug/pm_qos/cpu_dma_latency
# Default value: 2147483647
# CPU 0 (task: rt_task, pid: 1842): 50
PREEMPT_RT çekirdeği kullanıyorsanız, yüksek öncelikli RT görevlerinizin bulunduğu CPU'larda derin C-state'leri pm_qos ile sınırlamanız önerilir. Aksi takdirde 1 ms'lik wakeup gecikmesi bile zamanlama ihlallerine yol açabilir.
07 Kernel config — platform driver
cpuidle subsystem'ini etkinleştirmek için birkaç temel Kconfig seçeneği gerekir. Platform driver seçimi SoC'a bağlıdır.
CONFIG_CPU_IDLE=y
CONFIG_CPU_IDLE_GOV_LADDER=y
CONFIG_CPU_IDLE_GOV_MENU=y
CONFIG_CPU_IDLE_GOV_TEO=y # Linux 5.1+
CONFIG_CPU_IDLE_GOV_HALTPOLL=y # sanal makine için
# ARM platform-specific
CONFIG_ARM_CPUIDLE=y # generic ARM idle driver
CONFIG_ARM64_CPUIDLE=y # ARM64 için
CONFIG_ARM_PSCI_CPUIDLE=y # PSCI tabanlı idle
# x86 / ACPI
CONFIG_INTEL_IDLE=y # Intel-specific idle driver
CONFIG_ACPI_PROCESSOR=y
# İstatistik ve hata ayıklama
CONFIG_CPU_IDLE_STAT=y
Platform-specific notlar
Raspberry Pi 4 (BCM2711) → arm_idle + PSCI (firmware gerekli)
BeagleBone Black (AM335x) → cpuidle-am335x veya arm_idle
i.MX8 (NXP) → arm_idle + PSCI (ATF gerekli)
RK3568 (Rockchip) → arm_idle + PSCI
x86 Intel → intel_idle
x86 AMD → acpi_idle veya amd_idle
ARM64 sistemlerde derin C-state'ler (cluster sleep) için ARM Trusted Firmware'in (ATF/TF-A) doğru şekilde yapılandırılmış ve çalışır halde olması gerekir. ATF yoksa veya yanlış derlenirse PSCI çağrıları başarısız olur ve kernel sadece WFI düzeyinde idle yapar.
08 Pratik: idle latency ölçümü ve Cortex-A53 C-state profili
İki pratik: cyclictest ile C-state'lerin wakeup latency'ye etkisini ölçmek, ve ARM Cortex-A53 sistemde C-state profilini çıkarmak.
1 — cyclictest ile idle latency ölçümü
# rt-tests paketi: apt install rt-tests
# Baseline: tüm C-state'ler etkin, menu governor
cyclictest --mlockall --smp --priority=99 \
--interval=1000 --duration=30s \
--histogram=50 --histfile=hist_cstates_on.txt
# # Min Latencies: 00014 00015 00016 00014
# # Avg Latencies: 00024 00026 00022 00025
# # Max Latencies: 00847 01023 00912 00788
# C-state'leri kapat (performans baseline)
for st in /sys/devices/system/cpu/cpu*/cpuidle/state*/disable; do
echo 1 > "$st"
done
cyclictest --mlockall --smp --priority=99 \
--interval=1000 --duration=30s \
--histogram=50 --histfile=hist_cstates_off.txt
# # Min Latencies: 00012 00012 00013 00012
# # Avg Latencies: 00015 00016 00014 00015
# # Max Latencies: 00045 00052 00038 00041
# Sonuç: C-state'ler kapalıyken max latency ~20x daha düşük!
# Sadece derin state'i kapat (C2/cluster sleep)
for cpu in /sys/devices/system/cpu/cpu*/cpuidle/state2/disable; do
echo 1 > "$cpu"
done
# pm_qos ile 100 µs latency kısıtı koy (C-state'ler açık ama derin girişler engellenir)
echo 100 | python3 -c "import sys,struct; open('/dev/cpu_dma_latency','wb').write(struct.pack('i',int(sys.stdin.read().strip())))" &
2 — Cortex-A53 C-state profil scripti
#!/bin/bash
# Cortex-A53 C-state profil çıkarma
# Kullanım: ./cstate_profile.sh [saniye]
DURATION=${1:-30}
echo "=== cpuidle C-state Profil ($DURATION saniye) ==="
echo ""
# Başlangıç değerlerini kaydet
declare -A start_usage start_time
for cpu in 0 1 2 3; do
for state_dir in /sys/devices/system/cpu/cpu${cpu}/cpuidle/state*/; do
key="${cpu}_$(basename $state_dir)"
start_usage[$key]=$(cat "${state_dir}usage" 2>/dev/null || echo 0)
start_time[$key]=$(cat "${state_dir}time" 2>/dev/null || echo 0)
done
done
sleep "$DURATION"
# Delta hesapla ve raporla
echo "CPU | State | Giriş/sn | Toplam%"
echo "----|--------------|-----------|--------"
for cpu in 0 1 2 3; do
for state_dir in /sys/devices/system/cpu/cpu${cpu}/cpuidle/state*/; do
name=$(cat "${state_dir}name" 2>/dev/null)
key="${cpu}_$(basename $state_dir)"
end_usage=$(cat "${state_dir}usage" 2>/dev/null || echo 0)
end_time=$(cat "${state_dir}time" 2>/dev/null || echo 0)
delta_usage=$(( end_usage - ${start_usage[$key]} ))
delta_time=$(( end_time - ${start_time[$key]} ))
per_sec=$(( delta_usage / DURATION ))
pct=$(( delta_time / (DURATION * 10000) ))
printf " %d | %-12s | %9d | %5d%%\n" \
"$cpu" "$name" "$per_sec" "$pct"
done
done
=== cpuidle C-state Profil (30 saniye) ===
CPU | State | Giriş/sn | Toplam%
----|--------------|-----------|--------
0 | WFI | 1203 | 12%
0 | cpu-sleep-0 | 847 | 71%
0 | cluster-slp | 23 | 14%
1 | WFI | 998 | 9%
1 | cpu-sleep-0 | 901 | 78%
1 | cluster-slp | 19 | 11%
Sağlıklı bir gömülü sistemde boşta CPU, zamanının büyük çoğunluğunu (>80%) orta veya derin C-state'lerde geçirmelidir. WFI (C1) oranı çok yüksekse, daha derin state'lere geçişi engelleyen bir pm_qos kısıtı veya yüksek frekanslı interrupt kaynağı olabilir.