Skip to content

blockd: fix and improvements, mostly for uxc container use#20

Open
dangowrt wants to merge 8 commits into
openwrt:masterfrom
dangowrt:uvol-ubus-mount
Open

blockd: fix and improvements, mostly for uxc container use#20
dangowrt wants to merge 8 commits into
openwrt:masterfrom
dangowrt:uvol-ubus-mount

Conversation

@dangowrt

@dangowrt dangowrt commented Jun 9, 2026

Copy link
Copy Markdown
Member

A bunch of fixes and several improvements for blockd in preparation for an upcoming major uvol rework.

dangowrt added 8 commits June 2, 2026 02:04
blockd may be started after storage devices hotplug scripts
were already run. Call 'block hotplug' for all of them when
blockd starts (ie. coldplug).

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
The synchronous variant of block() reads WEXITSTATUS(status) straight
after waitpid(), but waitpid() may be interrupted by SIGCHLD from one of
the asynchronous hotplug-call children, returning -1 with errno EINTR and
leaving status untouched. The garbage status was then decoded as a
non-zero exit code (typically 127), so a perfectly successful
"block autofs add" was reported as a failure and the autofs request was
answered with AUTOFS_IOC_FAIL. The kernel then denied access to a volume
that had in fact just been mounted, which surfaced as intermittent
first-access failures on freshly registered uvol volumes.

Retry waitpid() on EINTR and initialise status so the child's real exit
code is used.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Devices injected into blockd at runtime over ubus (for example by uvol,
which derives its volumes from the self-describing LVM/UBI backing store
rather than from persistent UCI) have no matching section in
/etc/config/fstab. mount_device() looked the device up via find_block()
and, finding nothing, fell through to anon-mount or skipped it entirely,
so such a device never mounted at its registered target.

The autofs trigger only fires for devices blockd has registered for
autofs, so its presence is sufficient proof that a runtime registration
exists. Synthesise a minimal autofs mount in that case and let the
regular autofs path mount the device at /tmp/run/blockd/<device>, which
blockd's symlink already exposes at the requested target. This lets a
volume be mounted purely over ubus, with no persistent fstab state.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
autofs_expire() tears down idle mounts (the holder is gone), after which the
backing block device has no remaining holder. Emit a mount.umount notification
with the device name so subscribers can act on the device becoming free - e.g.
uvol's deferred reap of a volume that was still in use when removal was
requested.
A runtime volume registered over the block.hotplug ubus call carries no
fstab entry, so block had no way to learn how it should be mounted and
fell back to read-write. A read-only backing store (e.g. a squashfs or
erofs uvol volume, or an ext4 volume registered read-only) then failed
to mount with EACCES.

Keep the declared options with the device in blockd and hand them to
"block autofs add" as a fourth argument when the kernel requests the
mount. block applies them to the synthesised runtime mount via
parse_mount_options, so "options":"ro" mounts the volume read-only
without block or blockd ever inspecting the filesystem type.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Storage readiness is a level ("is storage available now?"), but blockd only
emitted it as an edge: a single mount.ready notification sent once the initial
coldplug settles. A subscriber that connects after that notification fired
never learns that storage is ready and falls back to a timeout.

Track readiness in a blockd_ready latch and expose it via a no-arg "status"
method returning { "ready": <bool> }, and add the same field to the no-device
"info" reply. Consumers can then subscribe and query the current level instead
of having to catch the one-shot notification at the right moment. mount.ready
stays as a lightweight "something changed, re-query me" hint.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
blockd gains an optional check_fs attribute on the hotplug registration. When
set, the autofs mount path runs the filesystem's preen check (e2fsck -p,
fsck.f2fs -a) before mounting, so a volume left dirty by power loss is repaired
instead of mounted read-only or refused. The check self-gates: a clean fs is a
no-op. Plain fstab mounts keep the global check_fs; the per-mount attribute lets
blockd-managed volumes (e.g. uvol) opt in without an fstab section, and does not
touch unrelated autofs mounts.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
block(1)'s autofs and hotplug actions call back into blockd over ubus with a
3s timeout. The startup path forked these helpers and waited for them with a
synchronous waitpid(), so blockd could not service their ubus calls while it
was blocked: every coldplug child timed out after 3s and the run took ~3.6s
per block device. blockd_ready was only set, and the mount.ready readiness
notification only sent, once that serialised coldplug finally returned tens of
seconds later -- long after consumers had subscribed, so they never saw it and
fell back to their own timeouts.

Spawn the startup helpers asynchronously and reap them via uloop instead,
keeping the ubus loop live so the children complete promptly. Announce
readiness once all of them have finished, preserving the invariant that the
coldplugged device list is complete before subscribers re-register.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant