Open Source im professionellen Einsatz
Linux-Magazin 11/2008

Kernel- und Treiberprogrammierung mit dem Kernel 2.6 – Folge 42 Folge 42

Kern-Technik

,

Kennen und unterstützen alle Treiberprogrammierer das Interface fürs Powermanagement, steht einem funktionierenden Suspend-to-Ram und Suspend-to-Disk nichts mehr im Wege.

1665

Die Zukunft ist grün! Auch die von Linux. Mit jeder neuen Version wird sie noch grüner, schließlich tunen die Entwickler den Kernel mit immer ausgereifteren Stromspartechniken (siehe Kasten "Green Computing"). Ganz aktuell überarbeiten die Kernelhacker das Linux-Powermanagement (PM). Sie hoffen damit Treiberentwickler dafür zu gewinnen, noch mehr PM-Funktionen zu unterstützen. Das Powermanagement kümmert sich um Suspend-to-Ram (STR) und um Suspend-to-Disk (STD). An anderer Stelle nennen Applikationen das auch Schlafenlegen (Sleep) oder Einfrieren des Systems (Hibernate).

Green Computing

Seit Energiepreise und Verkaufszahlen von Netbooks gleichermaßen in die Höhe schießen, nimmt die Öffentlichkeit den Stromverbrauch von IT-Hardware wahr. Statt Prozessorleistung vergleichen Anwender jetzt die Prozessorleistung pro Watt. Statt Tipps zum Übertakten fragen Anwender in den einschlägigen Foren zunehmend danach, wie sie ihren Rechner untertakten können, und erhoffen davon ein probates Mittel zum Stromsparen und damit längere Akkulaufzeit im laufenden Betrieb.

Zwei Ansätze zum Energiesparen

Green Computing verspricht - neben der umweltfreundlichen Produktion und Wiederverwertung der Geräte - den Stromverbrauch von Servern, Desktops, Notebooks und sonstigen prozessorgesteuerten Geräten auf ein Minimum zu senken. Seine Realisierungsvarianten fußen alle auf zwei Grundprinzipien: Erstens stellen die Geräte nur so viel Leistung zur Verfügung, wie tatsächlich benötigt wird. Andernfalls drehen sie den Strom ab. Zweitens verschieben Systeme Aufgaben möglichst auf einen späteren Zeitpunkt, wenn auch andere Komponenten eine Leistung anfordern. Das nennen die Experten Leistungsbündelung.

Sanftes Herunterregeln

Neben dem im Beitrag vorgestellten Powermanagement bietet Linux weitere Stromspartechniken. Es unterstützt etwa Prozessoreigenschaften wie Speedstep oder den konfigurierbaren Loadbalancer des Schedulers. Letzterer versucht alle Threads auf einer Mehrprozessormaschine auf eine möglichst kleine Anzahl von CPU-Kernen zu gruppieren. Ist das System nicht vollständig ausgelastet, lassen sich so einige Prozessorkerne in einen Schlafzustand versetzen. Der Anwender aktiviert diese Funktion durch »echo 1 > /sys/devices/system/cpu/sched_mc_power_savings«.

Das seit gut einem Jahr im Kernel aufgenommene Tickless-Patch [4] leistet auch seinen Beitrag zum Energiesparen: Linux löst nur dann Interrupts aus, wenn es etwas abzuarbeiten gibt. Für den energiebewussten Kernelhacker bringt das Tickless-Patch die Funktionen »round_jiffies()« und »round_jiffies_relative()« mit. Sie verschieben zeitgesteuerte Aktionen auf die nächste volle Sekunde und verhindern häufiges Aufwecken für nur kurze Zeiträume. Der Erfolg dieser und anderer Maßnahmen ist durchaus messbar: Ein Vergleichstest bescheinigt einem Red-Hat-Linux-Server einen um bis zu zwölf Prozent günstigeren Stromverbrauch als einem Server unter Windows 2008 [5].

Suspend-to-Ram und Suspend-to-Disk sind nur zwei der drei möglichen Schlaf-zustände, die die Industrie eingeführt hat, um Strom zu sparen (siehe Abbildung 1 und Tabelle 1). Den dritten Zustand - Standby - kennt Linux auch. Aber weil er kaum Vorteile bietet, setzt Linux ihn typischerweise nicht ein: Das Betriebssystem versetzt dabei seine Komponenten nur in einen leichten Schlummer und benötigt daher etwas mehr Strom als STR. Das wiegt die nur unwesentlich kürzere Zeit, um wieder aufzuwachen, nicht auf, findet Kernelentwickler Pavel Machek und kommentiert dies mit "easy, but useless".

Abbildung 1: Linux stellt einen von drei Schlafzuständen zur Auswahl, um verschieden tief zu schlafen und so Strom zu sparen.

Damit Suspend-to-Ram und Suspend-to-Disk funktionieren, müssen nicht nur die Hardware, sondern vor allem die Treiber die Methoden unterstützen. Daran hapert es aber vielfach. Oft deshalb, weil die Programmierer das zugehörige Interface einerseits nicht kennen und es sich darüber hinaus in der Vergangenheit gelegentlich änderte. Zum Glück erfordern die letzten Änderungen des Powermanagement-Code im Kernel nicht gleich eine Neu-Implementierung aller Treiber; vielmehr passen Entwickler ihre Treiber mit wenigen Zeilen Code an.

Tabelle 1:
S-States

 

Zustand

Beschreibung

S0

Betriebsbereit (Working)

S1

Schlafend, der CPU-Kontext bleibt erhalten

S2

Schlafend, der CPU-Kontext geht verloren

S3

Suspend-to-Ram (STR), diverse Hardware ist abgeschaltet

S4

Suspend-to-Disk (STD), gesichert auf Hintergrundspeicher

S5

Ausgeschaltet

Altes und neues API

Um den Ruhezustand (Suspend) und den Tiefschlaf (Hibernate) zu unterstützen, implementieren Treiberprogrammierer bis einschließlich Kernel 2.6.26 ein einfaches API. Es besteht aus einer Funktion zum Einschlafen (Suspend) und einer zum Aufwachen (Resume). Es findet noch weithin Anwendung, auch wenn es die Linux-Entwickler in Version 2.6.27 aktualisiert haben.

Im alten Modell ruft Linux in jedem Treiber die zwei genannten Methoden auf. Beide erhalten neben einem Zeiger auf das schlafen zu legende oder aufzuweckende Gerät noch einen Parameter vom Typ »pm_message_t«. Er gibt über den anvisierten Zustand Auskunft, zur Auswahl stehen Suspend-to-Ram oder Suspend-to-Disk. Innerhalb der Suspend-Funktion implementieren Entwickler die notwendigen Schritte, um das entsprechende Gerät ordnungsgemäß schlafen zu legen. Typischerweise schalten sie dazu alle DMA-Transfers und I/O-Operationen ab, retten den Gerätezustand und deaktivieren dann das Gerät. Innerhalb der Resume-Funktion aktivieren sie das Gerät wieder, restaurieren den geretteten Zustand und setzen eventuell unterbrochene Zugriffe fort.

Starke Bindungen

Das Powermanagement folgt programmtechnisch dem Gerätemodell. Es bietet drei Möglichkeiten, eigene Funktionen einzubinden: Entweder bindet der Entwickler die Suspend- und Resume-Methoden an das Klassen-Geräteobjekt (»struct class_device«) oder an das Geräteobjekt (»struct device«). Das zugehörige Bussystem, zum Beispiel PCI oder USB, verwaltet es. Wer also ein neues Bussystem in den Kernel integrieren möchte - etwa für Controller Area Networks - bindet die beiden Funktionen direkt an das Busobjekt vom Typ »struct bus_type«.

