On a patché le kernel !

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 ;

Louis Leseur

  • Concepteur-Développeur @ BeYs Cloud ;
  • Plus gros consommateur EDF de la région ;
  • Jeux de mots terribles ;

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 de la PKI ;
  • Qui vient pour un problème réseau ;
  • Sur du hardware custom ;
  • Et qui demande à des devs de résoudre ça.


Bon, en vrai, y'a du sens

  • La PKI a un besoin spécifique (un lab grandeur nature), donc ils ont commandé du matériel ;
  • En voyant le problème, ils ont demandé à l'équipe réseau (qui n'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
            

Pour être sûr, on recompile

  • Avec le commit 43645ce => Ça plante ;
  • 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;
}
            

Et c'est parti pour le fix


int qed_mcp_bist_nvm_get_num_images(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u32 *num_images) {
	u32 drv_mb_param = 0, rsp;
	int rc = 0;

	drv_mb_param = (DRV_MB_PARAM_BIST_NVM_TEST_NUM_IMAGES << DRV_MB_PARAM_BIST_TEST_INDEX_SHIFT);
	rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_BIST_TEST, drv_mb_param, &rsp, num_images);
	if (rc)
		return rc;
	if (((rsp & FW_MSG_CODE_MASK) != FW_MSG_CODE_OK))
		rc = -EINVAL;

	return rc;
}
            

Et c'est parti pour le fix

  • Comme on a deux possibilités, on rajoute deux lignes de log pour savoir où on échoue (print("papate"))
  • On vient de trouver la ligne fautive ! Maintenant, il faut comprendre pourquoi.
  • => Notre serveur, il échoue avec quel code d'erreur ?

Et c'est parti pour le fix

  • On recompile en ajoutant un log qui affiche la résultat de notre addition booléenne rsp & FW_MSG_CODE_MASK
  • => Le code affiché est 0x00000000
  • Grâce à un peu de logique booléenne et au fichier qed_mfw_hsi.h on découvre que le code...
  • ... Correspond à une opération non supportée !


Et c'est parti pour le fix

  • Pour rappel, dans qed_mcp_nvm_info_populate, nous avions ceci :

if (rc == -EOPNOTSUPP) {
	DP_INFO(p_hwfn, "DRV_MSG_CODE_BIST_TEST is not supported\n");
	goto out;
}
            

... Sauf que si personne ne renvoie EOPNOTSUPP, ça sert pas à grand chose !

Et c'est parti pour le fix

  • On rajoute donc un simple check dans qed_mcp_bist_nvm_get_num_images :

int qed_mcp_bist_nvm_get_num_images(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u32 *num_images) {
	u32 drv_mb_param = 0, rsp;
[...]

	rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_BIST_TEST, drv_mb_param, &rsp, num_images);
	if (rc)
		return rc;

+	if (((rsp & FW_MSG_CODE_MASK) == FW_MSG_CODE_UNSUPPORTED)) // <= Là :-)
+		rc = -EOPNOTSUPP;
~	else if (((rsp & FW_MSG_CODE_MASK) != FW_MSG_CODE_OK))
		rc = -EINVAL;
	return rc;
} 

On recompile...

  • Version 6.12 (latest), avec notre fix...

Et ça marche !

On a sauvé nos serveurs ! 🎉

  • On soumet alors une modification dans le kernel pour faire profiter à tout le monde
  • Et on découvre le kernel lore

Kernel lore

  • C'est l'historique complet du Kernel, sous forme de mailing-list !
  • Comme le kernel est trop gros pour être un projet Git standard, les changes sont gérés par mail :
    • On envoie nos changes avec un descriptif complet ;
    • En respectant à la lettre un ensemble de règles de soumissions ;
    • Les réponses servent de peer-review / relecture ;
    • Et à la fin, un mainteneur effectue le merge sur la code-base

Kernel lore


Et au bout de quelques semaines...


  • Notre soumission est approuvée, et nous arrivons dans le code ! (en 6.13)
  • Pour anecdote, ce fix va être backporté jusqu'en LTS 5.4 (2019) !

Et si on avait demandé à l'IA ?

(Vu que le LavaJUG n'a parlé que de ça en 2024, fallait le faire)

ChatGPT-5, 15/09/2025

I. Le diagnostic

J'ai une erreur "Failed getting number of images" sur une carte HPE QLogic 45000 avec une Debian 12 Kernel 6.1 Aide moi à diagnostiquer la cause de l'erreur.


[...]
Commence par confirmer quel driver est chargé pour cette carte :
lspci -nnk | grep -A 3 -i qlogic

Puis regarde s'il y a des erreurs au chargement du module :
dmesg -T | grep -iE "qlogic|qede|qla|qlcnic|qed"
[...]


✅ On a en effet fait ça !

I. Le diagnostic

Et là, ChatGPT nous propose déjà des solutions !

[...]
Liste le firmware installé :
dpkg -l | grep firmware
Tu devrais avoir quelque chose comme firmware-qlogic installé. Sinon :
sudo apt update ; sudo apt install firmware-qlogic
[...]


I. Le diagnostic

Jusque là, rien de bien différent par rapport aux suggestions de Google.

I. Le diagnostic

Il nous propose aussi d'explorer les propriétés de la carte

I. Le diagnostic

Il nous propose aussi de mettre à jour le Kernel (et il sait même la dernière version dans les backports)

II. Fix-assist

Le driver chargé est qed/qede. De quelle partie du code vient l'erreur "Failed getting number of images" ?


Bonne précision ! Si le driver chargé est qed/qede, on est bien sur la famille de drivers pour les cartes réseau Ethernet/Converged Network Adapters de Broadcom/QLogic (famille 44xxx/45xxx).

L’erreur "Failed getting number of images" vient en fait du qed_main.c (ou plus précisément du code de gestion du firmware dans le driver qed), au moment où il tente d’interroger le Management Controller (MFW) de la carte pour connaître combien d’images de firmware/NVM sont présentes.

Voici où ça se situe dans le code source du kernel Linux (drivers/net/ethernet/qlogic/qed/) :


✅ Sans-faute !

II. Fix-assist

😬 Il a presque tout juste...
Sauf pour le fichier concerné, le nom de la fonction et le code d'erreur.

III. Pour le plaisir

Est-ce qu'il existe un patch pour ce problème précis ?


Un patch intitulé “net/qed: allow old cards not [able to return]” (résumé) qui modifie le comportement introduit par le commit 43645ce03e00 (“qed: Populate nvm image attribute shadow.”).

Le commit original “Populate nvm image attribute shadow” ajoutait la gestion d’un attribut “num_images” pour les images NVM. Cela veut dire que le driver essaie toujours de demander au firmware le nombre d’images, via la commande mentionnée précédemment. lkml.org

Mais certaines cartes anciennes ou spécifiques ne supportent pas cette commande / n’ont pas la capacité de renvoyer cette information, donc l’appel échoue avec EINVAL.


😮 Oui, tout juste !

III. Pour le plaisir

Quels sont les auteurs du patch ?


D’après les discussions sur les listes de diffusion kernel, les principaux contributeurs du patch sont :

Louis Leseur (HPE) – auteur du patch
📧 louis.leseur [at] hpe.com
→ C’est lui qui a soumis le correctif à la mailing list netdev [at] vger.kernel.org.

Jakub Kicinski (Kernel networking maintainer) – reviewer et intégrateur
📧 kuba [at] kernel.org
→ Responsable de l’acceptation dans la branche net.

David S. Miller (mainteneur networking de Linux) – merge final
📧 davem [at] davemloft.net


Trahison, disgrâce

  • Je suis pas dans la liste ! (je ne vis que pour la gloire et la célébrité, d'où ma présence ce soir) ;
  • Jakub Kicinski & David S. Miller n'ont jamais participé à ce patch spécifique ;
  • Paolo Abeni (notre copain de chez RedHat) n'a pas été cité non plus ;
  • Et Louis... tu n'aurais pas quelque chose à nous dire ? 🧐


Louis Leseur (HPE) – auteur du patch
📧 louis.leseur [at] hpe.com

Pour conclure...

  • On a eu de la chance dans notre debug.
  • Linux, c'est super cool ; et le fait qu'il soit open-source encore plus !
  • Aucun système n'est infaillible, même le meilleur de tous.
  • Pour une fois, c'était pas la faute du réseau 🫣
  • Si trois gobelins y sont arrivés, vous pouvez vous aussi ! Contribuez à l'OSS. ❤️
  • Merci à Christophe d'avoir rendu tout ça possible.

  • ... Et le soulèvement des machines, c'est pas pour demain (et encore bravo Louis pour ton nouveau poste chez HPE)

Merci !