Add FreeBSD support - #339
Open
ocochard wants to merge 3 commits into
Open
Conversation
Contributor
|
Hi @ocochard thanks for the PR. We're taking a look at this internally and will let you know when we have updates. |
efs-utils builds and runs on FreeBSD/amd64 and FreeBSD/aarch64. A new mount_efs(8) helper mounts EFS filesystems through the same efs-proxy used on Linux, and the mount-watchdog daemon supervises the proxy just as it does on other platforms. FreeBSD differs from Linux in a few places that required targeted adjustments: * src/efs_utils_common/mount_options.py: FreeBSD's mount_nfs(8) does not recognize `nfsvers=4.1`; on FreeBSD, pass `nfsv4,minorversion=1` instead so mount_nfs negotiates NFSv4.1. Also add `oneopenown` and `retrycnt=1` to match the AWS-recommended FreeBSD EFS mount options. * src/efs_utils_common/mount_utils.py: dispatch to /sbin/mount_nfs on FreeBSD * src/efs_utils_common/proxy.py: detect FreeBSD init system as "rc" and start the watchdog with `service(8) onestart`. FreeBSD is also added to the SO_BINDTODEVICE-skip list for stunnel config generation. * src/watchdog/__init__.py: /proc/mounts does not exist on FreeBSD, and neither mount(8) nor nfsstat(8) expose the NFS client's TCP port. Enumerate the watchdog's own state files in STATE_FILE_DIR and cross-check against `mount -t nfs` to track live mounts. Keying off state files (rather than live proxy sockets) preserves the watchdog's ability to restart a dead efs-proxy. * dist/amazon-efs-mount-watchdog.rc: new FreeBSD rc(8) script, companion to the existing systemd unit and launchd plist.
ocochard
force-pushed
the
freebsd
branch
2 times, most recently
from
July 30, 2026 09:38
3378a91 to
82e1f29
Compare
A cold-start retry of mount.efs can leave a stale efs-proxy and state file alongside the live one. On FreeBSD, mount enumeration keys off state files, so a single mountpoint with two state file/port pairs was reported as two mounts, and the watchdog maintained two independent restart cycles for one physical mount. Disambiguate using sockstat(1): the kernel NFS client's loopback connection to the live proxy shows up as an ESTABLISHED tcp4 socket on 127.0.0.1:<port>, whereas an orphaned proxy only has its LISTEN socket. Prefer the established port per mountpoint and leave duplicates out, so the existing stale-mount cleanup path reaps the orphaned proxy and its state file. If sockstat is unavailable or ambiguous, keep the first port found rather than dropping a possibly-live mount. Pin the get_current_local_nfs_mounts tests to a non-FreeBSD platform so they exercise the Linux /proc/mounts path identically on any host.
The stunnel health check (check_stunnel_health) runs `df <mountpoint>` every interval and treats a timeout as "tunnel unhealthy", then SIGKILLs efs-proxy and restarts it. That design dates back to stunnel 4.56, which could hang silently while its process stayed alive (aws-efs-csi-driver#616): a process-liveness check could not see a dead-but-running tunnel, so an end-to-end `df` probe was used as a black box - a root GETATTR that must traverse loopback -> efs-proxy -> TLS -> EFS backend and come back. On FreeBSD that design is actively harmful, for two compounding reasons: 1. `df` does not measure the tunnel. Its latency is dominated by the EFS backend, not the local proxy. EFS throttling, burst-credit exhaustion, packet loss or a transient network stall routinely push a single GETATTR past the timeout while efs-proxy is perfectly healthy. The check has one bit of signal - wall-clock elapsed - and cannot distinguish "proxy dead, killing is correct" from "backend slow, killing is destructive". 2. FreeBSD's NFSv4.1 client does not reliably survive its transport being torn down mid-RPC. When the check SIGKILLs efs-proxy while a GETATTR is still in flight, the session can be left unrecoverable (nfsbadse); the next `df` then wedges in uninterruptible D-state, which the check reads as another failure and kills the proxy again. The check ends up manufacturing the failure it reacts to, spinning in an endless SIGKILL/restart loop that only `umount -f` or a reboot can clear. efs-proxy also no longer needs an external end-to-end probe the way stunnel 4.56 did: it self-monitors and, on an unrecoverable internal fault, exits the process so the watchdog restarts it. A broken proxy therefore surfaces as a dead process, not a silent hang. Replace the FreeBSD path with a direct proxy liveness probe. A live efs-proxy holds a persistent ESTABLISHED tcp4 loopback connection to the kernel NFS client (NFS-over-TCP keeps the socket up even when the mount is idle), so healthiness is read from sockstat(1) rather than from an NFS round trip. The proxy is restarted only when BOTH signals agree it is gone: no ESTABLISHED socket on its port AND the process no longer running. A slow backend keeps the socket ESTABLISHED, so it never triggers a kill; a live-but-not-serving proxy (e.g. mid-reconnect) is left alone rather than SIGKILLed mid-recovery. If sockstat cannot be run there is no evidence of failure, so nothing is done. No `df` is spawned on FreeBSD, so the D-state leak is structurally impossible there. The Linux/macOS `df` path is unchanged. Tests: pin the existing df-path tests to a non-FreeBSD platform, and add FreeBSD-branch coverage (established -> no restart; sockstat unavailable -> no restart; not established but process alive -> no restart; not established and process dead -> restart).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description of changes:
efs-utils builds and runs on FreeBSD/amd64 and FreeBSD/aarch64. A new mount_efs(8) helper mounts EFS filesystems through the same efs-proxy used on Linux, and the mount-watchdog daemon supervises the proxy just as it does on other platforms.
FreeBSD differs from Linux in a few places that required targeted adjustments:
src/efs_utils_common/mount_options.py: FreeBSD's mount_nfs(8) does not recognize
nfsvers=4.1; on FreeBSD, passnfsv4,minorversion=1instead so mount_nfs negotiates NFSv4.1. Also addoneopenownandretrycnt=1to match the AWS-recommended FreeBSD EFS mount options.src/efs_utils_common/mount_utils.py: dispatch to /sbin/mount_nfs on FreeBSD
src/efs_utils_common/proxy.py: detect FreeBSD init system as "rc" and start the watchdog with
service(8) onestart. FreeBSD is also added to the SO_BINDTODEVICE-skip list for stunnel config generation.src/watchdog/init.py: /proc/mounts does not exist on FreeBSD, and neither mount(8) nor nfsstat(8) expose the NFS client's TCP port. Enumerate the watchdog's own state files in STATE_FILE_DIR and cross-check against
mount -t nfsto track live mounts. Keying off state files (rather than live proxy sockets) preserves the watchdog's ability to restart a dead efs-proxy.dist/amazon-efs-mount-watchdog.rc: new FreeBSD rc(8) script, companion to the existing systemd unit and launchd plist.
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.