From fc4d7d7ceb9750013d994d1679527963a61f4b8a Mon Sep 17 00:00:00 2001 From: rob thijssen Date: Fri, 14 Aug 2026 16:54:41 +0300 Subject: [PATCH] Stop kernel upgrades writing entries that cannot boot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simulating an upgrade — running kernel-core's %posttrans command by hand — turned up two faults that had nothing to do with the full /boot, and would have broken every future kernel install on their own. 95-c630-devicetree trusted $KERNEL_INSTALL_BOOT_ROOT. `kernel-install inspect` reports "Layout: other, Boot Root: /boot/efi" here, because the ESP is all it recognises, while Fedora's 20-grub.install writes BLS entries to /boot/loader/entries regardless. So the hook looked for the device tree on the ESP, did not find it, and skipped the patch — leaving an entry with no devicetree line, which on this machine cannot boot. Paths in a BLS entry are relative to the filesystem holding the entry, so the only correct boot root is the one carrying both loader/entries and dtb-. Search for that instead. dracut-config-rescue sets dracut_rescue_image=yes, so each install also built a second ~210 MiB initramfs, which is what refilled /boot straight after it was cleared. That image cannot boot this machine either: the rescue entry is -0-rescue.conf, carrying no kernel version, so the devicetree hook never matches it. Turned off. Verified by rerunning the simulation: both kernels now have a devicetree line and a present initrd, no rescue image is generated, snd-soc-wsa881x is rebuilt automatically, and /boot holds at 72%. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011XgGF5wfxLDAybVnNz6eNQ --- docs/install.md | 26 +++++++++- overlay/etc/dracut.conf.d/10-c630.conf | 9 ++++ .../install.d/95-c630-devicetree.install.in | 48 +++++++++++++++---- 3 files changed, 72 insertions(+), 11 deletions(-) diff --git a/docs/install.md b/docs/install.md index f9dc9b1..54e5d6f 100644 --- a/docs/install.md +++ b/docs/install.md @@ -142,8 +142,30 @@ If `/boot` does fill up, the failure is quiet and genuinely dangerous: `95-c630-devicetree.install` never runs and the boot entry gets **no `devicetree` line** — which on this machine means it can never boot, because the UEFI supplies no device tree. -4. `dnf` reports success. Nothing retries, and later `dnf upgrade` runs look - clean because they no longer touch the kernel. +4. `dnf` reports success — `kernel-install` is invoked from kernel-core's + `%posttrans` as `... || exit 0`, which swallows the failure. Nothing + retries, and later `dnf upgrade` runs look clean because they no longer + touch the kernel. + +Two things made that worse, both fixed, both worth knowing if you are reading +an older image: + +**The devicetree hook used to skip silently even with space to spare.** It +trusted `$KERNEL_INSTALL_BOOT_ROOT`, and on this machine `kernel-install +inspect` reports `Layout: other, Boot Root: /boot/efi` — it recognises only +the ESP. Fedora's `20-grub.install` ignores that and writes entries to +`/boot/loader/entries` anyway, so the hook went looking for the device tree on +the ESP, did not find it, and left the entry alone. The paths inside a BLS +entry are relative to the filesystem holding the entry, so the hook now +searches for the root that has both `loader/entries` and `dtb-` instead +of being told. + +**The rescue image refilled `/boot` after every cleanup.** `dracut-config-rescue` +sets `dracut_rescue_image=yes`, so each kernel install built a *second* +~210 MiB initramfs. It could never boot this machine either: the rescue entry +is named `-0-rescue.conf` and carries no kernel version, so the +devicetree hook does not match it. `dracut_rescue_image="no"` in +`/etc/dracut.conf.d/10-c630.conf` turns it off. So an update can leave a kernel that is installed, listed in the boot menu, and unbootable. To check whether it actually completed: diff --git a/overlay/etc/dracut.conf.d/10-c630.conf b/overlay/etc/dracut.conf.d/10-c630.conf index 78e915b..9e9a557 100644 --- a/overlay/etc/dracut.conf.d/10-c630.conf +++ b/overlay/etc/dracut.conf.d/10-c630.conf @@ -47,3 +47,12 @@ install_items+=" /usr/lib/firmware/qcom/a630_sqe.fw.xz /usr/lib/firmware/qcom/a6 # keeps it from crash-looping (see docs/firmware.md) before the rmtfs drop-in # that tames it has had a chance to start. omit_drivers+=" qcom_q6v5_pas qcom_q6v5_mss " + +# No rescue image. dracut-config-rescue sets dracut_rescue_image=yes, so every +# kernel install builds a second ~210 MiB initramfs — on a /boot this size that +# is most of the partition, and it is what refills it after a cleanup. The +# image it produces cannot boot this machine anyway: the rescue entry is +# written as -0-rescue.conf, which does not carry the kernel +# version, so 95-c630-devicetree never matches it and it gets no devicetree +# line. A 210 MiB entry that is guaranteed not to boot is worse than no entry. +dracut_rescue_image="no" diff --git a/overlay/etc/kernel/install.d/95-c630-devicetree.install.in b/overlay/etc/kernel/install.d/95-c630-devicetree.install.in index 45268d2..1d89703 100755 --- a/overlay/etc/kernel/install.d/95-c630-devicetree.install.in +++ b/overlay/etc/kernel/install.d/95-c630-devicetree.install.in @@ -6,7 +6,7 @@ # BLS snippets have a `devicetree` key for exactly this, but nothing in the # stock toolchain populates it — which means a plain `dnf update kernel` would # otherwise produce an unbootable entry. This hook runs after 20-grub.install -# and patches the entry it just wrote. +# and 90-loaderentry.install, both of which write the entry, and patches it. set -eu @@ -14,20 +14,45 @@ COMMAND="${1:?}" KERNEL_VERSION="${2:?}" DTB_REL="@DEVICE_DTB@" -BOOT_ROOT="${KERNEL_INSTALL_BOOT_ROOT:-/boot}" -ENTRIES_DIR="${BOOT_ROOT}/loader/entries" + +# Do NOT trust $KERNEL_INSTALL_BOOT_ROOT. On this machine kernel-install +# decides the boot root is /boot/efi — `kernel-install inspect` reports +# "Layout: other, Boot Root: /boot/efi" — because the ESP is the only thing it +# recognises. Fedora's 20-grub.install ignores that and writes BLS entries to +# /boot/loader/entries anyway. Believing kernel-install meant looking for the +# device tree on the ESP, not finding it, and skipping the patch, so every +# kernel upgrade produced an entry with no devicetree line: installed, listed +# in the menu, and unable to boot. +# +# The paths inside a BLS entry are relative to the filesystem the entry itself +# lives on, so the only correct boot root is the one holding both the entries +# directory and the device tree. Look for that rather than being told. +find_boot_root() { + local cand + for cand in "${KERNEL_INSTALL_BOOT_ROOT:-}" /boot /efi /boot/efi; do + [ -n "$cand" ] || continue + [ -d "${cand}/loader/entries" ] || continue + [ -e "${cand}/dtb-${KERNEL_VERSION}/${DTB_REL}" ] || continue + printf '%s\n' "$cand" + return 0 + done + return 1 +} case "$COMMAND" in add) - dtb_path="/dtb-${KERNEL_VERSION}/${DTB_REL}" - if [ ! -e "${BOOT_ROOT}${dtb_path}" ]; then - echo "95-c630-devicetree: ${BOOT_ROOT}${dtb_path} is missing;" \ - "leaving boot entries alone rather than writing a bad one" >&2 + if ! BOOT_ROOT=$(find_boot_root); then + echo "95-c630-devicetree: found no boot root holding both loader/entries" \ + "and dtb-${KERNEL_VERSION}/${DTB_REL}; leaving boot entries alone" \ + "rather than writing a bad one. The resulting entry will NOT boot" \ + "— see docs/install.md" >&2 exit 0 fi + dtb_path="/dtb-${KERNEL_VERSION}/${DTB_REL}" + shopt -s nullglob - for entry in "${ENTRIES_DIR}"/*-"${KERNEL_VERSION}".conf; do + for entry in "${BOOT_ROOT}/loader/entries"/*-"${KERNEL_VERSION}".conf; do # Drop any stale devicetree line, then append the current one. Doing it # in that order makes the hook idempotent across re-installs. tmp="${entry}.c630.$$" @@ -40,7 +65,12 @@ add) remove) # The build writes its own bootstrap entry under a fixed name, which # kernel-install would not otherwise clean up when the kernel goes away. - rm -f "${ENTRIES_DIR}/c630-${KERNEL_VERSION}.conf" + # The device tree is likely already gone here, so the add-time search does + # not apply; just look wherever entries live. + for cand in "${KERNEL_INSTALL_BOOT_ROOT:-}" /boot /efi /boot/efi; do + [ -n "$cand" ] || continue + rm -f "${cand}/loader/entries/c630-${KERNEL_VERSION}.conf" + done ;; esac