Nei primi mesi del 2026 MongoDB ha smesso di funzionare con i kernel Linux 6.19+ e questo ha portato a un nuovo kernel-drama.
L’ho scoperto per caso dopo un aggiornamento di sistema provando ad avviare il mio solito container per gli esperimenti con MongoDB e… exit 139!
La cause del problema era meno banale di quanto pensassi e ho dovuto indagare un po’ per comprenderla e farlo funzionare di nuovo.
Quella che segue è la cronistoria della vicenda.
2013 -> 2018 - Le Restartable Sequences (RSEQ) arrivano nel Kernel
Alla Linux Plumbers Conf 2013 di New Orleans gli sviluppatori Paul Turner e Andrew Hunter di Google mostrano una soluzione che stavano già sperimentando all’interno dell’azienda: LPC - PerCpu Atomics. Si tratta di una funzionalità che avevano aggiunto ai loro kernel Linux per accedere a dei dati condivisi a livello di singola CPU da userspace senza necessità di usare lock o altre istruzione atomiche poco performanti. Negli anni successivi vengono presentate due diverse serie di patch per aggiungere la funzionalità che poi convergono su una, portata avanti da Mathieu Desnoyers di EfficiOS, con l’obiettivo di includerla nella versione 4.15 del Kernel di inizio 2018. Sfortunatamente Linus blocca le patch per mancanza di casi d’uso reali e relative metriche. Alla fine, dopo qualche altro mese, tutto il necessario viene raccolto e la nuova serie di patch riesce a portare RSEQ nel Kernel 4.18 nell’agosto dello stesso anno. Il riassunto dei cinque anni di lavoro necessari a raggiungere questo risultato li ha raccontati lo stesso Mathieu Desnoyers nell’articolo “The 5-year journey to bring restartable sequences to Linux”.
2020 - TCMalloc
Nel 2020 Google rilascia la prima versione della sua libreria TCMalloc.
TCMalloc is Google’s customized implementation of C’s
malloc()and C++’s operatornewused for memory allocation within our C and C++ code… It provides benefits such as performance scales with highly parallel applications. TCMalloc is designed to be more efficient at scale than other implementations.
I benefici per le applicazioni con alto livello di parallelismo sono raggiunti anche grazie all’uso di una cache condivisa a livello di CPU. Questa cache è implementata utilizzando proprio le Restartable Sequences.
Il prefisso “TC” in TCMalloc si riferisce proprio al Thread Caching che nelle sue prime versione rappresentava l’elemento distintivo della libreria. Nonostante i cambiamenti che ha subito nel tempo non hanno cambiato nome.
2024 -> 2026 - MongoDB 8 e TCMalloc
A ottobre 2024, MongoDB 8 ha aggiornato la libreria TCMalloc di Google così da usare la sua cache per-CPU invece che per-thread, aumentando così le performance.
I miglioramenti di performance dichiarati rispetto alla versione 7 sono:
- Up to 36% better read throughput.
- Up to 32% better performance for typical web applications.
- Up to 20% faster concurrent writes during replication.
Fino a oggi (aprile 2026) sono state rilasciate molte altre versioni patch e alcune minor fino alla 8.2 (e la 8.3 è in arrivo).
Glib vs TCMalloc
TCMalloc è stata una delle prime librerie a supportare e utilizzare RSEQ fin dal Kernel 4.18.
Glibc ne ha aggiunto il supporto dal rilascio 2.35 (febbraio 2022) ma c’è un problema: se nello stesso progetto si utilizza Glibc con TCMalloc, quest’ultimo non funzionerà come previsto.
Infatti, Glibc registrerà la struttura (struct) da utilizzare con RSEQ prima di TCMalloc, e allora TCMalloc non sarà più in grado di usare RSEQ.
Senza RSEQ, TCMalloc utilizza la cache per-thread e non più quella per-CPU.
Le applicazioni come MongoDB evitano questa situazione disabilitando la specifica funzionalità di Glibc con una variabile d’ambiente GLIBC_TUNABLES=glibc.pthread.rseq=0, lasciando quindi attiva quella di TCMalloc.
Questo sistema ha funzionato per anni e continua a funzionare oggi ma comprendere la causa dell’incompatibilità ci avvicina al problema di oggi.
La causa dell’incompatibilità
L’incompatibilità tra RSEQ di Glibc e TCMalloc è nota fin dall 2022 e, nel corso degli anni, lo stesso sviluppatore che ha portato RSEQ nel Kernel si è offerto di aiutare TCMalloc fornendo informazioni specifiche sul problema e come risolverlo.
Si tratta del campo cpu_id_start in sola lettura esposto dal Kernel in userspace e che TCMalloc usa in modo “creativo”.
TCMalloc non solo scrive all’interno di quel campo, ma si aspetta anche che il Kernel ne aggiorni il valore quando:
When a thread is rescheduled the kernel overwrites
cpu_id_startwith the current CPU number, which gives us the signal that the cached address is not valid anymore.
Nonostante, di fatto, il Kernel aggiorni quel campo proprio come TCMalloc si aspetta, la specifica di RSEQ non ha mai dichiarato questo comportamento. TCMalloc ha quindi sfruttato una funzionalità non documentata del Kernel, e questo è quello che rende TCMalloc incompatibile con Glibc. Inoltre, nonostante il funzionamento di TCMalloc sia noto da anni, Google non ha fatto nulla per renderlo conforme alla specifica che invece Glibc rispetta. Attivando la modalità di debug del Kernel per RSEQ, TCMallc smette di funzionare perché questa modalità effettua dei controlli che, per qualche motivo non sono presenti a runtime.
2026 - Linux 6.19, Glibc e RSEQ perf
Quindi, per anni, il campo cpu_id_start è stato aggiornato dal Kernel proprio come serviva a TCMalloc, anche se questo non era il comportamento previsto.
Nessuno se n’è accorto perché RSEQ era utilizzato praticamente solo da Google e dalla sua TCMalloc.
Quando però Glibc ha iniziato a supportare RSEQ, sono emersi problemi di performance e gli sviluppatori del Kernel hanno iniziato a lavorare a una riscrittura del codice di RSEQ per superare l’implementazione iniziale.
TCMalloc faceva affidamento su delle condizioni specifiche che si verificavano.
To understand [..] when a thread is rescheduled to another CPU, we overlap the top 4 bytes of the cached address with
__rseq_abi.cpu_id_start. When a thread is rescheduled the kernel overwritescpu_id_startwith the current CPU number, which gives us the signal that the cached address is not valid anymore. To distinguish the high part of the cached address from the CPU number, we set the top bit in the cached address, real CPU numbers (<2^31) do not have this bit set. ref.
Che però, con la riscrittura di RSEQ, sono state cambiate perché non conformi con l’ABI (Application Binary Interface) prevista inizialmente.
When addressing the performance issues of RSEQ the unconditional update stopped to exist under the valid assumption that the kernel has only to satisfy the guaranteed ABI properties, especially when they are enforcable by RSEQ debug. ref.:
A gennaio 2026 l’incompatibilità imminente con la nuova versione del Kernel era stata segnalata proprio da uno sviluppatore di Google che se n’era accorto dai test automatici interni. La risposta degli sviluppatori del Kernel però è negativa: “lo sapevate da anni e non avete fatto nulla” e “avete deliberatamente abusato dell’implementazione di RSEQ per i vostri interessi”. Inoltre, nello stesso thread sulla mailing list viene ricordato che le modifiche fatte al Kernel sono conformi alla specifica codificata nei test di RSEQ:
The correctness of these changes has been validated by the rseq selftests and I don’t see how that commit would violate the guaranteed ABI.
L'8 febbraio 2026 il Kernel 6.19 viene rilasciato includendo le modifiche a RSEQ.
Effetto domino
Il 20 febbraio 2026 compare su GitHub una segnalazione intitolata “Mongod hard-crashes exactly every 30 seconds (SIGSEGV)” in cui viene descritto questo strano problema senza un’apparente spiegazione né log specifici. Nelle settimane successive vengono creati vari ticket su Jira di MongoDB mentre si capisce che si tratta di un problema legato a delle modifiche che riguardano Glibc, TCMalloc, RSEQ e i Kernel recenti. Anche progetti esterni come Arch Linux si accorgono del problema ma trovano riscontro nel thread di gennaio 2026 del Kernel che si tratta di una questione di TCMalloc: non è quindi un problema del Kernel.
Solo dopo un mese, il 13 marzo, viene aperto un ticket nel progetto TCMalloc per segnalare il problema con i Kernel 6.19+ ma la prima risposta di un utente è la celebre citazione di Linus:
WE DO NOT BREAK USERSPACE!
Se TCMalloc ha funzionato per anni e ora una modifica al Kernel lo sta impedendo, è il Kernel che deve fare un passo indietro.
Un problema e una soluzione
Visto l’immobilismo di TCMalloc, il 22 aprile, uno sviluppatore di MongoDB scrive nella mailing list del Kernel spiegando il problema. Il punto centrale è quello che ormai conosciamo:
It breaks TCMalloc specifically by failing to overwrite the
cpu_id_startfield at points where it was relied on for correctness.
Inoltre aggiunge che la soluzione è semplice:
It would be sufficient for TCMalloc’s internal usage if every time the kernel nulled out
rseq_cs, it also wrote the cpu id tocpu_id_start. That should be essentially free since you are already writing to the same cache line.
A queste sue affermazioni seguono le risposte degli sviluppatori del Kernel che ricordano come TCMalloc conoscesse da anni il problema, che erano stati avvertiti più volte, …
All’alba guarda a Est
Dopo una prima ondata di risposte negative da parte degli sviluppatori del Kernel, Linus Torvalds è subentrato nella discussione ammonendoli.
We will have to fix this. It clearly violates our regression rules.
The only “ABI guarantee” is what people actually see and use, not some debug option that wasn’t enabled. If that rule was actually an important part of the ABI, it shouldn’t have been a debug thing.
Inoltre, invece di criticare il comportamento di TCMalloc che gli altri stavano additando come “abuso” di RSEQ, lo ha difeso:
That’s how clever hacks work - they take advantage of things past their design parameters. “If it works, it’s not stupid”.
Alla fine ha ricordato a tutti la regola numero uno del Kernel:
There is one major rule in kernel development, and it is “we don’t break user space”.
Quindi o il funzionamento precedente viene ripristinato o si dovrà trovare il modo di adeguare il nuovo senza creare problemi a TCMalloc.
In attesa
Ora non ci resta che aspettare la nuova versione del Kernel con la correzione o rimanere a versioni <6.19.
Nel frattempo si può usare la variabile d’ambiente GLIBC_TUNABLES="glibc.pthread.rseq=1" che, attivando RSEQ di Glibc, ha come effetto collaterale quello di disabilitare RSEQ di TCMalloc, facendo perdere però performance a MongoDB (infatti non è una soluzione ufficialmente supportata).