kernel: la clausura contrastada contra un olddefconfig de verdad — 65/68 con 0 falsos positivos

Hasta acá el armador era análisis puro: la clausura decía qué debía morir y nadie lo había
contrastado con el resolvedor real. NO hace falta construir un kernel para hacerlo: lo caro
es la fase compile (35-60 min); toda la cadena del armador vive en configure y corre en
segundos.

Método: árbol 6.16.12 entero, `make defconfig` de base (el config de linux.toml NO sirve de
base: ya apaga wifi/audio/fs a mano, así que el lado disable no apagaría nada y la
predicción no se pondría a prueba — fue mi primer error), el fragmento del plan, y
olddefconfig. Verdad de campo = los símbolos que pasaron de encendidos a apagados.

  sólo depends on ....... clausura 1775  aciertos 64/68  SOBRA 0  falta 4
  + huérfanos select .... clausura 1794  aciertos 65/68  SOBRA 0  falta 3
  + comparaciones ....... clausura 1795  aciertos 65/68  SOBRA 0  falta 3

SOBRA 0 en las tres: el predictor nunca dice que muere algo que sobrevive, que es la única
dirección en la que puede equivocarse sin fabricar un ladrillo.

Dos refinamientos que salieron de la medición, cada uno con su test:
  · HUÉRFANOS DE SELECT. Un símbolo sin prompt no se marca a mano: sólo entra por select.
    Si caen todos sus selectores, cae él, aunque nadie dependa de él (caso ACPI_NHLT). Se
    exige >=1 selector: sin ninguno entra por un default, y darlo por muerto mataría media
    tabla.
  · `X = y` SÍ ES DEPENDENCIA DURA. Medio drivers/video/fbdev declara su dependencia de FB
    como `depends on (FB = y) && ARM`. Tratar toda comparación como opaca dejaba esos
    drivers fuera. `X = n` sigue fuera a propósito: con X en n es VERDADERA. De regalo, las
    fugas select sin declarar de sin-graficos cayeron de 8+ a 1.

Los 3 que faltan NO son un fallo, son otra pregunta: CRYPTO_LIB_ARC4, REGMAP y
SYSTEM_DATA_VERIFICATION tienen selectores FUERA de la clausura (PPP_MPPE, 111 usuarios más
de REGMAP…). Se quedaron sin usuarios en ESE config; encendés PPP y ARC4 vuelve.
«Inalcanzable» y «apagado ahora» no son lo mismo, y la clausura contesta la primera.

Y un bug que sólo aparece corriendo el resolvedor: el diff-back contaba como promesa
incumplida todo símbolo pedido ausente del .config. Pero Kconfig NO EMITE un símbolo cuyas
dependencias no se cumplen ⇒ un `-d WLAN` cuya raíz ya cayó simplemente no sale. Con esa
cuenta un plan perfecto se reportaba roto (2 falsos incumplidos de 16). Ahora: ausente +
se pedía apagar = éxito; ausente + se pedía encender = fallo. La corrida real sale 14
cumplidos, 0 incumplidos.

GUARDIÁN NUEVO, y hacía falta: las cuatro recetas de kernel NO son el mismo kernel — linux
y linux-metal van por 6.16.12, linux-metal-dual y linux-generic por 7.1.2. Planear una
contra el árbol de la otra calcularía clausuras sobre símbolos que ahí no existen, y
saldría SIN RUIDO. KconfigTree lee ahora su versión del Makefile de arriba y `plan` FALLA
si no coincide con la de la receta (los diagnósticos sólo avisan). Aviso de la sesión de
granja/store, verificado antes de implementarlo.

Catálogo: las dos fugas que destapó la clausura más grande quedan declaradas con motivo
(FB_SYSMEM_HELPERS_DEFERRED por HID_PICOLCD_FB; DRM_DISPLAY_DP_TUNNEL_STATE_DEBUG por
DRM_I915_DEBUG), y las notas sobre THUNDERBOLT/REISERFS_FS pasan a pasado: ya se
corrigieron en 2602218.

