Aus Linux-Magazin 03/2025

Kernel- und Treiberprogrammierung mit dem Linux-Kernel – Folge 138

© masarik512 / 123RF.com

Ein Watchdog rettet gegebenenfalls einen eingefrorenen Rechner auf die harte Tour. Linux setzt dazu auf einfach zu handhabende Technik bei der Soft- und Hardwareanbindung. Es gibt aber auch Fallstricke.

Wie war das noch gleich mit der fehlerfreien Software? Richtig, die gibt es nicht. Auch Linux macht Fehler, bietet aber mit dem Watchdog-Subsystem ein Sicherungsnetz [1]. Wenn gar nichts mehr geht, wird der Wachhund aktiv, sorgt für einen Neustart und verhindert damit den dauerhaften Stillstand. Das ist gerade für unternehmenskritische Server oder Steuerungssysteme von immanenter Wichtigkeit.

Bei einem klassischen Watchdog handelt es sich um einen in Hardware realisierten Rückwärtszähler. Sobald er den Wert Null erreicht, löst er einen Reset aus und lässt den Rechner dadurch neu starten. In einem gesunden System wird die Null nie erreicht, da ein Task den Zähler per Keepalive-Signal rechtzeitig wieder auf seinen Ausgangswert zurücksetzt (Abbildung 1). Der Task generiert also periodisch den notwendigen Herzschlag. In einer “weichen” Variante verzichtet man auf die Hardware und realisiert den Zähler und den damit verbundenen Reset rein in Software.

Abbildung 1: Ein rechtzeitiges Keepalive verhindert den System-Reset.

Abbildung 1: Ein rechtzeitiges Keepalive verhindert den System-Reset.

Linux bietet seit 1996 Support für Watchdogs. Ein einheitliches Interface erschien mit dem Kernel 2.6 im Jahr 2003, eine grundlegende Modernisierung der internen Mechanismen fand 2016 mit Linux 4.8 statt. Dabei blieb das Interface zum Userland stabil.

Treiber-Magie

Diese Schnittstelle basiert im Wesentlichen auf der Gerätedatei »/dev/watchdog« und einigen I/O-Controls. Der Prozess, der dafür verantwortlich ist, das Lebenszeichen zu geben, öffnet die Gerätedatei und aktiviert damit den Watchdog.

Einmal aktiviert, bleibt der Watchdog selbst dann aktiv, wenn Sie das Gerät wieder per »close()« schließen. Es sei denn, die Magic-Close-Eigenschaft ist aktiviert und die schließende Applikation schreibt zum Abschied ein “V” auf die Gerätedatei. Allerdings unterstützt nicht jeder Treiber das Flag »WDIOF_MAGICCLOSE«: It’s not a bug, it’s a feature. Schließlich könnte der Watchdog auch versehentlich deaktiviert werden, und das wäre bei kritischen Systemen einfach zu gefährlich.

Unterstützt der Treiber Magic Close, kann die Software den Watchdog nicht nur per “V” deaktivieren, sondern auch per I/O-Control »WDIOC_SETOPTIONS« und den Parameter »WDIOS_DISABLECARD« (Tabelle “Watchdog-Settings”). Mit dem I/O-Control »WDIOC_GETOPTIONS« und der Übergabe der Speicheradresse einer Struktur »struct watchdog_info« überprüfen Sie im Bedarfsfall, ob »nowayout« (also kein Magic Close) gesetzt ist.

Name

Bedeutung

»WDIOS_DISABLECARD«

Watchdog-Timer deaktivieren

»WDIOS_ENABLECARD«

Watchdog-Timer aktiveren

»WDIOS_TEMPPANIC«

Kernel-Panik bei Temperaturproblemen

Herzschlag

Nach dem erfolgreichen Öffnen der Gerätedatei signalisiert eine Schreiboperation darauf, dass alles in Ordnung ist und der Zähler auf seinen Ursprungswert zurückgesetzt werden kann (Keepalive-Signal), also weit weg von der Reset-auslösenden Null. Dabei spielt es keine Rolle, welchen konkreten Wert die Applikation schreibt. Alternativ kann das Programm das I/O-Control »WDIOC_KEEPALIVE« absetzen.

Weitere I/O-Controls deklariert die Header-Datei »linux/watchdog.h« (siehe Tabelle “Watchdog-I/O-Controls”). Eine Reihe von Codebeispielen zum Einsatz der Controls finden Sie in Listing 1. Allerdings sind die meisten I/O-Controls optional.

Name

Datentyp

Bedeutung

»WDIOC_GETSUPPORT«

»struct watchdog_info«

Bit-Maske der unterstützten I/O-Controls

»WDIOC_GETSTATUS«

»int«

aktueller Status

»WDIOC_GETBOOTSTATUS«

»int«

Ursache des letzten Reboots

»WDIOC_GETTEMP«

»int«

Temperatur

»WDIOC_SETOPTIONS«

»int«

Bitmap: Enable und/oder Disable Card

»WDIOC_KEEPALIVE«

»int«

setzt den Timeout auf den Vorgabewert (Trigger)

»WDIOC_SETTIMEOUT«

»int«

Einstellen des Timeouts (Heartbeat, in Sekunden)

»WDIOC_GETTIMEOUT«

»int«

Rückgabe des eingestellten Timeouts

»WDIOC_SETPRETIMEOUT«

»int«

Setzen des Pretimeouts (in Sekunden)

»WDIOC_GETPRETIMEOUT«

»int«

Auslesen des Pretimeouts (in Sekunden)

»WDIOC_GETTIMELEFT«

»int«

Auslesen des Timeouts

Listing 1

keepalive.c – Watchdog ansteuern

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <linux/watchdog.h>
static int fd_wdt;
static void cleanup_and_exit(int signo)
{
 printf("catched signal %d, deactivate watchdog\n", signo);
 write(fd_wdt, "V", 1); // magic to stop wdt
 close(fd_wdt);
 exit(EXIT_SUCCESS);
}
int main(int argc, char **argv, char **envp)
{
 int ret;
 int time_left;
 struct timespec sleeptime;
 struct sigaction sa;
 int pretimeout;
 int option = WDIOS_DISABLECARD;
 sa.sa_handler = cleanup_and_exit;
 sigemptyset(&sa.sa_mask);
 sa.sa_flags = 0;
 if (sigaction(SIGINT, &sa, NULL) == -1
 || sigaction(SIGTERM, &sa, NULL) == -1) {
 perror("sigaction");
 return -1;
 }
 fd_wdt = open("/dev/watchdog0", O_RDWR);
 if (fd_wdt<0) {
 perror("/dev/watchdog");
 return -1;
 }
#if 0
 pretimeout = 10;
 if (ioctl(fd_wdt, WDIOC_SETPRETIMEOUT, &pretimeout) < 0) {
 perror("Fehler beim Setzen des Pretimeouts");
 //close(fd_wdt);
 //return - 1;
 }
#endif
 sleeptime.tv_sec = 15; // keep alive period
 sleeptime.tv_nsec= 0;
 while (1) {
 //if (ioctl(fd_wdt, WDIOC_GETTIMELEFT, &time_left) == 0) {
 //  printf("time left: %d\n", time_left);
 //}
 //else {
 //  perror("ioctl failed");
 //}
 printf("sending keep alive\n");
 ret = write(fd_wdt, "\0", 1);
 if (ret<=0) {
 perror("write failed");
 break;
 }
 clock_nanosleep(CLOCK_MONOTONIC, 0, &sleeptime, NULL);
 }
 // alternative to the magic "V"
 if (ioctl(fd_wdt, WDIOC_SETOPTIONS, &option) < 0) {
 perror("ioctl(WDIOC_SETOPTIONS, WDIOS_DISABLECARD)");
 }
 else {
 printf("watchdog deactivated\n");
 }
 close(fd_wdt);
 return 0;
}

Mach Sitz!

Greifen Sie jetzt aber bitte nicht übereifrig per Cat oder Echo auf den Watchdog zu. Mit etwas Pech wird sonst Ihr Rechner nach wenigen Sekunden neu starten. Schließlich öffnen beide Programme die Gerätedatei »/dev/watchdog«, ohne danach ein Lebenszeichen zu erzeugen. Geben Sie stattdessen lieber im Terminal »wdctl« ein: Das Programm zeigt Ihnen, ob ein Watchdog zur Verfügung steht und falls ja, welche grundsätzlichen Eigenschaften er aufweist.

Wachhund installieren

Allerdings besteht eine hohe Wahrscheinlichkeit, dass Wdctl zunächst lediglich das Fehlen der Gerätedatei »/dev/watchdog« anmeckert. Wurde der Kernel mit Watchdog-Support kompiliert, ist vermutlich wohl nur der Watchdog-Treiber nicht geladen.

Um herauszufinden, wie der Name des fehlenden Treibers lautet, suchen Sie im Sys-Filesystem per Find nach Dateien mit einem »wdt« oder »watchdog« im Namen. Auf Intel-Hardware findet sich beispielsweise häufig ein PCI-Device mit dem Namen »iTCO-wdt«. Der zugehörige Treiber trägt denselben Namen, sprich: Er lässt sich per »modprobe iTCO-wdt« laden. Danach sollte die Datei »/dev/watchdog« existieren.

In einem Fall haben wir im Test zwar die Hardware gefunden und konnten den Treiber laden, doch der meldete lapidar, der Hardware-Watchdog sei ausgeschaltet. Da kann man dann nichts machen, aber es steht ja immer noch die softwarebasierte Variante zur Verfügung. Sie hat den Vorteil, dass sie sich gut konfigurieren lässt und einen Testmodus mitbringt.

Dazu setzen Sie beim Laden des Treibers den Parameter »soft_noboot« auf 1. Über die Option »soft_margin« stellen Sie die Timeout-Zeit ein. Per »soft_reboot_cmd« definieren Sie ein Kommando, das der Treiber beim Erreichen des Timeouts ausführt. Für einen Test laden Sie also beispielsweise den Treiber mittels des Kommandos aus Listing 2.

Listing 2

Treiber laden

# modprobe softdog soft_noboot=1 soft_reboot_cmd="/bin/false"

Weiche Hunde

Danach gibt es nicht nur die Datei »/dev/watchdog«, sondern mindestens zusätzlich noch »/dev/watchdog0«. Der Kernel ist nämlich darauf vorbereitet, dass mehrere unterschiedliche Watchdogs in einem System existieren, die er mit 0 beginnend durchnummeriert. Die Datei »/dev/watchdog« repräsentiert dabei den priorisierten, typischerweise ersten Watchdog. Sie existiert vor allem aus Gründen der Abwärtskompatibilität.

Jetzt kann der Test beginnen. Kompilieren Sie dazu den Quellcode »keepalive.c« aus Listing 1 per »make keepalive«. Er erzeugt das Keepalive-Signal und deaktiviert bei einem Programmabbruch den Watchdog. Sie starten das Programm als Superuser per »sudo ./keepalive«. Um die Wirkungsweise zu verfolgen, lassen Sie sich in einem gesonderten Terminal per »dmesg –follow | grep softdog« die relevanten Systemmeldungen ausgeben. Idealerweise beobachten Sie zusätzlich in einem dritten Terminal den Zustand des Watchdogs per »wdctl«.

Standardmäßig ist die Timeout-Zeit für den Watchdog auf 60 Sekunden gesetzt. Da der Code alle 15 Sekunden ein Keepalive sendet, kommt es zu keinem Reboot und auch zu keiner Meldung über einen abgelaufenen Watchdog im Syslog. Beenden Sie jetzt durch einen Druck auf [Strg]+[C] das Programm und konfigurieren Sie durch Eingabe von »wdctl -s 10« den Watchdog um.

Jetzt bleiben nur noch 10 Sekunden Zeit, das Keepalive zu senden. Da unser Programm es nur alle 15 Sekunden abschickt, kommt es zu Meldungen im Syslog (Abbildung 2). Da wir unseren Softdog im Testmodus betreiben, kommt es aber glücklicherweise zu keinem echten Neustart.

Abbildung 2: Trifft das Keepalive nicht rechtzeitig ein, l&ouml;st der Watchdog aus.

Abbildung 2: Trifft das Keepalive nicht rechtzeitig ein, löst der Watchdog aus.

Noch besser können Sie die Aktionen des Watchdogs verfolgen, indem Sie einen eigenen Watchdog-Treiber programmieren und in den relevanten Funktionen Ausgaben als Kernel-Nachrichten erzeugen.

Im Zentrum

Dreh- und Angelpunkt eines Watchdog-Kernel-Treibers ist ein Objekt vom Typ »struct watchdog_device« (Abbildung 3). Es beschreibt die Treibereigenschaften (Attribut »info«), die Funktionsadressen (Attribut »ops«), den erlaubten Timeout-Bereich sowie den initialen Timeout (Attribut »timeout«).

Abbildung 3: Die Grundstruktur eines Watchdog-Treibers beruht auf einem Objekt des Typs &raquo;struct watchdog_device&laquo;.

Abbildung 3: Die Grundstruktur eines Watchdog-Treibers beruht auf einem Objekt des Typs »struct watchdog_device«.

Die Eigenschaften des Watchdog-Treibers finden sich in der Struktur »struct watchdog_info«. Beim Element »options« handelt es sich um ein Bit-Feld, in dem jedes Bit als Flag eine Eigenschaft repräsentiert, die der Treiber unterstützt. Die Flags sind in der Header-Datei »linux/watchdog.h« definiert und lassen sich am Präfix »WDIOF« (»F« steht hier für Flag) erkennen (Abbildung 4). Der Treiber in Listing 3 implementiert Keepalive, Magic Close und das Setzen der Timeout-Zeit.

Abbildung 4: Die Eigenschaften des Treibers sind im Bitfeld &raquo;options&laquo; kodiert.

Abbildung 4: Die Eigenschaften des Treibers sind im Bitfeld »options« kodiert.

Listing 3

wdt.c – Watchdog-Treiber-Codegerüst

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/watchdog.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#define WATCHDOG_TIMEOUT 42
static int timeout = WATCHDOG_TIMEOUT;
static bool nowayout = 0;
static struct watchdog_device my_wdt_dev;
static int my_wdt_start(struct watchdog_device *wdd)
{
  pr_info("starting watchdog\n");
  return 0; // success
}
static int my_wdt_stop(struct watchdog_device *wdd)
{
  pr_info("stopping watchdog\n");
  return 0; // success
}
static int my_wdt_ping(struct watchdog_device *wdd)
{
  pr_info("pinging watchdog\n");
  // reset watchdog
  return 0;
}
static int my_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
{
  my_wdt_dev.timeout = timeout;
  pr_info("setting timeout to %d\n", timeout);
  return 0;
}
static struct watchdog_ops my_wdt_ops = {
  .owner = THIS_MODULE,
  .start = my_wdt_start,
  .stop = my_wdt_stop,
  .ping = my_wdt_ping,
  .set_timeout = my_wdt_set_timeout,
};
static struct watchdog_info my_wdt_info = {
  .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
  .firmware_version = 1,
  .identity = "My Watchdog",
};
static int __init my_wdt_init(void)
{
  my_wdt_dev.info = &my_wdt_info;
  my_wdt_dev.ops = &my_wdt_ops;
  my_wdt_dev.min_timeout = 1;
  my_wdt_dev.max_timeout = 600;
  my_wdt_dev.timeout = timeout;
  watchdog_set_nowayout(&my_wdt_dev, nowayout);
  pr_info("registering watchdog device\n");
  return watchdog_register_device(&my_wdt_dev);
}
static void __exit my_wdt_exit(void)
{
  pr_info("unregistering watchdog device\n");
  watchdog_unregister_device(&my_wdt_dev);
}
module_init(my_wdt_init);
module_exit(my_wdt_exit);
MODULE_LICENSE("GPL");

Alle Eigenschaften sind in Methoden definiert, deren Adressen die Struktur »struct watchdog_ops« enthält. Die Methode zum Starten des Watchdogs müssen Sie zwingend implementieren, alle anderen sind optional. In unserem Demo-Treiber implementieren wir neben »start« noch »stop«, »ping« und »set_timeout«. Die Methoden geben nur eine kurze Nachricht aus, ohne weitere Funktionalität zu implementieren, mit Ausnahme des Setzens des Timeouts.

In einem realen Treiber würde ein Aufruf von »start« den Zähler aktivieren und »ping« ihn jeweils wieder auf den unkritischen Wert zurücksetzen. Ein »stop« hält den Zähler an. Löst die Hardware bei Erreichen des Zählerstands Null keinen Reboot aus, kann der Treiber dazu die Kernel-Funktion »emergency_restart()« aufrufen.

Kein Ausweg

Wichtig ist noch die Frage, ob man den Watchdog-Treiber nach seiner Aktivierung wieder deaktivieren darf. Falls nicht, gilt es, das Bit »WDOG_NO_WAY_OUT« im Attribut »status« zu setzen. Das erledigt die Funktion »watchdog_set_nowayout()«.

Per Aufruf der Kernel-Funktion »watchdog_register_device()« mit der »struct watchdog_device« als Parameter meldet sich der Treiber beim Watchdog-Subsystem an. Anschließend braucht man sich nicht mehr um das Anlegen der Gerätedateien und der Verwaltungseinträge im Sys-Filesystem zu kümmern. Überdies stellt das Watchdog-Subsystem sicher, dass nicht mehr als ein einzelner Task auf den Wachhund zugreift.

Der Quellcode aus Listing 3 (»wdt.c«) lässt sich mithilfe des Makefiles aus Listing 4 durch Aufruf von »make« in das Kernel-Objekt »wdt.ko« überführen. Per »sudo insmod wdt.ko« laden Sie das Modul, das Sie dann durch Aufruf der Keepalive-App (»sudo ./keepalive«) testen. Abbildung 5 zeigt den Ablauf.

Abbildung 5: Der Beispieltreiber demonstriert die internen Abl&auml;ufe.

Abbildung 5: Der Beispieltreiber demonstriert die internen Abläufe.

Listing 4

Makefile

obj-m += wdt.o
all:   make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Der Zustand des Treibers und seine Parameter lassen sich übrigens auch über das Sys-Filesystem verifizieren. Dazu legt der Kernel unterhalb von »/sys/devices/virtual/watchdog/« für jeden Treiber ein Unterverzeichnis an, in dem er die einzelnen Attribute in Dateiform ablegt.

Zustandswächter aus dem Regal

Warum das Rad neu erfinden, wenn es das Gesuchte schon gibt? Per »sudo apt install watchdog« installieren Sie unter Ubuntu die Programme »watchdog«, »wd_keepalive« und »wd_identify«. Letzteres spoilert Informationen zum in der Datei »/etc/watchdog.conf« konfigurierten Watchdog. »wd_keepalive« entspricht prinzipiell dem im Artikel im Quellcode vorgestellten Programm »keepalive«. Es öffnet die Datei »/dev/watchdog« und verhindert durch das regelmäßige Schreiben auf das Gerät den ungewollten Neustart.

Die Erweiterung »watchdog« wiederum nimmt zudem diverse eigene Systemtests vor. Das Programm prüft, ob noch neue Rechenprozesse gestartet werden können, ob es genügend freien Speicher gibt, ob man auf in der Konfiguration definierte Dateien zugreifen kann und ob das Netzwerk und der Zugriff auf das Internet funktionieren. Daneben sieht es nach, ob sich konfigurierte Dateien innerhalb eines Intervalls verändert haben, ob die generelle Systemlast zu hoch liegt, ob konfigurierte Tasks laufen oder ob es dem System gerade zu heiß wird. Zusätzlich lassen sich noch eigene Checks einpflegen und Reparaturkommandos absetzen.

Grundsätzlich leitet »watchdog« einen Soft-Reboot ein, wenn eine der Prüfungen fehlschlägt oder einer der Checks länger als 60 Sekunden dauert. Dann versucht der Daemon, das System möglichst ordnungsgemäß herunterzufahren.

In der Konfiguration sollten Sie in jedem Fall die Variable »realtime« auf »yes« setzen, damit der Daemon in Lastsituationen nicht aus dem Speicher fliegt und in möglichst allen Situationen aktiv bleibt.

Besonderheiten

Es existieren noch weitere Features, die allerdings nur ausgewählte Treiber unterstützen. So gibt es beispielsweise ein Pretimeout, das kürzer als das eigentliche Timeout ist. Wird das Pretimeout erreicht, ohne dass ein Keepalive eingetroffen ist, sendet der Kernel der Keepalive-Applikation beispielsweise ein Signal (»SIG_ALARM«). Die gut programmierte Applikation fängt das Signal ab und kann vor dem potenziell anstehenden Reset beispielsweise noch Maßnahmen zur Systemstabilisierung einleiten und Daten sichern.

Außerdem kann der Watchdog aktiv werden, wenn er hardwarebedingte Fehlerquellen registriert wie eine Unter- oder Überspannung (»WDIOF_POWERUNDER«, »WDIOF_POWEROVER«), eine Überhitzung (»WDIOF_OVERHEAT«) oder den Ausfall eines Lüfters (»WDIOF_FANFAULT«). Es gibt Watchdog-Hardware, die elektrische Eingänge zur Verfügung stellt und bei der der Treiber bei definierten Pegeln den Reboot einleitet. Per »WDIOC_GETBOOTSTATUS« lassen Sie sich dann mit dem nächsten Reboot die Ursache für den Neustart mitteilen.

Fazit

Bei ernsten Problemen kann man mit einem Watchdog also retten, was noch zu retten ist. Technik und Handhabung bleiben einfach gestrickt, wie man es von einem effektiven Sicherungssystem erwartet, bieten aber zugleich ausgefeilte Möglichkeiten. Und das Schönste: Das Programmieren eines eigenen Watchdog-Kernel-Treibers stellt, wie Sie hier gesehen haben, erfreulicherweise kein Hexenwerk dar. (jlu)

Der Autor

Jürgen Quade, Professor an der Hochschule Niederrhein, gibt für Unternehmen Schulungen zu den Themen Treiberprogrammierung und Embedded Linux.

Infos

  1. Watchdog-Support in der Linux-Kernel-Doku: https://www.kernel.org/doc/html/latest/watchdog/index.html
DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 6 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben