bincompat-memcached - #114
Conversation
9485d32 to
8ed6343
Compare
349ac0c to
c7d533f
Compare
Apps that use threads fail with "Unsupported clone flags requested". pthread_create() asks for the CLONE_SYSVSEM and CLONE_CHILD_CLEARTID flags, but the handlers for them come from uklock semaphores and from posix-futex, and LIBPOSIX_FUTEX is not enabled by anything. Enable it in the QEMU and Firecracker defconfigs. It also selects LIBUKLOCK, whose semaphores handle CLONE_SYSVSEM. The futex syscall is needed by pthreads anyway. With this change memcached can start its worker threads. Signed-off-by: Andreea-Ștefania DUMITRACHE <dumitracheandreea155@gmail.com>
memcached needs eventfd2() for its worker threads. Without it the syscall returns ENOSYS and memcached exits at startup. Signed-off-by: Andreea-Ștefania DUMITRACHE <dumitracheandreea155@gmail.com>
c7d533f to
3ffee83
Compare
There was a problem hiding this comment.
Pull request overview
Adds a new bincompat-memcached application that runs the system Memcached (1.6) as a Linux ELF under Unikraft’s binary-compatibility layer, using elfloader-net as the base and providing build/run/test automation for QEMU and Firecracker.
Changes:
- Enable futex + eventfd support in
elfloader-netdefconfigs to support Memcached threading requirements. - Add
bincompat-memcachedrootfs build that bundles/usr/bin/memcached, required shared libraries, dynamic loader, and minimal passwd/group. - Add build/run/test scripts (including a Firecracker config) plus companion documentation for running and testing.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| elfloader-net/scripts/defconfig/qemu.x86_64 | Enables additional POSIX features (futex/eventfd) needed by Memcached. |
| elfloader-net/scripts/defconfig/fc.x86_64 | Enables additional POSIX features (futex/eventfd) needed by Memcached. |
| bincompat-memcached/setup.sh | Creates workdir/ layout and links required repos (modeled on other bincompat apps). |
| bincompat-memcached/scripts/test/wrapper.sh | Starts an instance and validates Memcached set/get behavior. |
| bincompat-memcached/scripts/test/README.md | Documents how to run the test scripts. |
| bincompat-memcached/scripts/test/common.sh | Shared test helpers for starting/stopping instances and probing services. |
| bincompat-memcached/scripts/test/all.sh | Builds and runs QEMU/FC test flows, capturing logs. |
| bincompat-memcached/scripts/test/.gitignore | Ignores test log output directory. |
| bincompat-memcached/scripts/run/qemu.x86_64 | QEMU run wrapper configuring networking and boot args for memcached. |
| bincompat-memcached/scripts/run/fc.x86_64 | Firecracker run wrapper configuring tap networking and VM config. |
| bincompat-memcached/scripts/README.md | Documents build/run/test script entrypoints. |
| bincompat-memcached/scripts/common.sh | Shared build helpers; builds the elfloader-net base for the chosen target. |
| bincompat-memcached/scripts/build/rootfs.x86_64 | Builds the rootfs and packs it into initrd.cpio. |
| bincompat-memcached/scripts/build/qemu.x86_64 | Builds rootfs + QEMU kernel. |
| bincompat-memcached/scripts/build/kernel.qemu.x86_64 | Builds the elfloader-net base kernel for QEMU. |
| bincompat-memcached/scripts/build/kernel.fc.x86_64 | Builds the elfloader-net base kernel for Firecracker. |
| bincompat-memcached/scripts/build/fc.x86_64 | Builds rootfs + Firecracker kernel. |
| bincompat-memcached/rootfs/Makefile | Copies system memcached + shared libs and generates minimal /etc/passwd and /etc/group. |
| bincompat-memcached/rootfs/.gitignore | Ignores generated rootfs artifacts. |
| bincompat-memcached/README.md | Main app documentation for setup, build, run, and test. |
| bincompat-memcached/fc.x86_64.json | Firecracker VM configuration (boot args, initrd, networking). |
| bincompat-memcached/.gitignore | Ignores workdir/ and generated initrd. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
3ffee83 to
6a03a93
Compare
There was a problem hiding this comment.
The rootfs/ directory must contain a Dockerfile and a Makefile that builds and extracts the required root filesystem. The Dockerfile should be actually the one from the examples/memcached1.6-base/ directory in the unikraft/catalog repository.
The Makefile would run the docker and extract the root filesystem. The Makefile here is a good starting point.
The current way to do a Makefile relies on local filesystem files that may or may not be present. The Dockerfile is the best way for that.
1c16087 to
bd920fd
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.
Suppressed comments (17)
bincompat-memcached/README.md:10
- The host
memcachedpackage is not used—the binary is taken from Docker—while every scripted test and the documented client command requirenc, which is not in the top-level requirements and is not installed byapt install memcachedon a minimal system. A clean setup can therefore reachnc: not found; document/installnetcat-openbsdinstead.
Make sure you installed the [requirements](../README.md#requirements), including `memcached` itself:
```console
sudo apt install memcached
**bincompat-memcached/rootfs/Dockerfile:35**
* Copying `/etc/passwd` alone is insufficient for the glibc `getpwnam()` lookup used by `-u root`: glibc resolves passwd entries through NSS and dynamically loads `libnss_files.so.2` (with the relevant NSS configuration). This scratch rootfs does not include that module, so Memcached can fail its user lookup at startup. Include the required NSS module/configuration, or change the launch strategy to avoid a name-based user lookup.
COPY --from=build /etc/passwd /etc/passwd
**bincompat-memcached/rootfs/Dockerfile:1**
* This image is intended to supply an x86_64 ELF, but the build stage follows the Docker host platform. On an arm64 build host, the stage contains arm64 paths/binaries while the later `COPY` commands require `/lib/x86_64-linux-gnu/...`, so the rootfs build fails instead of producing the documented target. Pin the build stage to `linux/amd64` (as the other binary-compatible Docker image does).
FROM memcached:1.6.23-bookworm as build
**bincompat-memcached/rootfs/Makefile:11**
* The `docker export | tar` pipeline reports only the `tar` status, and the following `docker rm` can mask either failure. A failed export or extraction can therefore make `make` succeed and leave an unusable rootfs. Check each step explicitly (and clean the temporary archive/container on failure).
docker export temp-container | tar -x -C $(ROOTFS_DIR)
docker rm temp-container
**bincompat-memcached/scripts/test/all.sh:31**
* The return status from this QEMU test, and from the Firecracker test below, is discarded. If QEMU fails and Firecracker succeeds, or if KVM is unavailable and the final `printf` runs, `all.sh` exits successfully despite a failed test. Accumulate the statuses and exit nonzero after all requested tests finish.
test_build_run qemu.x86_64
**bincompat-memcached/scripts/test/all.sh:29**
* `./setup.sh` is allowed to fail and the test suite continues with whatever repositories or artifacts happen to exist. That can make a setup failure look like a later test result (and, with stale builds, a false pass). Check this command and abort or propagate its status before starting builds.
./setup.sh
**bincompat-memcached/rootfs/Dockerfile:26**
* The comment contains a spelling error: `libraryes` should be `libraries`.
System libraryes
**bincompat-memcached/rootfs/Makefile:11**
* The fixed `temp-container` name is not cleaned up if an export is interrupted or a later recipe step fails, so the next build can fail at `docker create` with a name-conflict error; `make clean` does not remove Docker containers. Remove any stale container before creating it and install an exit trap to remove the current one.
docker create --name temp-container $(DOCKER_IMAGE) /usr/bin/memcached
mkdir -p $(ROOTFS_DIR)
docker export temp-container | tar -x -C $(ROOTFS_DIR)
docker rm temp-container
**bincompat-memcached/scripts/build/fc.x86_64:4**
* These commands are not chained, so a failed rootfs build/packaging step is followed by a kernel build and the latter's success status makes this script report success even though `initrd.cpio` is invalid or missing. Chain the commands (or enable fail-fast handling) so the kernel build is not reported as successful after a rootfs failure.
./scripts/build/rootfs.x86_64
./scripts/build/kernel.fc.x86_64
**bincompat-memcached/scripts/build/qemu.x86_64:3**
* These commands are not chained, so a failed rootfs build/packaging step is followed by a kernel build and the latter's success status makes this script report success even though `initrd.cpio` is invalid or missing. Chain the commands (or enable fail-fast handling) so the kernel build is not reported as successful after a rootfs failure.
./scripts/build/rootfs.x86_64
./scripts/build/kernel.qemu.x86_64
**bincompat-memcached/scripts/build/rootfs.x86_64:1**
* These commands are not fail-fast: a failed Docker build or extraction can be followed by `mkcpio`, and the script may then return success to the kernel build wrapper with an incomplete or missing initrd. Enable `set -e` (or chain the commands) so rootfs failures are propagated.
make -C rootfs/ clean
make -C rootfs/
**bincompat-memcached/scripts/run/fc.x86_64:20**
* This cleanup unconditionally downs and deletes the shared `tap0` name before recreating it. If another Firecracker VM or network service owns that interface, running this script can disconnect it. Use an application-specific interface or only delete a tap created by this script.
sudo ip link set dev virbr0 down
sudo ip link del dev virbr0
sudo ip link set dev tap0 down
sudo ip link del dev tap0
**bincompat-memcached/scripts/run/qemu.x86_64:20**
* This cleanup unconditionally downs and deletes `virbr0`, a common libvirt bridge name, before recreating it. On a host using libvirt or another VM on that bridge, running this script can disconnect and remove unrelated networking. Use an application-specific bridge or only remove an interface created by this script.
sudo ip link set dev virbr0 down
sudo ip link del dev virbr0
sudo ip link set dev tap0 down
sudo ip link del dev tap0
**bincompat-memcached/scripts/test/all.sh:11**
* This failure branch prints `FAILED` but then returns the status of `echo`, which is zero. As a result, callers cannot detect a failed Firecracker build when KVM is unavailable. Return nonzero from the failure branch so the test driver can propagate it.
This issue also appears on line 31 of the same file.
if test $? -eq 0; then
echo "PASSED"
else
echo "FAILED"
fi
**bincompat-memcached/scripts/test/common.sh:12**
* `fc.x86_64.json` is a shared basename used by many other applications in this repository (for example `c-hello` and `elfloader-net`). Matching only `firecracker.*fc.x86_64.json` can therefore SIGKILL an unrelated Firecracker VM whenever this test cleans up. Use a per-application socket or config path and match that unique identifier instead.
sudo pkill -9 -f "firecracker.*fc.x86_64.json"
**bincompat-memcached/scripts/test/common.sh:18**
* The test cleanup also unconditionally deletes `virbr0` after the test, so it can remove a pre-existing libvirt bridge or disconnect unrelated VMs even when this test did not create that interface. Restrict cleanup to interfaces owned by this test instead of deleting the shared name.
sudo ip link set dev virbr0 down
sudo ip link del dev virbr0
**bincompat-memcached/scripts/test/wrapper.sh:18**
* The test scripts invoke `nc` for readiness and protocol checks, but the application setup only tells users to install `memcached` and the repository requirements do not list netcat. On a clean supported host, `all.sh` will fail with `nc: not found`; document or install a concrete provider such as `netcat-openbsd`.
while ! nc -z -w 1 172.44.0.2 11211 2> /dev/null; do
</details>
Run the unmodified Linux Memcached 1.6 binary on Unikraft through app-elfloader, with networking, on QEMU and Firecracker. The layout follows bincompat-c-hello, with elfloader-net as the base kernel. The rootfs is built from the system Memcached binary (apt) together with its dynamic libraries and minimal /etc/passwd and /etc/group files, needed because Memcached runs with "-u root" and looks up the user with getpwnam(). The tests follow the structure from unikraft#112. Signed-off-by: Andreea-Ștefania DUMITRACHE <dumitracheandreea155@gmail.com>
Signed-off-by: Andreea-Ștefania DUMITRACHE <dumitracheandreea155@gmail.com>
bd920fd to
013f7aa
Compare
razvand
left a comment
There was a problem hiding this comment.
Reviewed-by: Razvan Deaconescu razvand@unikraft.io
Approved-by: Razvan Deaconescu razvand@unikraft.io
Add
bincompat-memcached: run Memcached 1.6 in binary-compatibility mode, modeled onbincompat-c-hello, withelfloader-netas base.Root filesystem
rootfs/builds through Docker, followingexamples/memcached1.6-base/inunikraft/catalog:Dockerfile— multi-stage build on top of the officialmemcached:1.6.23-bookwormimage: an
alpine:3stage supplies CA certificates and timezone data, and the finalscratchstage carries thememcachedbinary, its dynamic libraries(
libssl,libcrypto,libevent,libsasl2,libc,ld-linux) and/etc/passwd(needed by
-u root/getpwnam()).Makefile— runsdocker build, thendocker create/docker exportto extractthe filesystem into
rootfs/extracted_rootfs/.scripts/build/rootfs.x86_64packs it intoinitrd.cpiowithmkcpio.Configuration changes
Two options had to be enabled in the
elfloader-netdefconfigs:CONFIG_LIBPOSIX_FUTEX— without itpthread_create()fails with"Unsupported clone flags requested";
CONFIG_LIBPOSIX_EVENTFD— Memcached needseventfd2()for its worker threads.Also fixes
CONFIG_LIBUKBOOT_MAINTHREAD=y=y→CONFIG_LIBUKBOOT_MAINTHREAD=yin theelfloader-basicandelfloader-netdefconfigs; the duplicated value is not validdefconfig syntax.
Testing
Tested on QEMU/x86_64:
set,getandstatswork on172.44.0.2:11211.scripts/test/runs the checks automatically:wrapper.shpolls the port untilMemcached accepts connections (30s timeout) instead of sleeping a fixed interval, and
all.shskips the Firecracker run when/dev/kvmis not writable rather than failing.The Firecracker scripts are included but not fully verified: the test VM has no
/dev/kvm, and my local CPU hits Unikraft/Firecracker issues unrelated to this app.