Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Buildroot/board/FOG/FOS/rootfs_overlay/etc/fstab
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
proc /proc proc defaults 0 0
devpts /dev/pts devpts defaults,gid=5,mode=620 0 0
tmpfs /dev/shm tmpfs mode=0777 0 0
tmpfs /tmp tmpfs mode=1777 0 0
# size= is the default only: /etc/init.d/S00tmpsize resizes /tmp from
# ramdisk_size= (FOG_KERNEL_RAMDISK_SIZE). It caps anything only because the
# kernel builds CONFIG_TMPFS; without it "tmpfs" is ramfs and ignores size=.
tmpfs /tmp tmpfs mode=1777,size=256m 0 0
tmpfs /run tmpfs mode=0755,nosuid,nodev 0 0
sysfs /sys sysfs defaults 0 0
# Type is efivarfs, NOT efivars: "efivars" was the old CONFIG_EFI_VARS sysfs
Expand Down
50 changes: 50 additions & 0 deletions Buildroot/board/FOG/FOS/rootfs_overlay/etc/init.d/S00tmpsize
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/bin/bash
#
# Caps /tmp at the size the FOG server asks for (ADR-0021).
#
# FOS's root is in RAM, and so is /tmp. A runaway write to /tmp must end in
# "No space left on device", not in the client running out of memory and
# hanging. fstab mounts /tmp as tmpfs with a 256 MiB default; this resizes it
# from ramdisk_size= on the kernel command line, which the server fills from
# the FOG_KERNEL_RAMDISK_SIZE setting (in KB). That setting sized the old ext2
# ram disk; an initramfs has no ram disk, so the same knob now sizes /tmp, and
# every FOG server already sends it.
#
# The size is clamped: never below 64 MiB, never above half of physical
# memory -- a tmpfs larger than RAM caps nothing. A missing or non-numeric
# value keeps the 256 MiB default.
#
# S00tmpsize start resize /tmp (what rcS runs)
# S00tmpsize print print the size in KB it would use, change nothing
#
# FOG_CMDLINE and FOG_MEMINFO override /proc/cmdline and /proc/meminfo, for
# tests/checks/tmp-size.sh.

DEFAULT_KB=262144
MIN_KB=65536

tmpSizeKB() {
local cmdline="${FOG_CMDLINE:-/proc/cmdline}" meminfo="${FOG_MEMINFO:-/proc/meminfo}"
local arg kb="" memkb half
for arg in $(cat "$cmdline" 2>/dev/null); do
[[ $arg == ramdisk_size=* ]] && kb="${arg#ramdisk_size=}"
done
[[ $kb =~ ^[0-9]{1,10}$ ]] || kb=$DEFAULT_KB
[[ $kb -lt $MIN_KB ]] && kb=$MIN_KB
memkb=$(awk '/^MemTotal:/ {print $2; exit}' "$meminfo" 2>/dev/null)
if [[ $memkb =~ ^[0-9]+$ ]]; then
half=$(( memkb / 2 ))
[[ $kb -gt $half ]] && kb=$half
fi
echo "$kb"
}

case $1 in
start)
kb=$(tmpSizeKB)
mount -o remount,size="${kb}k" /tmp || echo " * Could not resize /tmp to ${kb} KB; it keeps the fstab default"
;;
print)
tmpSizeKB
;;
esac
13 changes: 13 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ tests/checks/mdraid-gate.sh # md arrays auto-assemble only with mdraid=
tests/checks/multicast-receiver.sh # udp-receiver gets --start-timeout from mcastMaxWait; an empty stream says no sender answered (fos#48)

tests/checks/fixparts-binary-present.sh # fs.patch builds fixparts, which runFixparts() calls (fos#194)
tests/checks/tmp-size.sh # /tmp is real tmpfs, sized from ramdisk_size= and clamped (ADR-0021)
tests/checks/perftest.sh # Performance Test task: rates, sampled image, dev/ share, bottleneck (fos#55)

tests/checks/package-mirrors.sh # build.sh's package mirror fallback and hash enforcement
Expand Down Expand Up @@ -405,6 +406,18 @@ and add a new ADR for any similarly hard-to-reverse decision:
wherever `web=` comes from. The `fogpin=` parser drops base64 padding, so
`fog.curl` restores it. Guarded by `tests/checks/tls-trust.sh`.

