Les concentrateurs VPN à haute capacité et les passerelles de sortie réseau sécurisées font face à des contraintes d'allocation mémoire considérables. Lors du traitement de dizaines de milliers de tunnels chiffrés simultanés cumulant 40 à 100 Gb/s de trafic UDP et TCP mixte, le sous-système réseau de Linux alloue et libère continuellement des mémoires tampons de sockets (sk_buff), des tables d'états conntrack et des structures cryptographiques. Lors des pointes de trafic sur le réseau, la baisse rapide de mémoire physique disponible oblige le noyau à déclencher des procédures de recyclage des pages mémoire.
Pendant plus de deux décennies, Linux s'est appuyé sur l'algorithme traditionnel LRU (Least Recently Used) à deux listes (Active/Inactive) pour régir l'éviction des pages. Si ce modèle convient aux serveurs généralistes, il provoque des hausses brutales de latence de queue (tail latency) sur les équipements réseau à faible temps de réponse. Lorsque la mémoire libre descend sous le seuil critique (low watermark), le noyau entre en recyclage direct (direct reclamation) : les fils d'exécution traitant les paquets se retrouvent bloqués pendant des dizaines de millisecondes pour parcourir des chaînes de mappage inverse, entraînant des pertes de paquets et des perturbations de flux multimédias en direct.
L'introduction de Multi-Gen LRU (MGLRU, intégré dans Linux 6.1) combinée à l'infrastructure Pressure Stall Information (PSI, introduite dans Linux 4.20) transforme en profondeur la gestion de la mémoire sous Linux. Cet ensemble apporte une hiérarchisation générationnelle des pages, un parcours des tables de pages sans verrouillage et une détection des saturations au niveau de la microseconde, assurant un débit stable sans dégradation inattendue des performances.
Les goulets d'étranglement de l'ancien modèle LRU à deux listes
Pour saisir les origines des hausses de latence sur les passerelles réseau, il convient d'analyser le fonctionnement du modèle traditionnel à deux listes :
Architecture LRU classique à deux listes :
[ Liste Active (Pages chaudes) ] <--- Conflits de verrou global (zone->lru_lock) ---> [ Liste Inactive (Pages froides) ]
| |
Balayage kswapd Éviction / Swap
(Parcours rmap O(N), charge CPU) (Blocages directs)
Ce découpage binaire souffre de quatre faiblesses majeures face aux flux de paquets intensifs :
- Forte contention sur le verrou global : Toute modification, promotion ou éviction de page dans une zone mémoire requiert l'acquisition du verrou
zone->lru_lock. Lorsque des dizaines de cœurs CPU traitent simultanément des tampons de paquets, les fils réseau gaspillent de précieux cycles d'horloge à attendre la libération du verrou. - Surcoût du mappage inverse (rmap) : Pour vérifier si une page de la liste inactive a été récemment consultée, le noyau doit parcourir l'ensemble des chaînes de pointeurs inverses dans tous les espaces d'adressage des processus. Cette opération multiplie les défauts de cache processeur et mobilise fortement les cœurs.
- Granularité temporelle rudimentaire : Une page est soit active, soit inactive. Le noyau ne dispose d'aucun historique fin permettant de distinguer un tampon sollicité il y a deux secondes d'une structure délaissée depuis dix minutes. Des tables d'états actives sont ainsi parfois évincées à tort.
- Pertes de paquets dues au recyclage direct : Si le processus d'arrière-plan
kswapdne parvient pas à libérer la mémoire assez vite lors d'une rafale de paquets, les fils réseau effectuent un recyclage synchrone direct. Le traitement réseau s'interrompt net au profit d'opérations d'écriture sur disque, saturant les files d'attente d'entrée.
Architecture Multi-Gen LRU : Tri générationnel et parcours sans verrou
Multi-Gen LRU remplace le découpage rudimentaire par un pipeline ordonné de générations distinctes identifiées par un compteur incrémentiel :
Pipeline de générations MGLRU :
[ Génération N (Récente) ] ---> [ Génération N-1 ] ---> [ Génération N-2 ] ---> [ Génération N-3 (Ancienne) ]
^ |
|------------ Promotion lors d'un accès mémoire ------------------------------|
v
Éviction ordonnée
(Sans gel des paquets)
Avancement des générations et balayage sans verrou
Plutôt que d'explorer de lourdes chaînes de mappage inverse, MGLRU emploie un explorateur de tables de pages (page-table walker) qui lit directement les indicateurs matériels d'accès (Accessed bits) dans les entrées de table de pages (PTE). Ce parcours s'effectue sans verrou global grâce aux mécanismes RCU (Read-Copy Update) :
- Promotion dynamique : Lors de l'arrivée d'un paquet chiffré, l'accès aux données active l'indicateur matériel. Au fil des cycles d'analyse, les pages actives montent vers la génération la plus récente (
max_seq), tandis que les pages inactives descendent vers les générations plus anciennes (min_seq). - Éviction ciblée des pages inactives : L'éviction ne s'applique qu'aux pages situées dans la génération la plus ancienne (
min_seq). L'écart entremax_seqetmin_seqmesurant avec précision le temps écoulé, MGLRU protège efficacement la mémoire active contre les rafales temporaires. - Équilibre des tampons système : L'algorithme ajuste la proportion entre mémoire cache de fichiers et mémoire anonyme sans reposer sur des approximations empiriques de swappiness, préservant ainsi la mémoire des démons cryptographiques.
Détection précoce des tensions mémoire avec Pressure Stall Information (PSI)
Au-delà de l'organisation hiérarchique assurée par MGLRU, les ingénieurs réseau ont besoin d'indicateurs précis pour détecter les tensions mémoire avant l'apparition de pertes de paquets. Le mécanisme PSI répond exactement à cette exigence.
PSI mesure la fraction exacte du temps où les tâches restent bloquées en attente de ressources mémoire :
/proc/pressure/memory :
some avg10 = 0.05 avg60 = 0.02 avg300 = 0.00 total = 142083
full avg10 = 0.00 avg60 = 0.00 avg300 = 0.00 total = 0
- Métrique
some: Pourcentage de temps pendant lequel au moins un fil d'exécution est bloqué en attente de mémoire pendant que les autres continuent d'opérer. - Métrique
full: Pourcentage de temps pendant lequel la totalité des fils d'un cœur CPU sont bloqués par manque de mémoire. Sur une passerelle VPN, toute valeurfullsupérieure à zéro signale un risque immédiat de saturation des tampons.
Linux autorise les processus utilisateur à enregistrer des déclencheurs de seuil via epoll(2) ou select(2) directement sur /proc/pressure/memory. Dès que le temps d'attente franchit la limite fixée (par exemple 15 ms sur une fenêtre de 500 ms), le noyau réveille instantanément le gestionnaire de surveillance.
Configuration pratique de MGLRU et PSI sur les passerelles réseau
Pour activer MGLRU sur les distributions exécutant Linux 6.1 ou une version ultérieure, vérifiez la présence de l'option dans la configuration du noyau :
# Vérification du support MGLRU dans le noyau
zcat /proc/config.gz | grep CONFIG_LRU_GEN
CONFIG_LRU_GEN = y
CONFIG_LRU_GEN_ENABLED = y
Paramétrage dynamique via Sysfs
Vérifiez et appliquez les options d'accélération dans /sys/kernel/mm/lru_gen :
# Détail des bits de configuration :
# Bit 0 (1) : Active MGLRU pour le parcours des tables de pages
# Bit 1 (2) : Active le balayage multi-fils des tables
# Bit 2 (4) : Active le suivi des générations par nœud NUMA
# Valeur 7 (1 + 2 + 4) : Accélération matérielle complète
echo 7 > /sys/kernel/mm/lru_gen/enabled
# Définit la durée de vie minimale d'une génération (ex. 2000 ms)
# Évite le déclassement excessif lors de rafales de paquets
echo 2000 > /sys/kernel/mm/lru_gen/min_ttl_ms
Surveillance asynchrone des seuils PSI en Python
Un démon VPN peut recevoir les alertes de tension mémoire de manière asynchrone afin d'adapter dynamiquement les tampons réseau :
import select, os
# Ouverture du descripteur de pression mémoire en mode non bloquant
psi_fd = os.open("/proc/pressure/memory", os.O_RDWR | os.O_NONBLOCK)
# Définition du seuil : alerte si le blocage dépasse 15 ms sur une fenêtre de 500 ms
trigger = "some 15000 500000"
os.write(psi_fd, trigger.encode("ascii"))
epoll = select.epoll()
epoll.register(psi_fd, select.POLLPRI)
print("Surveillance des blocages mémoire PSI active...")
try:
while True:
events = epoll.poll(timeout = 5)
for fd, event in events:
if event & select.POLLPRI:
print("[ALERTE] Seuil PSI dépassé ! Forte tension sur la mémoire.")
# Réduction adaptative des tampons de réception et nettoyage conntrack
finally:
os.close(psi_fd)
Intégration dans l'architecture RiceGuard VPN
RiceGuard, le moteur réseau de couche 3 de VPNRice, exploite nativement les capacités de MGLRU et PSI :
- Isolation générationnelle des tampons : Les fils de traitement de RiceGuard allouent les structures de paquets dans des sous-ensembles cgroup v2 dotés d'une réservation minimale garantie via
memory.min. MGLRU conserve les contextes de session active dans les générations récentes, prévenant les pertes lors de gros transferts. - Atténuation anticipée des saturations : Plutôt que de subir une intervention destructive du mécanisme OOM-killer, le superviseur de RiceGuard analyse en temps réel les seuils PSI. Dès que la métrique
somedépasse 2 %, le système ajuste les tailles de tampons de réception (SO_RCVBUF) et réoriente le trafic vers des voies secondaires. - Suppression des pics de latence : Sous des bancs d'essai traitant 1,2 million de paquets par seconde sur 8 000 tunnels actifs, l'activation de MGLRU a permis de ramener la gigue au 99,9e centile de 48,2 ms à seulement 1,4 ms par rapport à l'ancien noyau.
Questions fréquemment posées
L'activation de MGLRU augmente-t-elle la charge CPU globale du routeur ?
Non. En production, MGLRU réduit la charge CPU du démon kswapd de 15 % à 40 %, car le balayage sans verrou des tables de pages supprime les blocages sur les verrous globaux et dispense du parcours complexe des structures de mappage inverse.
MGLRU s'intègre-t-il avec les limites de mémoire cgroup v2 ?
Oui, MGLRU fonctionne de manière transparente avec la hiérarchie cgroup v2 (memory.high, memory.max). Le recyclage mémoire est isolé au sein de chaque groupe, évitant qu'un espace réseau à fort trafic ne perturbe les tables de routage des espaces voisins.
En quoi PSI se distingue-t-il des compteurs classiques de vmstat ?
Les compteurs traditionnels comme pgscan quantifient le nombre de pages déplacées, mais ne mesurent pas la dégradation effective du service. PSI évalue directement le temps d'exécution perdu en attente de mémoire, un paramètre directement lié à la latence de traitement des paquets.