An welches Objekt der Entwickler die Funktionen bindet, hängt vom Zeitpunkt ab, zu dem Linux die Funktionen aufrufen soll. Tatsächlich durchläuft der Kernel mehrere Zustände, bis er zum Stillstand kommt (siehe Abbildung 2). Sobald der Anwender das System schlafen legt, schreibt Linux zunächst alle im Page-Cache befindlichen, ungesicherten Daten auf den Hintergrundspeicher. Dazu verwendet es den Systemaufruf »sys_sync()«, den auch das Kommando »sync« verwendet. Registrierte Komponenten wie Threads informiert das Betriebssystem per Suspend-Notifier über die bevorstehende Zustandsänderung. Hat es seine Callback-Funktionen abgearbeitet, friert es die Rechenprozesse ein. Aus Diskussionen der Linux-Entwickler lässt sich ablesen, dass das Einfrieren der Rechenprozesse wohl in künftigen Kernelversionen unterbleibt.

Abbildung 2: Bei Suspend oder Hibernate versucht Linux zunächst ganze Geräteklassen, danach die an den Bussen befindlichen Geräte zum Nachtschlaf zu bewegen.

Anschließend versetzt Linux die Geräte in genau umgekehrter Reihenfolge ihrer Registrierung in den Schlafzustand. Dazu ruft es zuerst die Suspend-Methoden auf, die der Treiberentwickler über die Geräteklassen-Objekte dem Kernel bekannt gegeben hat (siehe Listing 1).

Listing 1:
»suspendtest.c« testet den Ruhezustand

01 #include <linux/module.h>
02 #include <linux/fs.h>
03 #include <linux/device.h>
04 #include <linux/pm.h>
05 #include <asm/uaccess.h>
06 
07 static int major = 0;
08 static int suspend_count = 0;
09 
10 int my_suspend(struct device *dev,
11                pm_message_t state) {
12   switch (state.event) {
13   case PM_EVENT_ON:
14     printk("on ...n"); break;
15   case PM_EVENT_FREEZE:
16     printk("freeze ...n"); break;
17   case PM_EVENT_SUSPEND:
18     printk("suspend ...n"); break;
19   case PM_EVENT_HIBERNATE:
20     printk("hibernate ...n"); break;
21   default:
22     printk("pm_event: 0x%xn",
23            state.event); break;
24   }
25   printk("my_suspend(%p, %d)n",
26          dev, ++suspend_count);
27   return 0;
28 }
29 
30 int my_resume(struct device *dev) {
31   printk("my_resume(%p, %d)n", dev,
32          ++suspend_count );
33   return 0;
34 }
35 
36 static struct file_operations fops = {
37   .owner = THIS_MODULE,
38 };
39 
40 static struct class *test_class;
41 
42 static int __init my_module_init(void) {
43   printk("my_module_initn");
44   if ((major = register_chrdev(0,
45                 "testdev", &fops))) {
46     test_class = class_create(THIS_MODULE,
46                   "testclass");
47     if (IS_ERR(test_class)) {
48       printk("no udev supportn");
49       unregister_chrdev(major, "testdev");
50       return -EIO;
51     }
52     test_class->suspend = my_suspend;
53     test_class->resume  = my_resume;
54     device_create(test_class, NULL,
55                   MKDEV(major, 0), "lulu");
56     return 0;
57   }
58 
59   printk("loading failedn");
60   return -EIO;
61 }
62 
63 static void __exit my_module_exit(void) {
64   unregister_chrdev(major, "testdev");
65   if (!IS_ERR(test_class)) {
66     device_destroy(test_class,
67                    MKDEV(major, 0));
68     class_destroy(test_class);
69   }
70   printk("my_module_exit; "
71          "%d times suspendedn",
72          suspend_count);
73 }
74 
75 module_init(my_module_init);
76 module_exit(my_module_exit);
77 MODULE_LICENSE("GPL");

Im zweiten Schritt bearbeitet Linux die Funktionen der Geräteobjekte (siehe Listing 2) und drittens die des Bussystems. Das Powermanagement schaltet nun alle Prozessoren einer Mehrkernmaschine außer dem ersten ab, sperrt Interrupts und ruft die an die Geräteobjekte gekoppelten »suspend_late()«-Methoden auf. Nachdem die Systemgeräte abgeschaltet sind, schläft Linux. Erwartungsgemäß weckt Linux das System in umgekehrter Reihenfolge wieder auf.

Listing 2:
»pcitest.c« testet Suspend und Resume mit
PCI-Bus

01 ...
02 static int suspend_count = 0;
03 static int major = 0;
04 ...
05 static struct pci_device_id pci_drv_tbl[] = {
06   {MY_VENDOR_ID, MY_DEVICE_ID,
07      PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
08   {0, }
09 };
10 
11 static struct pci_driver pci_drv = {
12     name:     "pci_drv",
13     id_table: pci_drv_tbl,
14     probe:    pci_init_one,
15     remove:   pci_remove_one,
16     suspend:  my_suspend,
17     resume:   my_resume,
18 };
19 
20 static int my_suspend(struct pci_dev *dev,
21                       pm_message_t state) {
22   printk("my_suspend(%p, %d)n", dev,
23          ++suspend_count);
24   return 0;
25 }
26 
27 static int my_resume(struct pci_dev *dev) {
28   printk("my_resume(%p, %d)n", dev,
29          ++suspend_count);
30   return 0;
31 }
32 ...
33 static int __init pci_drv_init(void) {
34   int ret;
35 
36   if ((major = register_chrdev(0,
37         "driver-name", &driver_fops)) != 0)
38   {
39     if ((ret = 
40        pci_register_driver(&pci_drv)) < 0 ) {
41       printk(KERN_ERR "can't register "
42              "PCI driver (%d)n", ret);
43       unregister_chrdev(major,"driver-name");
44       return -EIO;
45     }
46     return 0;
47   }
48   return -EIO;
49 }
50 
51 static void __exit pci_drv_exit(void) {
52     pci_unregister_driver(&pci_drv);
53     unregister_chrdev(240, "driver-name");
54     printk("pci_drv_exit(%d)n",
55             suspend_count);
56 }
57 ...

Listing 1 zeigt ein vollständiges Beispiel, das Powermanagement-Funktionen über das Klassenobjekt im Kernel 2.6.26 einbindet. Bevor Experimentierfreudige den Code allerdings übersetzen und den Treiber per »insmod suspendtest.ko« laden, sollten sie sicherstellen, dass ihr System den Ruhezustand unterstützt. Bei den meisten Systemen drücken sie dafür kurz den Ein-Aus-Schalter und schicken es so in den Ruhezustand STR. Danach wecken sie es wieder auf.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 5 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Kern-Technik

    Wer schläft, spart. Daher organisiert der Linux-Kernel das kollektive Schlafen der Rechnerkomponenten, bis das komplette System ruht. Er strukturiert Abhängigkeiten baumartig und verwendet ein Phasenmodell. Damit sind bei Bedarf alle Komponenten schnell wieder hellwach.

  • Kern-Technik

    Vorgefertigte Codestücke helfen dabei, die Komplexität von Treibercode zu beherrschen. Makros und Muster unterstützen Treiberentwickler bei kritischen Abschnitten und dem Powermanagement.

  • Kernel-Management

    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.

  • Kern-Technik

    Einen Treiber in den Kernel einzubinden ist leider wenig intuitiv. Deshalb gibt es hier ein Grundgerüst als Ausgangspunkt. Es zeigt, wie der Kernel dem Treiber Gerätenummern zuweist und der Gerätedateiverwalter Udev eigenständig die zugehörigen Gerätedateien anlegt.

  • Kernel 3.15: Aufwachen nach Suspend-to-Disk zehnmal schneller

    Intel-Ingenieur Todd Brandt hat offenbar einen Weg gefunden, mit dem ein Linux-System nach einem Suspend-to-Disk zehnmal schneller aufwacht, ohne an der Powermanagement-Infrastruktur im Kernel zu spielen. Kernel 3.15 hat das Patch bereits integriert.

comments powered by Disqus

Stellenmarkt

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.