Laissez-nous vous conter l'histoire de trois développeurs qui ont rencontré un problème réseau...
Florian Forestier
Consultant Zenika ;
Enseignant vacataire ISIMA ;
Board Clermont'ech ;
Speaker & "bloggeur" ;
OSS contributor.
Louis Leseur
Concepteur-Développeur @ BeYs Cloud ;
Plus gros consommateur EDF de la région ;
OSS contributor.
Au début, tout allait bien
Un beau matin de novembre ; au bureau avec l'équipe
L'équipe, parlons-en !
Trois développeurs ;
Golang / Svelte + "DevOps"
4-6 ans d'XP
=> Pas trop le style à bidouiller le kernel
Quand soudain, un manager
"Ça marche pas !"
"J'ai des serveurs qui viennent d'arriver, et le réseau y marche po"
Euh... Ok 🤨
"Ça marche pas !"
C'est le manager du SOC ;
Qui a reçu des serveurs pour la PKI ;
Qui vient pour un problème réseau ;
Et qui demande à des devs de résoudre ça.
Bon, en vrai, y'a du sens
La PKI a appelé au secours le SOC ;
Qui a demandé à l'équipe réseau (qui a pas trouvé) ;
Du coup, nous sommes le dernier espoir.
On récupère le matos
HP ProLiant DL380 Gen 10 ;
700Go de RAM ;
20To de disque ;
2 * 24 cores (Intel)
Et notre réseau : HPE QLogic FastLinQ 45000 (x2)
Ah, et y'en a 8 comme ça. Aucun ne fonctionne.
(Bon, si, mais uniquement sur la carte d'admin en 1Gbps, c'est pas pratique)
On regarde le BIOS...
... Et on voit les cartes réseau ! 🎉
"Tranquille, c'est juste un driver" !
On installe une Debian 12
Assez logiquement, ça marche pas
On voit bien les cartes dans lspci, mais pas avec ip link
On récupère les erreurs dans dmesg
[ 9.526144] QLogic FastLinQ 4xxxx Core Module qed
[ 9.531195] qede init: QLogic FastLinQ 4xxxx Ethernet Driver qede
[ 9.583755] [qed_mcp_nvm_info_populate:3374()]Failed getting number of images
[ 9.583758] [qed_hw_prepare_single:4722()]Failed to populate nvm info shadow
[ 9.583761] [qed_probe:513()]hw prepare failed
Et on apprend des choses
Nos cartes sont bien reconnues par le kernel ! (mais ça coupe après)
Visiblement, qed et qede sont les drivers responsables de ces cartes ;
Chaque carte devrait apparaître comme 4 cartes de 25Gbps qui forment au total 100GBps ;
Et le driver plante à l'initialisation de la carte.
Il doit exister un système driver
On se met donc à chercher un driver pour nos cartes sur Internet ;
On trouve des drivers chez HPE...
... Qui sont juste des repackaging de qed/qede...
... Sur de vieilles versions du Kernel (5.x)...
... Et uniquement pour RHEL et SUSE ! 🥳🔫
Bon, ça va pas nous aider. Autre idée ?
Ça reste intéressant :
On apprend que nos HPE QLogic ne sont que des rebranding !
En fait, ce sont des Marvell QLogic
Et les drivers sont pour RHEL 7 et SUSE 12 (donc "vieux")
=> Les drivers du kernel doivent marcher en natif, sinon il y aurait eu du support !
Serait-ce un bug de la distribution Debian ?
Du coup, on installe plein de trucs !
Ubuntu (24.04) car "y'a tous les drivers de la terre dessus" => Nope.
CentOS (Stream) car "C'est juste RHEL rebadgé et instable" => Nope.
Alpine Linux (3.18) car "On sait jamais, sur un malentendu, ça peut passer..." => Nope.
Et là, l'envie d'abandonner.
Rien ne marche, et il n'existe aucun driver disponible.
On retourne à nos sujets habituels
On conserve un serveur sur un coin de bureau, au cas où une idée surgisse.
D'autres tentent, et se cassent les dents aussi.
Et soudain, l'idée à la con 💡
(et c'est moi qui l'ai eu, eheh)
"Hey, si on installe cette vieille Ubuntu 18, ça donne quoi ?"
"Mais Florian c'est totalement idiot, on casse jamais* la compatibilité ascendante dans Linux tu le sais tr... Oh,
ça marche"
Ubuntu 18 => Tout droit !
root@ubuntu-18-lts:~# ip a
[...]
6: ens2f0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN [...]
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
7: ens2f1: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN [...]
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
8: ens2f2: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN [...]
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
9: ens2f3: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN [...]
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff
[...]
Et là, on hésite entre la joie et l'envie de pleurer
Ça marche !
Mais sur un OS qui date d'il y à des années.
Faire n'importe quoi fonctionne, continuons !
On récupère la version du kernel de notre Ubuntu 18 (4.15) ;
Et on le relance notre Debian 12 sur le 4.15.
Et... ça marche. 👀
Aller, juste pour être sûr, on recompile un 4.15 depuis les sources... Ça marche aussi !
Jazz music stops
Le bug est dans le kernel, quelque part.
Mais comment le trouver ?
Ubuntu 18 : Kernel 4.15
Debian 12 : Kernel 6.1
Il y a 4 ans de commits sur le projet le plus gros du monde !
(
C'est quoi, le Kernel ?
Le Kernel, c'est un gros morceau de code qui gère les fondements du système :
Allocation mémoire, "aiguillage" des instructions processeur, réseau, etc
C'est du très bas niveau, c'est du C, c'est un projet énorme
Quand on parle de distribution Linux, on parle d'un système d'exploitation qui utilise ce Kernel
Créé par Linus Torvalds en 1991, open-sourcé en 1992
C'était le chainon manquant à la communauté Open-Source pour faire un OS complet (👋 Hurd)
Mais le Kernel c'est pas un OS à lui seul !
Pour utiliser le kernel, il faut un ensemble de logiciels utilitaires "de base" (ls, cat, more, tail, etc)
Il en existe plusieurs, notamment :
GNU-CoreUtils (Debian, RHEL, SUSE)
BSD (FreeBSD)
BusyBox (Alpine, OpenWRT)
Et ça, ça fait (à peu près) un OS !
Mais un OS pas giga utilisable.
Il manque les bibliothéques système (glibc notamment), un gestionnaire de paquet, de daemon, de fenêtres, etc
=> On créé des distributions, qui packagent tout ça : Debian, Ubuntu, Alpine Linux, etc
Donc, quand on utilise Debian, en vrai :
On utilise la distribution Debian ;
Qui utilise les utilitaires GNU ;
Avec la bibliothèque standard glibc ;
Avec le kernel Linux.
Ne dites plus Debian mais Debian GNU/Linux !
(d'ailleurs, il existe une variante Debian GNU/Hurd)
)
Deux questions :
Quelle partie du kernel casse ?
En quelle version le breaking change apparaît ?
Quelle partie du kernel casse ?
[ 9.526144] QLogic FastLinQ 4xxxx Core Module qed
[ 9.531195] qede init: QLogic FastLinQ 4xxxx Ethernet Driver qede
[ 9.583755] [qed_mcp_nvm_info_populate:3374()]Failed getting number of images
[ 9.583758] [qed_hw_prepare_single:4722()]Failed to populate nvm info shadow
[ 9.583761] [qed_probe:513()]hw prepare failed
Le driver qui plante semble être qed sur une invocation de qede
Euh, attend, qed et qede ?
Ce sont des drivers intégrés au kernel qui gèrent plein de cartes réseaux.
qed contrôle directement le firmware, les interruptions système, etc ;
qede expose les interfaces Ethernet de nos cartes ;
Mais ils ont plein d'autres copains !
qedr contrôle la convergence des 4 cartes Ethernet incluses dans notre carte PCI en un seul port
optique (infiniband) ;
qedi permet de gérer iSCSI (dans notre cas, aucun usage) ;
qedf permet de gérer FCoE (idem).
C'est quand que ça a foiré ?
Name & Shame
Essayer de trouver l'endroit exact pour identifier la source du bug
=> Et ainsi faciliter le fix !
C'est quand que ça a foiré ?
Entre la 4.15 et la 6.1, il y a 28 versions (hors version "fix")
Idée : On monte de LTS en LTS, et on voit où ça casse.
Une LTS => Plusieurs années de support. Non-LTS : 6 mois.
=> La LTS après la 4.15, c'est la 4.19
4.19 sur Debian 12 -> Fail !
Notre bug est donc entre la 4.15 et la 4.19 !
On retente en 4.17 ?
4.17 sur Debian 12 -> Fail !
C'est quelque part entre la 4.15 et la 4.17
Aller, en 4.16 pour être sûr !
4.16 sur Debian 12 -> It works !
On est donc quelque part sur les commits entre la 4.16 et la 4.17 !
Bon, y'a plus qu'à analyser les commits entre ces deux versions !
Et on trouve assez vite !
Le commit 43645ce rajoute la
fonction qed_mcp_nvm_info_populate
Et c'est cette méthode qui semble poser soucis d'après les logs :
[ 9.526144] QLogic FastLinQ 4xxxx Core Module qed
[ 9.531195] qede init: QLogic FastLinQ 4xxxx Ethernet Driver qede
[ 9.583755] [qed_mcp_nvm_info_populate:3374()]Failed getting number of images <== ICI !
[ 9.583758] [qed_hw_prepare_single:4722()]Failed to populate nvm info shadow
[ 9.583761] [qed_probe:513()]hw prepare failed
Avec le commit 50bc60c
(juste avant) => Ça fonctionne !
Et c'est parti pour le fix
On regarde la fonction qui nous fait mal : qed_mcp_nvm_info_populate
Grâce au log, on identifie sa localisation
/* Acquire from MFW the amount of available images */
nvm_info.num_images = 0;
rc = qed_mcp_bist_nvm_get_num_images(p_hwfn, p_ptt, &nvm_info.num_images);
if (rc == -EOPNOTSUPP) {
DP_INFO(p_hwfn, "DRV_MSG_CODE_BIST_TEST is not supported\n");
goto out;
} else if (rc || !nvm_info.num_images) {
DP_ERR(p_hwfn, "Failed getting number of images\n");
goto err0;
}