Executive Summary

Volevo una cosa semplice: spostare il desktop di Linux sulla grafica integrata Intel e lasciare tutta la VRAM delle mie due GPU Pascal all’AI.
Appena ho attivato la iGPU nel BIOS, però, il driver NVIDIA ha smesso di caricarsi con un errore criptico: ZSTD-decompression failed with status 20.
Dopo due ipotesi sbagliate ho trovato il colpevole: con la iGPU attiva, l’IOMMU (VT-d) faceva sì che alcune letture dal disco a inizio boot arrivassero in RAM come pagine piene di zeri.
Fix: intel_iommu=off sulla riga del kernel.


Il contesto

La mia workstation è un Haswell del 2014 (i7-4790K su MSI Z97S SLI Krait) con una GTX 1080 Ti e una GTX 1070. Insieme fanno 19 GB di VRAM, che uso per LLM MoE e ComfyUI (ne parlo in 61.01-resurrezione-gpu-pascal-llm e 61.04-dynamic-comfyui-router-openwebui).

Il problema: Xorg, Cinnamon e il browser si mangiavano oltre 1,3 GB sulla 1080 Ti. Con un modello da 35B caricato, restavano poche centinaia di MB prima dell’OOM.

L’idea era far gestire il desktop alla Intel HD 4600 integrata nella CPU e lasciare le NVIDIA senza uscite video attive.


Il sintomo

Nel BIOS MSI la iGPU si spegne da sola se ci sono schede PCIe. Per tenerla accesa va attivato IGD Multi-Monitor. Fatto questo:

  • la iGPU compare (00:02.0, driver i915) e il logo di boot appare su entrambi i monitor;
  • poi, nei log del kernel, a raffica:
    ZSTD-decompression failed with status 20
  • il modulo nvidia.ko.zst non si carica, nvidia-persistenced fallisce e il desktop si pianta.

Spegnendo la iGPU nel BIOS, tutto torna a funzionare. Lo status 20 di zstd è corruption_detected: i dati compressi che il kernel sta leggendo sono corrotti.


Due ipotesi sbagliate (e perché le racconto)

1. “È la RAM nella zona rimappata”

Confrontando la mappa della memoria (e820) con iGPU spenta e accesa ho notato una cosa interessante. Con la iGPU attiva, il “buco” di indirizzi riservato ai dispositivi PCI sotto i 4 GB si allarga, e circa 270 MB di RAM vengono rimappati in una zona di indirizzi fisici che altrimenti non viene mai usata.

Sembrava perfetto: un banco difettoso proprio lì sarebbe stato invisibile finché la iGPU era spenta. Ho fatto il boot con mem=33776M per escludere quella zona…
Errori identici. Ipotesi bocciata.

2. “È la memoria video condivisa troppo grande”

La iGPU si riservava 512 MB di RAM (DVMT). Ridotti a 128 MB… errori identici. Del resto la mappa mostrava che la stolen memory viene presa dalla RAM bassa e non cambia la zona rimappata.

Avrei potuto andare avanti con memtest per ore. Invece mi sono fermato su un dettaglio che avevo sottovalutato: il caricamento falliva sempre, al 100%. Una cella di RAM difettosa a caso farebbe fallire solo ogni tanto.


La prova decisiva: disco o cache?

Ho avviato in modalità solo testo (systemd.unit=multi-user.target), così il desktop non poteva bloccarsi, e ho letto lo stesso file in due modi:

F=/lib/modules/$(uname -r)/updates/dkms/nvidia.ko.zst
sha256sum < "$F"                                       # passa dalla page cache
dd if="$F" iflag=direct bs=1M status=none | sha256sum  # O_DIRECT: salta la cache
Letturasha256
Diretta dal disco (O_DIRECT)941a0199…✅ giusto
Dalla page cache riempita al boot0a9fd182…❌ diverso

Il file su disco era integro. La copia in RAM no. E il confronto byte per byte, a blocchi da 1 MiB, mostrava uno schema tutt’altro che casuale:

====000000000000000000000000000000000000000000000000000000000000000000000000000000=

Ogni carattere è 1 MiB: = vuol dire uguale, 0 vuol dire pieno di zeri. I primi 4 MiB e la coda erano giusti, gli ~84 MiB in mezzo erano tutti zeri.

Questo non è il comportamento di una RAM guasta, che produce bit sbagliati sparsi. È il comportamento di una lettura dal disco che risulta completata ma i cui dati non sono mai arrivati nelle pagine giuste. Il kernel le aveva comunque marcate come valide in cache, quindi ogni nuovo tentativo di caricare il modulo rileggeva gli stessi zeri.

Altra conferma: dopo aver buttato via il file dalla cache (dd iflag=nocache count=0, senza nemmeno servire sudo), la rilettura era corretta e sudo modprobe nvidia funzionava. Con la iGPU accesa! Il problema c’era solo all’inizio del boot.

flowchart TD
    A["Errore ZSTD status 20<br/>solo con iGPU attiva"] --> B{"RAM nella zona<br/>rimappata?"}
    B -->|"mem=33776M → errori uguali"| C{"DVMT troppo<br/>grande?"}
    C -->|"128 MB → errori uguali"| D{"Disco o cache?"}
    D -->|"O_DIRECT ok, cache no"| E["~84 MiB di pagine a zero<br/>lette a inizio boot"]
    E --> F{"IOMMU?"}
    F -->|"intel_iommu=off → 0 errori"| G["✅ Risolto"]

Il colpevole: l’IOMMU

L’IOMMU (Intel VT-d) è il componente che traduce gli indirizzi usati dai dispositivi per il DMA, cioè quando un dispositivo scrive direttamente in RAM, come fa il controller SATA leggendo un file. Il kernel di Ubuntu lo attiva di default. Con la iGPU accesa entra in gioco anche l’unità DMAR dedicata alla grafica, e nei log compare VT-d active for gfx access.

Su questa combinazione (Haswell, chipset Z97, BIOS del 2016, kernel 7.0) qualcosa nella traduzione degli indirizzi all’inizio del boot va storto: la lettura risulta completata, ma i dati finiscono altrove.

Il test definitivo, con la iGPU accesa:

intel_iommu=off
PrimaDopo
Errori ZSTDda 7 a 19 a ogni boot0
Driver NVIDIAnon si caricasi carica da solo
iGPU Intel (i915)caricatacaricata

L’IOMMU serve soprattutto per assegnare dispositivi a macchine virtuali (VFIO/passthrough). Io non lo faccio, quindi spegnerlo non mi costa nulla.


Cosa mi porto a casa

  • Lo schema della corruzione dice molto. Bit sparsi fanno pensare alla RAM, blocchi interi di zeri fanno pensare al DMA o all’IOMMU.
  • O_DIRECT contro page cache è un test da 10 secondi che separa “il disco è rotto” da “la copia in RAM è rotta”. Lo userò ancora.
  • Prima di correggere, tagliare il problema a metà. Con le prime due ipotesi stavo cercando di correggere la cosa sbagliata. Il test disco-o-cache ha diviso lo spazio delle cause in due.
  • La modalità testo (systemd.unit=multi-user.target) permette di indagare anche quando il desktop non parte.

Il prossimo passo è spostare davvero Xorg sulla Intel e capire come gestire il monitor ultrawide, visto che la scheda madre ha solo HDMI 1.4. Ma questa è un’altra storia.