1. Chargez
un
nouveau
noyau
linux à chaud avec kexec
Thierry GAYET
http://www.nextinnovation.org
Qui n'a jamais souhaité pouvoir tester un noyau à chaud sans avoir à
rebooter physiquement son système pour minimiser le temps mort
du au redémarrage d'un système.
1. Introduction
Le nom « kexec » provient de l'exécution du noyau, dérivé de l'appel du noyau exec Unix / Linux.
C'est un mécanisme du noyau Linux qui permet le démarrage «à chaud» d'un nouveau noyau
« sur » le noyau en cours d'exécution.
En effet, il permet donc de sauter plusieurs étape :
•
phase d'initialisation du matériel par le microprogramme du système : BIOS, BIOS UEFI,
coreboot (aka linuxBios) ;
•
différents tests sur le matériel :
◦ mémoire vive ;
◦ périphériques de stockages ou mass-storage (disques RAID, … ) ;
◦ ...
•
bootloader grub ou autre
Il charge donc directement le nouveau noyau en mémoire, et commence à s'exécuter
immédiatement après. Cela permet d'éviter de longs délais associés à un redémarrage complet, et
peut aider les systèmes à répondre aux exigences de haute disponibilité en minimisant les temps
d'arrêt.
Cela fonctionne sur des serveurs de grosses capacités (mémoire, disques, etc), et même sur
certain ordinateurs de bureau ou portables.
2. Installation
Tout comme cela peut être fait habituellement, les binaires de kexec sont disponible dans la
majorité des repos de packages. Kexec est fournis par le paquet “kexec-tools” :
$ apt-cache search kexec
[sudo] password for tgayet:
kexec-tools - tools to support fast kexec reboots
mkelfimage - utility to create ELF boot images from Linux kernel images
pxe-kexec - Fetch PXE configuration file and netboot using kexec
$ apt-get install kexec-tools
Avec les dérivés de Red Hat, la commande pourra varier « yum install kexec-tools » ou sous
gentoo « emerge -av kexec-tools ».
L'installation de ce package entraînera par dépendance l'installation d'autres packages comme
« debconf », « initramfs-tools », « makedumpfile » et enfin « crash ».
2. 3. Démarrage
Un redémarrage via “kexec” se décompose en 2 phases :
Un redémarrage via « kexec » se décompose en 2 phases :
•
chargement du noyau en spécifiant les paramètres nécessaires ;
•
exécution du redémarrage
3.1 Vérification des conditions initiales
Premièrement on vérifiera la version courante du noyau GNU/linux déjà chargé pour la comparer
une fois le redémarrage du nouveau noyau effectué :
$ uname -a
Linux PCL131017 3.8.0-34-generic #49~precise1-Ubuntu SMP Wed Nov 13 18:05:00 UTC 2013 x86_64
x86_64 x86_64 GNU/Linux
On vérifiera également les paramètres passés au noyau par le bootloader grub2 :
$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-3.8.0-34-generic root=UUID=b48414f7-fdbc-4b4d-97fc-a2b40e53e839 ro quiet
splash vt.handoff=7
Enfin, on pourra vérifier les versions noyaux présente dans la partition de boot :
$ ls -al /boot | grep vmlinuz
-rw-r--r-- 1 root root 3847424 févr. 27 2013 vmlinuz-2.6.32-art-generic
-rw-r--r-- 1 root root 4237104 nov. 18 19:47 vmlinuz-2.6.38.8-xenomai
-rw------- 1 root root 4998096 nov. 21 01:23 vmlinuz-3.2.0-57-lowlatency
-rw-r--r-- 1 root root 5426432 oct. 15 16:40 vmlinuz-3.8.0-29-generic
-rw------- 1 root root 5428304 sept. 11 20:44 vmlinuz-3.8.0-31-generic
-rw------- 1 root root 5428528 oct. 2 18:43 vmlinuz-3.8.0-32-generic
-rw------- 1 root root 5426064 oct. 24 18:50 vmlinuz-3.8.0-33-generic
-rw------- 1 root root 5426224 nov. 13 19:26 vmlinuz-3.8.0-34-generic
et
$ ls -al /boot | grep initrd.img
-rw-r--r-- 1 root root 13324390 nov. 18 20:11 initrd.img-2.6.38.8-xenomai
-rw-r--r-- 1 root root 14240658 déc. 6 12:32 initrd.img-3.2.0-57-lowlatency
-rw-r--r-- 1 root root 16208253 oct. 15 17:03 initrd.img-3.8.0-29-generic
-rw-r--r-- 1 root root 16211572 oct. 15 17:58 initrd.img-3.8.0-31-generic
-rw-r--r-- 1 root root 16214027 nov. 7 09:49 initrd.img-3.8.0-32-generic
-rw-r--r-- 1 root root 16242300 nov. 19 09:58 initrd.img-3.8.0-33-generic
-rw------- 1 root root 24034504 déc. 14 11:01 initrd.img-3.8.0-34-generic
On pourra donc soit charger un noyau compilé séparément ou bien un autre noyau.
3.2 Chargement du noyau
Dans cette phase préparatoire, il sera donné plusieurs paramètre à “kexec” :
3. •
le noyau à charger (option “-l”)
•
les options qui doivent être passé à ce noyau (option “—append=” ou “—command-line=”)
•
l’initrd à appliquer (option “—initrd=” ou “—ramdisk=”)
Cela donne donc avec le chargement du noyau 3.8.0.33 :
$ sudo kexec -l /boot/vmlinuz-3.8.0-33-generic --append="$( cat /proc/cmdline ) panic=10"
--initrd=/boot/initrd.img-3.8.0-33-generic
Pour la ligne de paramètre, on reprendra celui du noyau courant qui au runtime peut être obtenu
depuis le pseudo filesystem /proc/cmdline.
A noter que le ramdisk reste optionnel et que nous aurions pu nous contenter de :
$ sudo kexec -l /boot/vmlinuz-3.8.0-33-generic --append="$( cat /proc/cmdline ) panic=10"
Une fois le noyau chargé, cela ne l'exécute pas pour autant.
3.3 Redémarrage du noyau noyau
L’option “-e” (pour “execute”) de la commande kexec lance la séquence de redémarrage. :
$ sudo kexec -e
Attention sur un serveur cela change de noyau mais sur un desktop, la session sera coupée.
Une fois redémarré, on peut vérifier le changement de noyau grace à la version :
$ uname -a
Linux PCL131017 3.8.0-33-generic #48~precise1-Ubuntu SMP Thu Oct 24 16:28:06 UTC 2013 x86_64
x86_64 x86_64 GNU/Linux
Et voilà votre système qui vient de change de noyau et cela à chaud !!
3. L'envers du décors de kexec
Un des plus grands défis dans le développement de kexec vient du fait que le noyau Linux
fonctionne à partir d'une adresse fixe en mémoire. Cela signifie que le nouveau noyau aura besoin
de se charger à la même adresse que le noyau actuel. Sur les systèmes x86, le noyau se trouve à
l'adresse phyisique 0x100000 (adresse de 0xc0000000 en virtuel, connu sous le nom
PAGE_OFFSET). La tâche d'écrasement de l'ancien noyau avec la nouvelle se fait en trois étapes
:
•
Copie du nouveau noyau en mémoire ;
•
Déplacement de l'image du noyau dans la mémoire dynamique du noyau ;
•
Copie de cette image dans la destination réelle (en remplaçant le noyau actuel), puis
redémarrage du nouveau noyau .
Les deux premières étapes sont réalisées lors du chargement du nouveau noyau .La première
tâche consiste à interpréter le contenu du fichier image du noyau. Kexec-tool a été construit de
telle sorte que l'on puisse en principe charger et démarrer à n'importe quel moment un noyau
(même non-linux).
Actuellement, il est possible de démarrer à n'importe quel noyau de format ELF32. Le fichier est
analysé et les segment du noyau sont chargés dans les buffers. Ces segments sont classés en
fonction de la nature du code.
Par exemple, dans le cas où le format de fichier du noyau couramment utilisé est « bzImage », les
4. segments typiques sont pour le code 16 bits noyau, le code du noyau sera 32 bits, et init Code du
disque virtuel .
La structure utilisée pour le suivi de ces segments est connu comme kexec_segment et est une
structure assez simple en soit :
struct kexec_segment
{
void* buf;
size_t bufsz;
void* mem;
size_t memsz;
};
Détail de la structure kexec_segment.
Les deux premiers éléments de la structure pointe sur la mémoire tampon de l'espace utilisateur
et sa taille. Les deux éléments suivants indiquent la destination finale du segment ainsi que sa
taille.
Une fois l'image du noyau chargée en mémoire par le module relatif au format respectif, elle est
transférée dynamiquement grâce à l'appel système sys_kexec. Cet appel système alloue les
pages de noyau dynamique pour chacun des segments qui ont été adoptées depuis l'espace
utilisateur et copie les segments sur ces pages noyau.
Kexec alloue également une page de noyau pour stocker un petit stub de code assembleur, connu
sous le nom reboot_code_buffer. Cette souche de code fait le travail réel d'écrasement du noyau
actuel avec le nouveau noyau et s'il déplace. Le reboot_code_buffer est le seul tampon qui se
trouve dans son dernier « lieu transitoire». En d'autres termes, il est exécuté à partir du même
endroit où il a été initialement initialement chargé. Pour ce faire, sur les systèmes avec MMU
d'activé, la page contenant le code identité comme mapée. Pour parler simplement, il s'agit de
créer une page d'entrée de table dans la structure de la table de la page du noyau « init_mm »
avec la même adresse physique et virtuel. Cela est nécessaire pour être en mesure d'accéder à
ce morceau de code lors de l'opération de redémarrage , comme nous le verrons plus tard.
Les informations sur la reboot_code_buffer, les différents segments, et d'autres détails sont
maintenues grâce à l'utilisation de la structure de kimage suivante :
struct kimage
{
kimage_entry_t head;
kimage_entry_t *entry;
kimage_entry_t *last_entry;
unsigned long destination;
unsigned long offset;
unsigned long start;
struct page *reboot_code_pages;
unsigned long nr_segments;
struct kexec_segment segment[KEXEC_SEGMENT_MAX+1];
struct list_head dest_pages;
struct list_head unuseable_pages;
};
Détail de la structure kimage.
Les parties les plus importantes de cette structure sont, bien sûr, le segment
[KEXEC_SEGMENT_MAX+1] qui pointent vers les tampons de la mémoire du noyau contenant
l'image, et le pointeur reboot_code_pages au stub assembleur utilisé pendant la phase de
redémarrage.
Une fois l'image du noyau chargé, le système est prêt à redémarrer avec ce dernier. Le
5. fonctionnement réel sur le redémarrage sur le nouveau noyau démarre avec la commande kexec
-e . Cette commande appelle essentiellement le noyau pour effectuer un redémarrage à l'aide de
l'appel
système
de
sys_reboot,
mais
avec
un
drapeau
spécial
de
LINUX_REBOOT_CMD_KEXEC
L'appel système reboot, en voyant le drapeau spécial, transfère le contrôle à la fonction
machine_kexec(). Les actions réalisées par machine_kexec( ) sont extrêmement spécifique à
l'architecture. Sous x86, la séquence d'actions est la suivante:
1. Pour l'accès reboot_code_buffer bascule de la structure de mm du processus actuel à la
structure de init_mm du noyau ;
2.
Arrête les APICS et désactive les interruptions ;
3.
Copie l' ensemble du code de stub dans le reboot_code_buffer que l'on a attribué lors
du chargement de l'image du noyau. Le code assembleur se trouve dans le sousprogramme de relocate_new_kernel ;
4. Charge tous les registres de segment avec le segment de données du noyau
( __KERNEL_DS ) de valeur et invalide la GDT et IDT ;
5. va au code localisé dans reboot_code_buffer, et passe des informations vitales comme
les paramètres à passer au nouveau noyau, la page d'indirection contenant les adresses
source / destination de l'image du noyau, l'adresse de départ du nouveau noyau,
l'adresse de la page reboot_code_buffer et un drapeau indiquant si le système a
l'extension d'adresse physique (PAE) est activé.
Le stub assembleur effectue quant à lui les opérations suivantes :
•
Lit les arguments de la pile et les sauvegarde dans des registres et désactive les
interruptions ;
•
En utilisant l'adresse de sa propre page qui lui a été passé comme argument, met en
place une pile à la fin de cette page ;
•
Stocke l'adresse de départ de la nouvelle image du noyau sur la pile de sorte que le
retour du code de remplacement prendra automatiquement le système à l'image du
nouveau noyau ;
•
Désactive la pagination en réglant les bits appropriés dans le registre de cr0 ;
•
Remet le registre de base de répertoire de pages, cr4 , à zéro ;
•
Efface la traduction Tampons de Consultation (TLB) ;
•
Copie toutes les pages d'image du noyau dans leurs pages de destination finale ;
•
Vide le TLB une fois de plus ;
•
Remet tous les registres à zéro , à l'exception du registre de pointeur de pile esp ( car il
pointe vers la pile contenant l'adresse de départ du nouveau noyau ) .
•
« Retours » à partir du stub. Cela prend en compte automatiquement le système comme
le nouveau noyau .
Après cette séquence terminée, le nouveau noyau prend le contrôle et le système est démarré
normalement.
4. Avantages et utilisations de kexec
Les systèmes avec des exigences de haute disponibilité et les développeurs du noyau qui doivent
redémarrer sans cesse leurs systèmes apprécieront le plus des avantages que peut offrir kexec.
Parce que kexec saute les parties les plus fastidieuses (lente) du redémarrage du système, à
savoir la phase de firmware, le redémarrages avec kexec sont extrêmement rapides et la
disponibilité est augmentée.
Kexec a également des applications intéressantes dans les outils de vidage mémoire. Le projet
Linux Kernel Crash Dumps (LKCD) a utilisé kexec pour élaborer un mécanisme de dumping
6. différent.
Lors d'un kernel panic du système ou lors de l'initialisation d'un dump par l'utilisateur, l'image de la
mémoire système est comprimé et stocké dans les pages de mémoire disponibles. Ensuite, le
système est redémarré avec un autre noyau en utilisant kexec. La zone de stockage est précisé à
ce dernier de façon à éviter tout écrasement ou altération. Par la suite, le dump mémoire peut
être récupéré sur l'une d'une partition de disque ou à travers le réseau.
La clé de la réalisation réside dans le fait qu'en évitant l'étape de firmware pendant le
redémarrage, LKCD est capable d'empêcher le contenu de la mémoire physique d'être effacé.
5. Spécificité à Debian ou Ubuntu
Pour les utilisateurs de Debian, Ubuntu, etc, “kexec” ce substitue au “reboot” classique de la machine dès
son installation.
Par défaut, un “reboot” charge, à chaud, le noyau “/vmlinuz” avec les options courantes (récupérées dans
“/proc/cmdline”) et l’initrd “/initrd.img”.
Ces paramètres peuvent être modifiés dans le fichier « /etc/default/kexec » :
...
# Utilisation de kexec pour le redémarrage ? true/false
LOAD_KEXEC=true
# Noyau par défaut à charger :
KERNEL_IMAGE="/vmlinuz"
# initrd à charger par défaut :
INITRD="/initrd.img"
# Options de démarrage du noyau. Si vide, récupère les options
# de /proc/cmdline :
APPEND=""
# ex : APPEND="$( cat /proc/cmdline ) panic=10"
Une fois “kexec-tools” installé sur votre système vous pouvez effectuer un redémarrage classique via
“coldreboot” qui est un script bash contenant :
#!/bin/sh
NOKEXECFILE=/tmp/no-kexec-reboot
/bin/rm -f $NOKEXECFILE
touch $NOKEXECFILE
/sbin/reboot $*
La commande du redémarrage nécessite elle aussi des droits superutilisateur :
$ sudo coldreboot
6. Allier un boot tftp avec Kexec
Avec kexec, le package « pxe-kexec » est utilisable. Il faut tout d'abord l'installer :
$ sudo apt-get install pxe-kexec
Pour la mise en place de kexec via pxe, l'utilitaire « pxkxc » peut être téléchargé :
http://myllynen.fedorapeople.org/
Il allie la simplicité du PXE et la puissance de kexec pour permettre des démarrage d'une image
kernel / initrd depuis un serveur PXE sans la nécessité pour le soutien HW, les images de
démarrage, ou même d'avoir à redémarrer le système.
Par exemple, on peut lancer l'installation d'une distribution sans média de démarrage.
pxkxc fournit une interface utilisateur et comme il a été implémenté en Python il est facile de
l'améliorer et de le personnaliser au besoin.
Quelques exemples de cas d'utilisation concret :
7. •
Un utilisateur décide, avec le soutien kexec, de faire une nouvelle installation d'un système
linux sur un système en cours d'exécution. L'utilisateur exécute simplement pxkxc en tant
que root et choisit l'option de menu appropriée . Le programme d'installation démarre
automatiquement séparément en téléchargeant ou l'écriture des images de démarrage
incluant tout le support nécessaire pour le matériel.
•
Une entreprise dispose de plusieurs types de systèmes sur leur réseau, dont la plupart
supportent PXE au niveau matériel. Les autres sont compatible avec gPXE. Cependant , le
portage de nouveaux drivers ou même des sous-systèmes du noyau se révélerait une
tâche hardue à concrétiser pour faire en sorte que tous les drivers réseau possède un
support avec gPXE. En créant une image d'amorçage constitué par un noyau récent
GNU/linux et un initrd modifié qui lance pxkxc l' entreprise peut faire les installations
basées PXE sur tous leurs systèmes, indépendamment du niveau de prise en charge
PXE / gPXE de leur matériel. Aussi, en ajoutant quelques personnalisations comme les
paramètres de proxy HTTP ou d'autres ajustements en fonction de l'environnement de
réseau détectée, cela devient un outils convivial pour l'administrateur système et réseau.
Pxkxc jusqu'ici a surtout été testé en utilisant les touches F12 et F13, l'autre combinaisons
peuvent aussi fonctionner.
7. Sécurité
Bien que possible, la mise en œuvre d'un mécanisme tel que kexec soulève deux défis majeurs:
•
La mémoire du noyau en cours d'exécution est remplacé par le nouveau noyau, alors que
l'ancien est toujours en cours d'exécution ;
•
Le nouveau noyau devra généralement s'attendre à ce que tous les périphériques
physiques soient déjà dans un état bien défini, car ils sont après un redémarrage du
système , lorsque le BIOS , l'UEFI ou le microprogramme du système réinitialise les à un
état « sain d'esprit » . Le contournement d'un réel redémarrage peut laisser les appareils
dans un état inconnu, et le nouveau noyau devra se remettre de cette transition.
Il y a des problèmes de sécurité associés au mécanisme kexec, comme pratiquement tout ce que
peut être chargé et exécuté de cette façon, en raison du fait que le nouveau noyau à exécuter
n'est pas nécessairement signé.
En d'autres termes, même si un mécanisme dans le noyau solide permettrait d'assurer que seuls
les modules du noyau Linux signés puisse être chargés dans le noyau en cours d'exécution,
l'utilisateur root peut toujours charger du code arbitraire via kexec et l'exécuter.
8. Conclusion
Voila, avec kexec, il est possible de réduire la disponibilité d'un système en minimisant les reboot.
Cela peu également servir à tester la configuration d'un noyau.
9. Liens
•
•
•
•
•
•
•
•
•
https://www.kernel.org/pub/linux/kernel/people/geoff/petitboot/kexec-man-11.10.21g90da2c37.txt
http://manpages.ubuntu.com/manpages/precise/en/man8/kexec.8.html
http://manpages.ubuntu.com/manpages/precise/en/man8/pxe-kexec.8.html
http://myllynen.fedorapeople.org/pxkxc-0.1.1.tar.gz
http://doc.fedora-fr.org/wiki/Configuration_d
%27un_serveur_pour_lancer_des_installations_par_PXE_boot
http://pxe-kexec.berlios.de/
https://www.kernel.org/pub/linux/kernel/people/geoff/petitboot/petitboot.html
http://www.solemnwarning.net/kexec-loader/
http://www.solemnwarning.net/kexec-loader/faq