- **0021 — initramfs for every arch.** x64/x86 now ship an xz cpio initramfs
(same names, `init.xz`/`init_32.xz`) instead of an ext2 image booted as a
classic initrd: mainline is removing `root=/dev/ram0` initrd support
(January 2027), and fos#75 panics inside that loader. The server's
`root=`/`ramdisk_size=`/`rootfstype=` are ignored once `/init` exists, so no
server change is needed. Two traps: customizing the init is now an appended
overlay cpio, not a loop mount (`xz --check=crc32` still required); and
`CONFIG_SHMEM` was off, which made every "tmpfs" a size-ignoring ramfs, so
`/tmp` was never capped. SHMEM/TMPFS are now on and `S00tmpsize` sizes `/tmp`
from `ramdisk_size=` (FOG_KERNEL_RAMDISK_SIZE), clamped to half of RAM.
Guarded by `tests/checks/initrd-format.sh` and `tests/checks/tmp-size.sh`.

General conventions to preserve when editing `funcs.sh`/`partition-funcs.sh`:

- Both libraries hardcode `/usr/share/fog/lib` as their own path and expect to
Expand Down
4 changes: 2 additions & 2 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,11 @@ function buildFilesystem() {
cd dist || { echo "Couldn't change directory to dist"; exit 1; }
case "${arch}" in
x64)
compiledfile="../fssource$arch/output/images/rootfs.ext2.xz"
compiledfile="../fssource$arch/output/images/rootfs.cpio.xz"
initfile='init.xz'
;;
x86)
compiledfile="../fssource$arch/output/images/rootfs.ext2.xz"
compiledfile="../fssource$arch/output/images/rootfs.cpio.xz"
initfile='init_32.xz'
;;
arm64)
Expand Down
13 changes: 12 additions & 1 deletion configs/fsx64.config
Original file line number Diff line number Diff line change
Expand Up @@ -2943,7 +2943,18 @@ BR2_PACKAGE_PARTIMAGE=y
#
# BR2_TARGET_ROOTFS_BTRFS is not set
# BR2_TARGET_ROOTFS_CLOOP is not set
# BR2_TARGET_ROOTFS_CPIO is not set
BR2_TARGET_ROOTFS_CPIO=y
BR2_TARGET_ROOTFS_CPIO_FULL=y
# BR2_TARGET_ROOTFS_CPIO_DRACUT is not set
# BR2_TARGET_ROOTFS_CPIO_NONE is not set
# BR2_TARGET_ROOTFS_CPIO_GZIP is not set
# BR2_TARGET_ROOTFS_CPIO_BZIP2 is not set
# BR2_TARGET_ROOTFS_CPIO_LZ4 is not set
# BR2_TARGET_ROOTFS_CPIO_LZMA is not set
# BR2_TARGET_ROOTFS_CPIO_LZO is not set
BR2_TARGET_ROOTFS_CPIO_XZ=y
# BR2_TARGET_ROOTFS_CPIO_ZSTD is not set
# BR2_TARGET_ROOTFS_CPIO_UIMAGE is not set
# BR2_TARGET_ROOTFS_CRAMFS is not set
# BR2_TARGET_ROOTFS_EROFS is not set
BR2_TARGET_ROOTFS_EXT2=y
Expand Down
13 changes: 12 additions & 1 deletion configs/fsx86.config
Original file line number Diff line number Diff line change
Expand Up @@ -3050,7 +3050,18 @@ BR2_PACKAGE_PARTIMAGE=y
#
# BR2_TARGET_ROOTFS_BTRFS is not set
# BR2_TARGET_ROOTFS_CLOOP is not set
# BR2_TARGET_ROOTFS_CPIO is not set
BR2_TARGET_ROOTFS_CPIO=y
BR2_TARGET_ROOTFS_CPIO_FULL=y
# BR2_TARGET_ROOTFS_CPIO_DRACUT is not set
# BR2_TARGET_ROOTFS_CPIO_NONE is not set
# BR2_TARGET_ROOTFS_CPIO_GZIP is not set
# BR2_TARGET_ROOTFS_CPIO_BZIP2 is not set
# BR2_TARGET_ROOTFS_CPIO_LZ4 is not set
# BR2_TARGET_ROOTFS_CPIO_LZMA is not set
# BR2_TARGET_ROOTFS_CPIO_LZO is not set
BR2_TARGET_ROOTFS_CPIO_XZ=y
# BR2_TARGET_ROOTFS_CPIO_ZSTD is not set
# BR2_TARGET_ROOTFS_CPIO_UIMAGE is not set
# BR2_TARGET_ROOTFS_CRAMFS is not set
# BR2_TARGET_ROOTFS_EROFS is not set
BR2_TARGET_ROOTFS_EXT2=y
Expand Down
3 changes: 2 additions & 1 deletion configs/kernelarm64.config
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ CONFIG_EPOLL=y
CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
# CONFIG_SHMEM is not set
CONFIG_SHMEM=y
CONFIG_TMPFS=y
CONFIG_AIO=y
CONFIG_IO_URING=y
# CONFIG_IO_URING_MOCK_FILE is not set
Expand Down
3 changes: 2 additions & 1 deletion configs/kernelx64.config
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ CONFIG_EPOLL=y
CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
# CONFIG_SHMEM is not set
CONFIG_SHMEM=y
CONFIG_TMPFS=y
CONFIG_AIO=y
CONFIG_IO_URING=y
# CONFIG_IO_URING_MOCK_FILE is not set
Expand Down
3 changes: 2 additions & 1 deletion configs/kernelx86.config
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@ CONFIG_EPOLL=y
CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
# CONFIG_SHMEM is not set
CONFIG_SHMEM=y
CONFIG_TMPFS=y
CONFIG_AIO=y
CONFIG_IO_URING=y
# CONFIG_IO_URING_MOCK_FILE is not set
Expand Down
106 changes: 106 additions & 0 deletions docs/adr/0021-initramfs-for-every-arch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# An initramfs for every arch

The x64 and x86 inits were ext2 filesystem images (`rootfs.ext2.xz`). The
kernel boots such an image through the classic initrd path: it copies the
bytes to `/initrd.image`, identifies the format at offset 0, writes the image
into `/dev/ram0`, and mounts that as `root=/dev/ram0`. arm64 already shipped a
cpio initramfs (`arm_init.cpio.gz`).

We decided **every arch ships a cpio initramfs. x64 and x86 now build
`rootfs.cpio.xz`, published under the same names, `init.xz` and `init_32.xz`.**

## Why

Mainline is removing the classic initrd path:

- v7.0 removed its `linuxrc` half
([vfs initrd pull](https://lkml.iu.edu/hypermail/linux/kernel/2602.0/09468.html),
[series](https://lkml.iu.edu/2511.2/11639.html)).
- 7.2 logs "using deprecated initrd support, will be removed in January 2027;
use initramfs instead" on every boot (`init/do_mounts_initrd.c`). 6.18.59
logs "will be removed soon".
- Initramfs is not affected.

A kernel after the removal cannot boot an ext2 init at all. The 6.18 LTS line
keeps the path, so stable FOS is not broken today. Any move to a 7.x kernel,
such as the fos#110 experiment, needs this first.

fos#75 is the second reason. Its panic, "RAMDISK: Couldn't find valid RAM disk
image starting at 0", comes from `init/do_mounts_rd.c`, the classic loader.
An initramfs is unpacked by different code (`init/initramfs.c`) and never
reaches that function. Whether this fixes fos#75 is **not yet known**: the
reporters' machines are the only test.

## Compatibility

- **Server:** nothing changes. With an initramfs that contains `/init`, the
kernel runs it and ignores `root=/dev/ram0`, `ramdisk_size=` and
`rootfstype=ext4`, which FOG's boot menus and `create-usb-image.sh` still
send. Buildroot adds that `/init`; it execs `/sbin/init`.
- **Kernel:** the 6.18 kernels FOS ships unpack an xz initramfs
(`CONFIG_RD_XZ=y`, `CONFIG_BLK_DEV_INITRD=y`). An older FOS kernel paired
with a new init also works, for the same reason.
- **The root filesystem** is now the kernel's rootfs (ramfs while `root=` is
on the command line, `init/do_mounts.c` `init_rootfs()`) instead of a
256 MiB ext2 ram disk. Nothing large is written there: logs rotate, and
the scripts write a few small files under `/etc` and `/usr/share/fog/lib`.
`inittab`'s `remount,rw /` still succeeds.

## /tmp is capped, from the setting the server already sends

`/tmp` was never on the ram disk; fstab mounts it as tmpfs. But every kernel
config had `CONFIG_SHMEM` off, and without it the kernel registers "tmpfs" as
ramfs, which ignores `size=`. So `/tmp` had no limit before this ADR either:
a runaway write grew until the client ran out of memory. `df` printing 0 for
`/tmp` on a live boot is how this was found.

- All three kernel configs now build `CONFIG_SHMEM` and `CONFIG_TMPFS`.
- fstab gives `/tmp` `size=256m` as a default.
- `etc/init.d/S00tmpsize` resizes `/tmp` from `ramdisk_size=` on the kernel
command line. The server fills that from **FOG_KERNEL_RAMDISK_SIZE** (KB,
default 275000). That setting sized the ext2 ram disk; an initramfs has no
ram disk, so the same setting now sizes `/tmp`, and every FOG server, old
or new, already sends it. An admin who wants 512 MiB or 1 GiB sets it there.
- The size is clamped to at least 64 MiB and at most half of physical
memory. A larger tmpfs would cap nothing. A missing or non-numeric value
keeps 256 MiB.

Guarded by `tests/checks/tmp-size.sh`.

## Customizing the init

The long-standing forum method -- `xz -d`, loop-mount the ext2 image, edit,
unmount, `xz -C crc32` -- does not work on a cpio: there is no filesystem to
mount. Two methods replace it:

- **Append an overlay (preferred).** The kernel unpacks concatenated cpio
archives in order, and a later file replaces an earlier one. Put only the
changed files in a small cpio and append it:

```
cd overlay && find . | cpio -o -H newc -R 0:0 | xz -9 --check=crc32 > ../overlay.cpio.xz
cat init.xz overlay.cpio.xz > init-custom.xz
```

No root, no repack of the whole init, and the same overlay re-applies to
every new release unchanged. Verified in QEMU on 6.18.59: a script added
this way ran at boot.
- **Unpack and repack**, for removing files: `xz -dc init.xz | cpio -idm` as
root, edit, then `find . | cpio -o -H newc | xz -9 --check=crc32`.

`--check=crc32` is required either way, as it was before: the kernel's xz
decoder supports only CRC32 or no check, not xz's default CRC64
(`Documentation/staging/xz.rst`).

Postinit scripts on the storage node (`/images/postinitscripts`) need no
change; they never touched the init file.

## What would make us revisit this

A machine that boots the ext2 init but not the initramfs. The experimental
release exists to find one before this reaches Latest.

## Guard

`tests/checks/initrd-format.sh` fails when any arch ships a non-cpio artifact,
and still pairs each artifact's compression with the kernel's decompressor.
6 changes: 6 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,12 @@ tests/checks/fixparts-binary-present.sh
# it. A symbol with no Kconfig definition is
# dropped silently, which is how every init from
# 2023-12 on shipped without it. See fos#194
tests/checks/tmp-size.sh
# /tmp is a real, capped tmpfs: SHMEM/TMPFS in
# every kernel config (-b: post-oldconfig), a
# size= default in fstab, and S00tmpsize's size
# from ramdisk_size= clamped to 64 MiB..half of
# RAM. See ADR-0021
tests/checks/perftest.sh
# fog.perftest (Performance Test task): MB/s and
# GB/min arithmetic, the dev/ share mapped to the
Expand Down
10 changes: 9 additions & 1 deletion tests/checks/initrd-format.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# # in any kernelsource<arch>/ present
#
# Why this exists. build.sh publishes a *different* init image per arch --
# rootfs.ext2.xz for x64/x86, rootfs.cpio.gz for arm64 -- and each of those
# rootfs.cpio.xz for x64/x86, rootfs.cpio.gz for arm64 -- and each of those
# compressions needs its own CONFIG_RD_* symbol in the kernel that has to unpack
# it. Nothing in the tree tied the two ends together, and they drifted: the
# arm64 kernel shipped with `# CONFIG_RD_GZIP is not set` while the arm64 init
Expand Down Expand Up @@ -164,6 +164,14 @@ for arch in x64 x86 arm64; do
brsuffix="${row%%|*}"
rdsuffix="${row#*|}"
echo "arch $arch ships $artifact"
# A filesystem image (ext2) is booted through the classic initrd path,
# root=/dev/ram0, which mainline deprecated and schedules for removal in
# January 2027; 7.2 warns on every boot. It is also the code path fos#75
# panics in. Every arch ships an initramfs (cpio) instead -- ADR-0021.
if [[ $imgtype != cpio ]]; then
echo "FAIL [build.sh] arch $arch ships $artifact, a classic initrd image; ship a cpio initramfs (ADR-0021)"
fails=$((fails + 1))
fi
checkBuildroot "$arch" "$imgtype" "$brsuffix"
checkKernel "configs/kernel${arch}.config" \
"$REPO/configs/kernel${arch}.config" "$rdsuffix"
Expand Down
Loading
Loading