Der Linux-Kernel versetzt einzelne Geräte in den Stromsparzustand, wenn er sie gerade nicht benötigt. Dank des Runtime-Powermanagement-Subsystems läuft das gut organisiert ab.
Linux versucht auch ohne Zutun von Distribution und Benutzer den Stromverbrauch zu minimieren. Hinter dem Schlagwort “Leistungsanpassung” steht die Idee, genau so viele Ressourcen zur Verfügung zu stellen, wie die Anwendungen momentan benötigen. Das Betriebssystem taktet Prozessoren herunter (Frequency Scaling) und versorgt sie mit möglichst geringer Spannung (Voltage Scaling).
Selektiv abschalten
Nicht jede Hardwarekomponente unterstützt diese dynamische Anpassung, einige lassen sich aber zumindest aus- und bei Bedarf wieder einschalten. Damit dies koordiniert und zum richtigen Zeitpunkt passiert, stellt der Linux-Kernel neben dem Schlafenlegen des gesamten Rechners (System Sleep, siehe “Kern-Technik” in diesem Linux-Magazin) seit 2009 das Runtime-Powermanagement (Runtime-PM) zur Verfügung (Abbildung 1). Dabei handelt es sich im Wesentlichen um ein Framework, das die Hauptverantwortung für das Powermanagement dem eigentlichen Gerätetreiber übergibt [1].
Das ist durchaus sinnvoll, denn das Schlafenlegen und Aufwecken sowie das damit verbundene Einsparpotenzial sind gerätespezifisch. Im Gerätetreiber findet sich der zugehörige Code erstens bei der Initialisierung, zweitens bei den Zugriffsfunktionen (beispielsweise »driver_read()« und »driver_write()« ) und drittens in Form der Funktionen »runtime_idle()« , »runtime_suspend()« und »runtime_resume()« .
Bei der Initialisierung des Treibers konfiguriert der Entwickler den stromsparmäßigen Grundzustand für das Gerät (»suspended« oder »active« , Abbildung 2). Außerdem kann er noch Timeouts angeben, nach denen das Gerät bei Inaktivität “suspendiert” werden soll.
Die für das Runtime-PM modifizierten Zugriffsfunktionen schließlich setzen den Mechanismus in Gang, der das zugehörige Gerät schlafen legt oder auch aufweckt (Abbildung 3). Dazu haben die Kernelentwickler Zugriffsfunktionen wie »driver_read()« oder »driver_write()« um den Aufruf von Hilfsfunktionen erweitert, die dem Kernel mitteilen, dass das Gerät benötigt oder nicht mehr benötigt wird (siehe Tabelle 1: “Wichtige Hilfsfunktionen des Runtime-PM”).
Technisch betrachtet ist der Mechanismus simpel: »pm_runtime_get()« erhöht den Zähler »usage_count« , der die auf das Gerät zugreifenden Instanzen zählt. Ist der Zähler ungleich null und befindet sich das Gerät im Schlafzustand, triggert die Funktion das Aufwecken. »pm_runtime_put()« dekrementiert den Instanzenzähler wieder und ruft – falls der Zähler null ist und keine Instanz auf das Gerät zugreift – die erwähnte Funktion »runtime_idle()« auf, die das Gerät in den Stromsparmodus versetzt.
Allerdings werden beim Aufruf von »pm_runtime_get()« die Funktionen »runtime_resume()« und »runtime_suspend()« des zugehörigen Treibers nicht direkt abgearbeitet. Vielmehr reiht der PM-Kern Aufträge zum Aufruf der Funktionen in eine Workqueue ein, in der sie ein Kernelthread abarbeitet. Dieser asynchrone Weg weist in den meisten Umgebungen Performancevorteile auf. Daneben gibt es aber auch den synchronen Weg über die Funktionen »pm_runtime_get_sync()« und »pm_runtime_put_sync()« .
Die Funktion »runtime_suspend()« versetzt das Gerät in den Stromsparmodus. Dazu rettet sie den aktuellen Hardwarezustand, aktiviert – falls vom Gerät unterstützt – das Wakeup aus dem Low-Power-State und fährt schließlich die Stromversorgung und/oder Taktgeneratoren herunter beziehungsweise koppelt das Gerät von diesen ab. Typischerweise ruft Linux »runtime_suspend()« nicht direkt auf, sondern durch »runtime_idle()« . Letzteres überprüft erst mal, ob ein Schlafenlegen überhaupt sinnvoll ist und zu einer Energieersparnis führt.
Die Funktion »runtime_resume()« ist für das Wiederherstellen des Hardwarezustands, das Deaktivieren des Wakeup-Signaling und das Hochfahren der Stromquellen und Taktgeneratoren zuständig. Nach dem Abarbeiten dieser Funktion kann das System wieder normal auf das Gerät zugreifen. Den erfolgreichen Zustandswechsel melden die Funktionen über den Rückgabewert 0.
Der Entwickler muss für Wakeup-Signaling übrigens die dazu benötigte Interrupt-Serviceroutine selbst implementieren, die ihrerseits die Funktion »runtime_resume()« triggert.
Erst die Kinder
Die Adressen der Suspend-, Resume- und Idle-Funktionen werden zunächst einer Power-Domain zugeordnet, die wiederum mit dem Geräteobjekt (»struct device« ) verknüpft ist. Power-Domains (in Kernel 3.1 über das Objekt »struct dev_pm_domain« , in früheren Versionen über »struct dev_power_domain« implementiert) repräsentieren die kontrollierbaren Strom- und Taktgeber für eine Gruppe von Geräten oder auch nur für ein einzelnes Gerät. Erst wenn alle Geräte innerhalb einer Power-Domain »idle« sind, fährt Linux die Strom- und Taktgeber herunter.
Das Geräteobjekt (»struct device« ) wird für jedes Gerät im Kernel benötigt und ist Teil des Gerätemodells. Dieses Modell reflektiert den hierarchischen Aufbau der Hardware und ist baumartig strukturiert. Der Kernel stellt sicher, dass er nur dann ein Gerät in den Low-Power-Mode versetzt, wenn alle abhängigen Geräte bereits schlafen. Ansonsten würde beispielsweise der USB-Hub schlafen gelegt, obwohl Skype noch auf die darüber angeschlossene Webcam zugreift.
Zusätzlich offeriert das Gerätemodell eine Schnittstelle zur Information und Konfiguration der Hardware, die auch vom Runtime-PM unterstützt wird (siehe Kasten “Konfiguration des Runtime-Powermanagements”).
Konfiguration des Runtime-Powermanagements
Zur Konfiguration des Runtime-Powermanagements legt der Kernel für jedes Gerät beziehungsweise für jede Geräteklasse, die Runtime-PM unterstützt, im Sys-Dateisystem ein Verzeichnis an: »/sys/devices/virtual/[UCC:x00-fake-italic]Gerätename/[UCC:x00-fake-italic]Gerätename/power/« [4]. Es enthält die jeweiligen Informations- beziehungsweise Konfigurationsdateien.
Die Datei »runtime_status« beispielsweise teilt den aktuellen PM-Zustand – »suspended« oder »active« – mit. Die Datei »control« schaltet das Runtime-Powermanagement für das Gerät an (»auto« ) beziehungsweise aus (»on« ). Die Einträge »runtime_suspended_time« und »runtime_active_time« haben die Entwickler für das Diagnosetool Powertop implementiert. Sie summieren die Zeiten auf, in denen sich das Gerät im Zustand »suspended« beziehungsweise »active« befand. Steht in der Datei »asycnc« der String »enabled« , wird bei einem System-Sleep das Gerät asynchron aufgeweckt oder auch schlafen gelegt. Steht dort »disabled« vollziehen sich die Übergänge zwischen den Zuständen synchron.
Listing 1 zeigt auszugsweise die Implementierung von Runtime-Powermanagement in einem Treiber. Die Methode zum Schlafenlegen, die hier keinen produktiven Code enthält, findet sich in Zeile 3; die Funktion zum Aufwecken in Zeile 9. In Zeile 15 steht die Funktion, die im Fall, dass das Gerät sinnvoll schlafen gelegt werden kann, das Schlafenlegen triggert. Die Initialisierung des Runtime-PM ist in den Zeilen 25, 34 und 54 zu sehen; »fake_driver_read()« in Zeile 42 symbolisiert eine Zugriffsfunktion. Der vollständige Code steht auf dem Server des Linux-Magazins unter [2] zum Download bereit.
Listing 1
Runtime-Powermanagement im Treiber
01 [...]
02 #ifdef CONFIG_PM_RUNTIME /* Powermanagement */
03 static int template_runtime_suspend(struct device *dev )
04 {
05 dev_info(dev,"template_runtime_suspend( %p )\n", dev );
06 return 0;
07 }
08
09 static int template_runtime_resume(struct device *dev)
10 {
11 dev_info(dev,"template_runtime_resume( %p )\n",dev);
12 return 0;
13 }
14
15 static int template_runtime_idle(struct device *dev)
16 {
17 dev_info(dev,"template_runtime_idle( %p )\n",dev);
18 if ( does_not_make_sense_to_suspend )
19 return -EAGAIN;
20 pm_runtime_suspend( dev );
21 return 0;
22 }
23
24 //static struct dev_pm_domain template_pm_ops = { /* Kernel 3.1 */
25 static struct dev_power_domain template_pm_ops = {
26 .ops = {
27 .runtime_resume = template_runtime_resume,
28 .runtime_suspend = template_runtime_suspend,
29 .runtime_idle = template_runtime_idle,
30 },
31 };
32 #endif
33
34 static void fake_driver_probe( struct device *dev )
35 {
36 dev_info(dev,"probe...\n");
37 pm_runtime_set_active( template_dev );
38 pm_runtime_enable( template_dev );
39 pm_runtime_suspend( dev );
40 }
41
42 static void fake_driver_read( struct device *dev )
43 {
44 dev_info(dev,"fake_driver_read...\n");
45 pm_runtime_get_sync( dev );
46 dev_info(dev,"i/o...\n");
47 pm_runtime_put( dev );
48 }
49
50 static int __init template_init( void )
51 {
52 [...]
53 #ifdef CONFIG_PM_RUNTIME /* Powermanagement */
54 template_dev->pwr_domain = &template_pm_ops;
55 //template_dev->pm_domain = &template_pm_ops; /* Kernel 3.1 */
56 #endif
57 [...]
Abbildung 4 zeigt die Ausgaben im Syslog nach dem Laden des aus Listing 1 erzeugten Moduls. Die Probe-Funktion im Treiber versetzt das (virtuelle) Gerät in den Stromsparzustand. Greift eine Applikation auf den Treiber zu, wacht das Gerät durch den Kernel getriggert auf. Dank synchroner Methode kann der Zugriff direkt erfolgen (symbolisiert durch die Ausgabe »do i/o« im Syslog). Nach erfolgtem Zugriff ruft der Kernel die Idle-Funktion auf, die ihrerseits das Schlafenlegen anstößt.
Schwieriges Zusammenspiel
Bezüglich der Basismechanismen ist das Runtime-Powermanagement von Linux durchaus überschaubar. Probleme treten erst im Zusammenspiel mehrerer Subsysteme und Hardwarekomponenten zutage, die unter Umständen benötigt werden, um einzelne Komponenten in den Low-Power-State zu überführen.
Das hat die Problematik um das Active State Power Management (ASPM) für PCI-Express-Geräte gezeigt. Die Kernelentwickler hatten ASPM in Linux 2.6.38 deaktiviert, da es Rechner mit bestimmter Hardware einfrieren ließ. Dadurch liefen die Komponenten in einem Modus, der die maximale Energie verbrauchte. Das betraf unter anderem die Anwender der Ubuntu-Release Natty Narwhal, die einen 10 bis 30 Prozent höheren Energieverbrauch verzeichneten als mit der Vorgängerversion Maverick Meerkat [3].
Nach einer Zwischenlösung mit dem Argument »pcie_aspm=force« für den Kernel hat sich die Situation bereits durch angepasste Treiber in Kernel 3.2 gebessert. Die Überarbeitung des ASPM-Codes von Red-Hat-Entwickler Matthew Garrett wird allerdings erst in Kernel 3.3 einziehen. (mhu)
Infos
- Rafael Wysocki, Alan Stern, “Runtime Power Management Framework für I/O Devices”: https://www.kernel.org/doc/Documentation/power/runtime_pm.txt
- Quellcode zum Artikel: https://www.linux-magazin.de/static/listings/magazin/2012/03/powermanagement/
- Launchpad Bug #760131, “Power consumption raised significantly in Natty”: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/760131
- Rafael Wysocki, “Runtime Power Management Framework”: Vortrag auf der Linuxcon Japan 2011, https://events.linuxfoundation.org/slides/2011/linuxcon-japan/lcj2011_wysocki2.pdf