Runbook §4.bis: cómo probar un plan entero en 30 s en vez de 40 min.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-08-10 14:20:52 +00:00
co-authored by Claude Opus 5
parent b88930234f
commit ca9bde0251
6 changed files with 321 additions and 29 deletions
+51
View File
@@ -367,6 +367,57 @@ Un gate global habría rechazado `recipes/linux.toml`, que está sana.
pudieron mapear a ningún símbolo (built-ins de núcleo como `pcieport` o `serial8250`, cuyo nombre de
driver no coincide con el del módulo). Quedan listados como **sin comprobar**, no como aprobados.
## 12. La prueba contra un `olddefconfig` de verdad (2026-08-10)
Hasta acá todo era análisis: la clausura decía qué debía morir, y nadie lo había contrastado con el
resolvedor real. **No hace falta construir un kernel para hacerlo**: lo caro es la fase `compile`
(3560 min); la cadena entera del armador —fragmento → `scripts/config``olddefconfig`
diff-back— vive en `configure` y corre en segundos.
Método: árbol 6.16.12 extraído entero, `make defconfig` como base (que **sí** trae wifi, audio y los
fs encendidos — el config de `linux.toml` no sirve de base porque ya los apaga a mano), el fragmento
del plan, y `olddefconfig`. Ground truth = los símbolos que pasaron de encendidos a apagados.
| refinamiento | clausura | aciertos | **sobra** | falta |
|---|---|---|---|---|
| sólo `depends on` en inversa | 1775 | 64/68 | **0** | 4 |
| + huérfanos de `select` | 1794 | 65/68 | **0** | 3 |
| + comparaciones `X = y` / `X != n` | 1795 | 65/68 | **0** | 3 |
**Sobra 0 en las tres.** El predictor nunca dice que muere algo que sobrevive, que es la única
dirección en la que se puede equivocar sin fabricar un ladrillo.
### Los dos refinamientos que salieron de ahí
- **Huérfanos de `select`.** Un símbolo **sin prompt** no se puede marcar a mano: sólo entra por
`select`. Si todos sus selectores caen, él también, aunque nadie dependa de él. Era el caso de
`ACPI_NHLT`. Se exige ≥1 selector: sin ninguno entra por un `default`, y darlo por muerto mataría
media tabla.
- **`X = y` sí es una dependencia dura.** Medio `drivers/video/fbdev` declara su dependencia de `FB`
como `depends on (FB = y) && ARM`. Tratar toda comparación como opaca dejaba esos drivers fuera.
`X = n` sigue fuera a propósito: con `X` en `n` es **verdadera**. El efecto colateral fue limpiar
el ruido: las fugas `select` sin declarar de `sin-graficos` cayeron de 8+ a 1.
### Los 3 que faltan no son un fallo: son otra pregunta
`CRYPTO_LIB_ARC4`, `REGMAP` y `SYSTEM_DATA_VERIFICATION` se apagaron en ese `.config`, pero tienen
selectores **fuera** de la clausura (`PPP_MPPE`, 111 usuarios más de `REGMAP`, `MODULE_SIG_FORMAT`…).
Se quedaron sin usuarios *en esta configuración*; encendé PPP y `CRYPTO_LIB_ARC4` vuelve.
> **«Inalcanzable» y «apagado ahora» no son lo mismo.** La clausura contesta la primera, que es la
> que define un bundle. Meter los otros tres sería afirmar que el bundle los mata, y no es cierto.
### Y un bug que sólo aparece corriendo el resolvedor
El diff-back contaba como **promesa incumplida** todo símbolo pedido que no apareciera en el
`.config`. Pero Kconfig **no emite** un símbolo cuyas dependencias no se cumplen: un `-d WLAN` cuya
raíz ya cayó simplemente no sale. Con esa cuenta, un plan perfecto se reportaba roto (2 falsos
incumplidos de 16). Ahora se separa: ausente + se pedía apagar = **éxito**; ausente + se pedía
encender = **fallo**. El diff-back de la corrida real sale **14 cumplidos, 0 incumplidos**.
### El guardián que faltaba: las cuatro recetas no son el mismo kernel
`linux` y `linux-metal` van por **6.16.12**; `linux-metal-dual` y `linux-generic` por **7.1.2**.
Planear una contra el árbol de la otra calcularía clausuras sobre símbolos que ahí no existen, y
saldría sin ruido. `KconfigTree` ahora lee su propia versión del `Makefile` de arriba y `plan`
**falla** si no coincide con la de la receta (los diagnósticos sólo avisan).
### Un hallazgo de regalo
`recipes/linux.toml` apaga **`REISERFS_FS`, que no existe en 6.16.12** (upstream lo retiró). El
`scripts/config -d REISERFS_FS` es un no-op que nadie vio. Es exactamente el modo de fallo que el
+21 -2
View File
@@ -63,8 +63,8 @@ hammer kernel bundles --check # falla si envejeció respecto de este árbol
`--check` es el control de frescura y hace dos cosas:
- **Símbolos idos**: un `-d` sobre un símbolo que ya no existe es un no-op silencioso. Así se
descubrió que las cuatro recetas de kernel del repo apagan `THUNDERBOLT` (hoy `USB4`) y
`REISERFS_FS` (retirado), o sea que no apagan nada.
descubrió que las cuatro recetas de kernel del repo apagaban `THUNDERBOLT` (hoy `USB4`) y
`REISERFS_FS` (retirado), o sea que no apagaban nada. Corregido en `2602218`.
- **Fugas `select` sin declarar**: cada `select` nuevo que entra a un bundle es un símbolo que
upstream agregó y nadie revisó. Es la mitad barata de la curación del delta entre versiones, y
sale de comparar el grafo con el catálogo — sin IA.
@@ -148,6 +148,25 @@ reglas en 3182 ficheros). Lo que no pueda mapear lo lista como **sin comprobar**
como aprobado: un portón que calla lo que no miró no es un portón. Los que suelen quedar fuera son
built-ins de núcleo (`pcieport`, `serial8250`) cuyo nombre de driver no coincide con el del módulo.
## 4.bis Probar un plan SIN construir (30 s en vez de 40 min)
Lo caro es `compile`, no `configure`. La cadena entera del armador vive en `configure`, así que se
puede validar un plan completo sin pagar un build:
```sh
tar -xzf work/tarballs/linux-6.16.12.tar.gz -C work/kernel-test # el árbol ENTERO, 1,7 G
cd work/kernel-test/linux-6.16.12
python3 -c "import json;print(json.load(open('../../kernel-plans/plan.json'))['configure'])" > /tmp/cfg.sh
bash /tmp/cfg.sh # defconfig + fragmento + olddefconfig
hammer kernel diff-back --plan ../../kernel-plans/plan.json --config .config
```
Necesita `gcc`, `make`, `flex`, `bison` y `perl` en el host (`bc` sólo hace falta para compilar).
**Para medir la clausura contra la verdad de campo, la base tiene que ser `defconfig` pelado**, no
el config de `linux.toml`: esa receta ya apaga wifi/audio/fs a mano, así que el lado `disable` del
plan no apagaría nada y la predicción no se pondría a prueba. Es el error que cometí la primera vez.
## 5. Diff-back — después de construir
Sin esto la UI miente: un `-e FOO` cuya dependencia no se cumple se pierde en silencio.
+8 -2
View File
@@ -107,6 +107,7 @@ DRM_AUX_BRIDGE = "lo seleccionan los muxes USB-C (TYPEC_MUX_*): apagarlos romper
DRM_AUX_HPD_BRIDGE = "ídem, TYPEC_QCOM_PMIC / UCSI_HUAWEI_GAOKUN"
BACKLIGHT_CLASS_DEVICE = "lo seleccionan drivers de plataforma (ACPI_CMPC, SAMSUNG_Q10, USB_APPLEDISPLAY)"
DRM_DW_HDMI_I2S_AUDIO = "SND_SOC_RK3288_HDMI_ANALOG, SoC Rockchip: inerte en x86_64"
FB_SYSMEM_HELPERS_DEFERRED = "lo selecciona HID_PICOLCD_FB, una pantallita USB que es un dispositivo HID: decir «no quiero salida gráfica» no debe apagar un HID"
[bundle.contradicted_by]
pci_class = ["0300", "0380"]
@@ -116,7 +117,7 @@ title = "Sólo uso ext4"
help = "Apaga los demás sistemas de ficheros locales. Ojo con el USB de rescate: lo que no está no se monta."
disable = ["XFS_FS", "BTRFS_FS", "F2FS_FS", "JFS_FS", "GFS2_FS", "NTFS3_FS"]
close_leaks = ["NTFS_FS"]
notes = "REISERFS_FS NO va acá: upstream lo retiró y 6.16.12 ya no lo tiene. recipes/linux.toml todavía lo apaga, y ese `-d` es un no-op silencioso."
notes = "REISERFS_FS NO va acá: upstream lo retiró y 6.16.12 ya no lo tiene. Las cuatro recetas lo apagaban en vano hasta `2602218`, que quitó el flag."
[[bundle]]
id = "sin-usb"
@@ -162,7 +163,12 @@ id = "sin-thunderbolt-firewire"
title = "No necesito Thunderbolt ni FireWire"
help = "El símbolo se llama USB4 desde que upstream fundió Thunderbolt y USB4 en un solo driver."
disable = ["USB4", "FIREWIRE"]
notes = "Las CUATRO recetas de kernel apagan `THUNDERBOLT`, que 6.16.12 ya no tiene: ese `-d` es un no-op y el driver sigue entrando por el defconfig."
notes = """
Hasta el 2026-08-10 las cuatro recetas de kernel apagaban `THUNDERBOLT`, que 6.16.12 ya no tiene, y
ese `-d` era un no-op silencioso. Corregido a `USB4` en `2602218`, que lo convierte en un guardián
de verdad: si mañana la base o un `select` lo encienden, ahora sí lo apaga."""
[bundle.accept_leaks]
DRM_DISPLAY_DP_TUNNEL_STATE_DEBUG = "lo selecciona DRM_I915_DEBUG: es una perilla de depuración del driver gráfico, no hardware Thunderbolt"
[bundle.contradicted_by]
driver = ["thunderbolt"]