Skip to content

[Nexthop][feature] fboss-image: root filesystem headroom guard before platform_manager - #1892

Open
sazeed-nexthop wants to merge 1 commit into
meta:mainfrom
nexthop-ai:sazeed.disk-guard
Open

sazeed-nexthop wants to merge 1 commit into
meta:mainfrom
nexthop-ai:sazeed.disk-guard

Conversation

@sazeed-nexthop

Copy link
Copy Markdown

Pre-submission checklist

  • I've ran the linters locally and fixed lint errors related to the files I modified in this PR. You can install the linters by running pip install -r requirements-dev.txt && pre-commit install
  • pre-commit run

Summary

Companion to #1890. That PR makes platform_manager survive an unreadable rpmdb. This one keeps headroom on / before platform_manager starts, so that rpm, dnf and the rest of the stack can run at all on a full-disk boot. The two are independent and can land in either order.

Problem

When a switch running the distro image reboots with / full, the FBOSS stack does not come back:

  • rpm's sqlite backend runs a write transaction on open. Measured on a Wedge800BNHP: rpm -q and dnf list --installed work at 128 KB free and die with SIGBUS (exit 135) at 64 KB.
  • platform_manager queries and installs the BSP RPM at start, fails, and is restarted until systemd gives up (StartLimitBurst=5). With no /run/devmap, led_service and qsfp_service crash-loop behind it, each restart dumping core onto the full disk.
  • Nothing in the image frees space before the stack starts, and btrfs (the root filesystem the image uses) has no root-reserved blocks like ext4's 5%. Once the disk is full, root daemons have no headroom either, and the box stays down until someone frees space by hand.

Change

A small oneshot service, fboss_disk_guard, runs before platform_manager:

  • If / has at least MIN_FREE_KB free (default 100 MB), it exits. If the ballast file is missing and at least 10x the threshold is free, it creates it first.
  • Otherwise it logs FBOSS_ALERT <event:low_disk_space>, then frees things the image owns, in order, stopping at the first step that gets back above the threshold:
    1. journalctl --vacuum-size=16M
    2. systemd coredumps in /var/lib/systemd/coredump
    3. rotated logs under /var/log (*.gz, *.N, *.old)
    4. the ballast, a preallocated file at /var/lib/fboss/.ballast sized to the threshold
  • It logs low_disk_space_recovered with the step that worked, or low_disk_space_unrecovered, and always exits 0 so platform_manager starts regardless.

The ballast is what makes recovery deterministic. Steps 1 to 3 free whatever happens to be there, which may be nothing if the disk was filled by something else. The ballast is space reserved up front that can always be given back. It is effectively the btrfs substitute for ext4's reserved blocks.

Files

  • root_files/usr/local/bin/fboss_disk_guard.sh: the script. It re-polls df for up to 10 s after each step, because btrfs releases unlinked extents asynchronously. It never writes to disk itself, and it only touches the paths listed above. There is no dnf step, since dnf cannot run on a full disk.
  • root_files/usr/lib/systemd/system/fboss_disk_guard.service: Type=oneshot, RemainAfterExit=yes, Before=platform_manager.service.
  • platform_manager.service: adds Wants= and After=fboss_disk_guard.service. Because of Wants=, systemctl disable alone does not stop the guard; systemctl mask does.
  • config.sh: systemctl enable fboss_disk_guard.service.

Test Plan

pre-commit run passes, including shellcheck and shfmt on the new script.

On hardware, a Wedge800BNHP (Tomahawk5, kernel 6.11) on a freshly installed distro image. / was filled to 64 KB free with fallocate and dd. Coredumps and the journal were cleared beforehand, so the ballast was the only thing left to free. Then the switch was rebooted:

without guard with guard
guard log n/a Recovered 64KB -> 102464KB via: rm -f /var/lib/fboss/.ballast
platform_manager 5 aborts, systemd gives up active, 0 restarts
led_service / qsfp_service / agents crash-loop active
/run/devmap/xcvrs missing 66 entries
platform cores this boot 5+ 0
free on / after boot 64 KB 98 MB

The same result was seen with the guard combined with #1890: the guard recovers first, and platform_manager never sees the failure.

On a healthy boot with ample free space, the guard creates the 100 MB ballast once and exits without an alert.

🤖 Generated with Claude Code

…_manager

On a boot with / full, rpm and dnf cannot open the rpmdb (rpm's sqlite
backend runs a write transaction on open and dies with SIGBUS below
~128 KB free), so platform_manager, and everything ordered after it,
fails. Nothing in the image frees space before the stack starts, and
btrfs has no root-reserved blocks, so root daemons get no headroom once
the disk fills.

Add fboss_disk_guard.service, a oneshot ordered before
platform_manager.service (which Wants= and After= it). When free space
on / is below MIN_FREE_KB (100 MB) it logs an FBOSS_ALERT and frees, in
order, the journal (vacuum to 16 MB), systemd coredumps and rotated logs
under /var/log, then deletes a preallocated ballast file
(/var/lib/fboss/.ballast, sized to the threshold). It re-polls df after
each step because btrfs releases unlinked extents asynchronously, and
stops at the first step that recovers. On a healthy boot with at least
10x the threshold free it recreates the ballast. It always exits 0 and
never writes to disk itself.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@sazeed-nexthop
sazeed-nexthop requested a review from a team as a code owner November 2, 2026 06:14
@meta-cla meta-cla Bot added the CLA Signed label Nov 2, 2026
@github-actions github-actions Bot added nexthop project:feature FBOSS project: feature labels Nov 2, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant