Stop kernel upgrades writing entries that cannot boot
All checks were successful
build image / build (push) Successful in 39m19s
All checks were successful
build image / build (push) Successful in 39m19s
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-<kver>. 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 <machine-id>-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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011XgGF5wfxLDAybVnNz6eNQ
This commit is contained in:
@@ -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
|
`95-c630-devicetree.install` never runs and the boot entry gets **no
|
||||||
`devicetree` line** — which on this machine means it can never boot,
|
`devicetree` line** — which on this machine means it can never boot,
|
||||||
because the UEFI supplies no device tree.
|
because the UEFI supplies no device tree.
|
||||||
4. `dnf` reports success. Nothing retries, and later `dnf upgrade` runs look
|
4. `dnf` reports success — `kernel-install` is invoked from kernel-core's
|
||||||
clean because they no longer touch the kernel.
|
`%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-<kver>` 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 `<machine-id>-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
|
So an update can leave a kernel that is installed, listed in the boot menu, and
|
||||||
unbootable. To check whether it actually completed:
|
unbootable. To check whether it actually completed:
|
||||||
|
|||||||
@@ -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
|
# keeps it from crash-looping (see docs/firmware.md) before the rmtfs drop-in
|
||||||
# that tames it has had a chance to start.
|
# that tames it has had a chance to start.
|
||||||
omit_drivers+=" qcom_q6v5_pas qcom_q6v5_mss "
|
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 <machine-id>-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"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
# BLS snippets have a `devicetree` key for exactly this, but nothing in the
|
# 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
|
# stock toolchain populates it — which means a plain `dnf update kernel` would
|
||||||
# otherwise produce an unbootable entry. This hook runs after 20-grub.install
|
# 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
|
set -eu
|
||||||
|
|
||||||
@@ -14,20 +14,45 @@ COMMAND="${1:?}"
|
|||||||
KERNEL_VERSION="${2:?}"
|
KERNEL_VERSION="${2:?}"
|
||||||
|
|
||||||
DTB_REL="@DEVICE_DTB@"
|
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
|
case "$COMMAND" in
|
||||||
add)
|
add)
|
||||||
dtb_path="/dtb-${KERNEL_VERSION}/${DTB_REL}"
|
if ! BOOT_ROOT=$(find_boot_root); then
|
||||||
if [ ! -e "${BOOT_ROOT}${dtb_path}" ]; then
|
echo "95-c630-devicetree: found no boot root holding both loader/entries" \
|
||||||
echo "95-c630-devicetree: ${BOOT_ROOT}${dtb_path} is missing;" \
|
"and dtb-${KERNEL_VERSION}/${DTB_REL}; leaving boot entries alone" \
|
||||||
"leaving boot entries alone rather than writing a bad one" >&2
|
"rather than writing a bad one. The resulting entry will NOT boot" \
|
||||||
|
"— see docs/install.md" >&2
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
dtb_path="/dtb-${KERNEL_VERSION}/${DTB_REL}"
|
||||||
|
|
||||||
shopt -s nullglob
|
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
|
# Drop any stale devicetree line, then append the current one. Doing it
|
||||||
# in that order makes the hook idempotent across re-installs.
|
# in that order makes the hook idempotent across re-installs.
|
||||||
tmp="${entry}.c630.$$"
|
tmp="${entry}.c630.$$"
|
||||||
@@ -40,7 +65,12 @@ add)
|
|||||||
remove)
|
remove)
|
||||||
# The build writes its own bootstrap entry under a fixed name, which
|
# The build writes its own bootstrap entry under a fixed name, which
|
||||||
# kernel-install would not otherwise clean up when the kernel goes away.
|
# 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
|
esac
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user