diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..33d1f98 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,87 @@ +name: Harness CI + +on: + push: + workflow_dispatch: + inputs: + kernel_release: + description: 'Release tag that provides arm64 Image (default: kernel-main)' + required: true + default: 'kernel-main' + type: string + +permissions: + contents: read + +jobs: + tests: + runs-on: ubuntu-24.04-arm + strategy: + fail-fast: false + matrix: + test: + - smoke + - fsx + - postmark + - dbench + - diod-regression + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + KERNEL_RELEASE: ${{ inputs.kernel_release || 'kernel-main' }} + V9FS_DOCKER_IMAGE: ghcr.io/v9fs/docker:latest + steps: + - name: Checkout harness + uses: actions/checkout@v4 + + - name: Download published kernel Image + run: | + set -euo pipefail + mkdir -p kernel/.build/arch/arm64/boot + gh release download "${KERNEL_RELEASE}" -p Image -D kernel/.build/arch/arm64/boot --clobber + ls -la kernel/.build/arch/arm64/boot/Image + + - name: Run ${{ matrix.test }} (guest-direct, chroot) + run: | + mkdir -p "$GITHUB_WORKSPACE/tmp" + docker run --rm --privileged \ + --name "v9fs-test.${{ matrix.test }}.${{ github.run_id }}.${{ github.run_attempt }}" \ + --label v9fs.harness=v9fs-test \ + --user 0:0 \ + -e KERNELBUILD=/workspaces/kernel/.build \ + -v "$GITHUB_WORKSPACE:/home/v9fs-test/test" \ + -v "$GITHUB_WORKSPACE/kernel:/workspaces/kernel" \ + -v "$GITHUB_WORKSPACE/tmp:/workspaces/tmp" \ + -w /home/v9fs-test/test \ + "${V9FS_DOCKER_IMAGE}" \ + bash -lc "./scripts/v9fs-run-tests '${{ matrix.test }}'" + + - name: Dump logs on failure + if: failure() + run: | + set -euo pipefail + echo "==== logs tree ====" + (ls -R logs || true) + echo "==== qemu.log (tail) ====" + for f in logs/*/qemu.log; do + echo "---- $f ----" + tail -n 250 "$f" || true + done + echo "==== guest.log (tail) ====" + for f in logs/*/guest.log; do + echo "---- $f ----" + tail -n 250 "$f" || true + done + + - name: Fix log permissions (for artifact upload) + if: always() + run: | + sudo chmod -R a+rX logs || true + + - name: Upload logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.test }}-logs + path: logs + retention-days: 7 + diff --git a/.github/workflows/linux-kernel-publish.yml b/.github/workflows/linux-kernel-publish.yml index d53cbbd..dcc6156 100644 --- a/.github/workflows/linux-kernel-publish.yml +++ b/.github/workflows/linux-kernel-publish.yml @@ -1,17 +1,3 @@ -# Publish an arm64 `Image` built from v9fs/linux to this repo's GitHub Releases (+ GHCR). -# -# This repo cannot subscribe to pushes on another repo. Use one of: -# - `repository_dispatch` from v9fs/linux (recommended), or -# - `workflow_dispatch` here for manual / automation. -# -# Example repository_dispatch (from v9fs/linux CI or a PAT with repo scope on v9fs/test): -# event_type: publish-kernel-image -# client_payload: -# linux_repository: v9fs/linux -# linux_ref: main -# release_tag: kernel-main -# release_title: kernel-main # optional - name: Publish Linux kernel (arm64 Image) on: @@ -30,124 +16,72 @@ on: default: 'main' type: string release_tag: - description: 'Release tag to publish/update (e.g. kernel-main, kernel-nightly, kernel-6.12.0)' + description: 'Release tag to publish/update (kernel-main, kernel-nightly, kernel-)' required: true default: 'kernel-main' type: string - release_title: - description: 'Optional GitHub release title (defaults to release_tag)' - required: false - default: '' - type: string permissions: contents: write - packages: write jobs: publish: runs-on: ubuntu-24.04-arm env: - GHCR_REPO: ghcr.io/${{ github.repository_owner }}/v9fs-test-kernel - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + V9FS_DOCKER_IMAGE: ghcr.io/v9fs/docker:latest steps: - - name: Checkout test harness (Dockerfile + scripts) + - name: Checkout harness (scripts) uses: actions/checkout@v4 - - name: Resolve parameters - id: params - run: | - set -euo pipefail - if [ "${GITHUB_EVENT_NAME}" = "repository_dispatch" ]; then - python3 -c "import json,os; ev=json.load(open(os.environ['GITHUB_EVENT_PATH'])); p=ev.get('client_payload') or {}; o=open(os.environ['GITHUB_OUTPUT'],'a',encoding='utf-8'); \ - o.write('linux_repository=%s\n'%(p.get('linux_repository') or 'v9fs/linux')); \ - o.write('linux_ref=%s\n'%(p.get('linux_ref') or 'main')); \ - o.write('release_tag=%s\n'%(p.get('release_tag') or 'kernel-main')); \ - o.write('release_title=%s\n'%(p.get('release_title') or p.get('release_tag') or 'kernel-main'))" - else - title="${{ inputs.release_title }}" - if [ -z "${title}" ]; then title="${{ inputs.release_tag }}"; fi - { - echo "linux_repository=${{ inputs.linux_repository }}" - echo "linux_ref=${{ inputs.linux_ref }}" - echo "release_tag=${{ inputs.release_tag }}" - echo "release_title=${title}" - } >>"$GITHUB_OUTPUT" - fi - - - name: Checkout kernel under build + - name: Checkout kernel uses: actions/checkout@v4 with: - repository: ${{ steps.params.outputs.linux_repository }} - ref: ${{ steps.params.outputs.linux_ref }} + repository: ${{ inputs.linux_repository || github.event.client_payload.linux_repository || 'v9fs/linux' }} + ref: ${{ inputs.linux_ref || github.event.client_payload.linux_ref || 'main' }} path: linux - - name: Install ORAS - uses: oras-project/setup-oras@v1 - - - name: Determine kernel source SHA - id: kernelsha - run: | - set -euo pipefail - sha="$(git -C linux rev-parse HEAD)" - echo "sha=$sha" >>"$GITHUB_OUTPUT" - echo "KERNEL_SHA=$sha" >>"$GITHUB_ENV" - echo "Kernel SHA: $sha" - - - name: Build Docker environment - run: docker build -t v9fs-test-env:local . - - - name: Build kernel (QEMU guest kernel image) + - name: Build kernel (arm64 Image) run: | mkdir -p "$GITHUB_WORKSPACE/tmp" "$GITHUB_WORKSPACE/kernel" docker run --rm --privileged \ + --user 0:0 \ -v "$GITHUB_WORKSPACE:/home/v9fs-test/test" \ -v "$GITHUB_WORKSPACE/linux:/workspaces/linux" \ -v "$GITHUB_WORKSPACE/kernel:/workspaces/kernel" \ -v "$GITHUB_WORKSPACE/tmp:/workspaces/tmp" \ -w /home/v9fs-test/test \ - v9fs-test-env:local \ - bash -lc "v9fs-build-kernel && v9fs-export-kernel /workspaces/linux /workspaces/linux/.build /workspaces/kernel" + "${V9FS_DOCKER_IMAGE}" \ + bash -lc '\ + set -euo pipefail; \ + out=/workspaces/linux/.build; \ + mkdir -p \"$out\"; \ + if [ ! -f \"$out/.config\" ]; then \ + make -C /workspaces/linux O=\"$out\" defconfig; \ + fi; \ + cfg=/workspaces/linux/scripts/config; \ + if [ -x \"$cfg\" ]; then \ + \"$cfg\" --file \"$out/.config\" \ + -e NET_9P -e NET_9P_VIRTIO -e 9P_FS -e 9P_FS_POSIX_ACL \ + -e VIRTIO_PCI -e PCI; \ + make -C /workspaces/linux O=\"$out\" olddefconfig; \ + else \ + echo \"WARNING: missing scripts/config; relying on defconfig\"; \ + fi; \ + make -C /workspaces/linux O=\"$out\" -j\"$(nproc)\"; \ + mkdir -p /workspaces/kernel/.build/arch/arm64/boot; \ + cp -f \"$out/arch/arm64/boot/Image\" /workspaces/kernel/.build/arch/arm64/boot/Image; \ + ' - - name: Publish arm64 Image as release asset + - name: Publish Image as release asset env: - TAG: ${{ steps.params.outputs.release_tag }} - TITLE: ${{ steps.params.outputs.release_title }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ inputs.release_tag || github.event.client_payload.release_tag || 'kernel-main' }} run: | set -euo pipefail img="kernel/.build/arch/arm64/boot/Image" - if [ ! -f "$img" ]; then - echo "ERROR: missing $img" - find kernel/.build -maxdepth 6 -type f | sed 's|^| |' - exit 2 - fi - + test -f "$img" gh release view "$TAG" >/dev/null 2>&1 || \ - gh release create "$TAG" --title "$TITLE" --notes "Automated arm64 kernel Image from ${GITHUB_REPOSITORY}@${GITHUB_SHA} (linux ${KERNEL_SHA})." --prerelease + gh release create "$TAG" --title "$TAG" --notes "Automated arm64 kernel Image." --prerelease cp -f "$img" Image gh release upload "$TAG" Image --clobber - - name: Package kernel image(s) for GHCR - run: | - set -euo pipefail - mkdir -p dist - mapfile -t files < <( - find kernel/.build/arch -type f \( -name '*Image' -o -name 'bzImage' \) -print | sort - ) - if [ "${#files[@]}" -eq 0 ]; then - echo "ERROR: no kernel images found under kernel/.build/arch" - find kernel -maxdepth 5 -type f | sed 's|^| |' - exit 2 - fi - tar -czf "dist/kernel-image.tar.gz" "${files[@]}" - - - name: Publish kernel image package to GHCR (OCI artifact) - env: - ORAS_EXPERIMENTAL_OCI_ARTIFACT: "1" - run: | - set -euo pipefail - echo "${{ secrets.GITHUB_TOKEN }}" | oras login ghcr.io -u "${{ github.actor }}" --password-stdin - - oras push "${GHCR_REPO}:linux-${KERNEL_SHA}" \ - "dist/kernel-image.tar.gz:application/gzip" - oras tag "${GHCR_REPO}:linux-${KERNEL_SHA}" "${{ steps.params.outputs.release_tag }}" diff --git a/.gitignore b/.gitignore index e2907e3..7013148 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -logs/* +logs/ kernel/ tmp/ initrd.cpio diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6c67004 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,56 @@ +# AGENTS.md + +This file captures the working preferences for AI agents contributing to this repo. +Tweak freely. + +## Branching and change hygiene + +- Do active work on `rework` unless told otherwise. +- Keep `README.md`, `CHANGES.md`, and `TODO.md` updated as changes land. +- Do not commit generated outputs (`logs/`, `kernel/`, `tmp/`, `initrd.cpio`, pid files). + +## Test philosophy +- Prefer **guest-direct execution** (Option A): run tests **inside the QEMU guest**. +- Avoid SSH/port-forwarding flows unless explicitly requested. +- use u-root based minimal initrd as root filesystem +- use u-root/cpu with NFS option to expose tools, benchmarks, tests, and results directories to the guest running in qemu +- be able to run as github actions or using act locally +- provide easy mechanism for running local tests (make or script based) +- verify workflow locally before pushing to github +- CI should surface failures (red dashboards) while still running the full suite: + - Run the whole matrix + - Record failures + - Exit non-zero at the end + +## Local/dev environment assumptions + +- Primary local dev is **macOS via Docker + QEMU**. +- Prefer solutions that work on Docker Desktop (no reliance on KVM). +- After any manual experiment that uses `docker run`, ensure containers are not left running: + - Prefer `docker run --rm` plus a project label `v9fs.harness=v9fs-test`. + - If you suspect a hung run left containers behind, clean up with `make docker-clean` or `./scripts/v9fs-docker-clean`. + +## CI architecture preferences +- Use http://github.com/v9fs/docker published base image instead of building custom docker for kernel build and/or test frameworks +- Default to **ARM64** (`ubuntu-24.04-arm`) for builds/tests unless asked otherwise. +- Build and/or test will be triggered by external triggers (such as v9fs/linux changes) or user request in addition to any changes to this repo +- Separate concerns: + - **Kernel publishing** workflow: builds `v9fs/linux` arm64 `Image` and publishes it. + - **Harness CI** workflows: download a published kernel `Image` and run tests. +- Publishing: + - Prefer a stable, `wget`-able GitHub Release asset `Image` tagged `kernel-main`, `kernel-nightly`, or `kernel-`. + - GHCR is optional/secondary; keep it consistent if used. + +## Logging and debuggability + +- Always preserve logs for failures (artifact upload `if: always()`). +- When tests fail, also dump the relevant tails into the CI console output: + - `logs/*/qemu.log` + - per-test `*.log` + - `guest.exitcode` markers (or equivalent) + +## Style + +- Prefer small, explicit scripts over complex magic. +- Keep paths stable and explicit (`/workspaces/share`, `kernel/.build/...`). +- Avoid large refactors unless requested; preserve working behavior first. \ No newline at end of file diff --git a/CHANGES.md b/CHANGES.md index b192678..9b782b9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,11 +1,8 @@ ## Unreleased -- Start `rework` branch workflow and add baseline project docs (`README.md`, `CHANGES.md`, `TODO.md`). -- Add Docker + QEMU environment for local macOS runs and align GitHub Actions to use it. -- Run the suite **inside the QEMU guest** via initrd cmdline flags (`v9fs.run=1`, `v9fs.tests`, …) and a small guest runner (`scripts/v9fs-guest-run`), avoiding SSH/host port forwarding. -- Stabilize 9p exports for guest tests by bind-mounting the workspace/kernel/tmp/testbins under `/workspaces/share` (`scripts/v9fs-run-tests`). -- Give nightly/on-demand log artifacts **unique names** so parallel jobs do not collide on `actions/upload-artifact`. -- Ignore common local build outputs (`kernel/`, `tmp/`, generated initrd/pid files) in `.gitignore`. -- **Split workflows**: add `.github/workflows/linux-kernel-publish.yml` to build/publish **`v9fs/linux`** arm64 `Image` on **`repository_dispatch`** (from linux) or **`workflow_dispatch`**, publishing to **GitHub Releases** (`kernel-main`, `kernel-nightly`, `kernel-`, …) plus **GHCR** (`linux-` + release tag). -- **Harness CI** (`.github/workflows/demand.yml`, `.github/workflows/nightly.yml`) no longer builds the kernel; they **`gh release download`** the published `Image` (defaults: `kernel-main` / `kernel-nightly`) and run QEMU tests on **ARM64** runners. +- Clean-slate rework started; prior implementation preserved under `old/rework-take-1/` (tag `rework-take-1`). +- Rebuild harness (take 2): minimal Docker+QEMU guest-direct smoke test that mounts a 9p export and validates basic filesystem operations. +- Add GitHub Actions: + - `linux-kernel-publish.yml` to build/publish arm64 `Image` as a release asset + - `ci.yml` to download `Image` and run the smoke harness in CI diff --git a/CPU.md b/CPU.md new file mode 100644 index 0000000..d80d382 --- /dev/null +++ b/CPU.md @@ -0,0 +1,45 @@ +# CPU.md + +Lookaside notes for **u-root/cpu** (`cpu` + `cpud`) as it relates to this repo and the `v9fs/docker` initrd. + +Per `AGENTS.md`, treat **source code** as the authority. + +## What `cpud` is + +`cpud` is the “daemon side” of a Plan9-inspired `cpu` session. It can run: + +- **as PID 1** (init + daemon) +- **as a daemon** (listening server that forks per-session cpuds) +- **as a per-session “remote”** process + +Source of truth: + +- `u-root/cpu` `cmds/cpud/main_linux.go` and `cmds/cpud/cpuddoc.go` + +## `cpud` flags (server mode) + +From `cmds/cpud/main_linux.go` and `cmds/cpud/cpuddoc.go`: + +- `-hk`: host key file (host key) +- `-pk`: public key file (default `key.pub`) +- `-sp`: listen port (default `17010`) +- `-net`: network (default `tcp`) +- `-d`: debug prints +- `-klog`: log to kernel log instead of stdout +- `-register` / `-registerTO`: optional controller registration +- `-sleepBeforeServing`: delay before serving (useful when running as init) + +## `cpud` “remote” mode and argument rules + +In remote mode (invoked as `cpud -remote ...`) the accepted flag set is intentionally smaller, and the program expects remaining args to be the command to run. + +Source of truth: + +- `cmds/cpud/main_linux.go` (`if len(os.Args)>1 && os.Args[1] == "-remote"...` and the custom FlagSet) + +## Relevance to `v9fs/docker` + +The `v9fs/docker` base initrd’s `/init` is a symlink to `bbin/cpud` (and `bbin/cpud` is an applet of the `bb` binary). + +For this repo’s harness, we are **not** depending on `cpu/cpud` features to run tests yet; we only rely on the u-root init sequence and the availability of `/bbin/bb` applets to implement `/bin/uinit` and mount 9p. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d1b74a5 --- /dev/null +++ b/Makefile @@ -0,0 +1,39 @@ +.PHONY: docker-smoke docker-clean docker-clean-aggressive + +IMAGE ?= ghcr.io/v9fs/docker:latest +KERNEL_RELEASE ?= kernel-main + +docker-smoke: + mkdir -p ./tmp ./kernel + @echo "Place kernel Image at ./kernel/.build/arch/arm64/boot/Image (or download from release $(KERNEL_RELEASE))" + @set -euo pipefail; \ + name="v9fs-test.smoke.$$(date +%s)"; \ + mkdir -p logs/docker; \ + echo "Running container $$name"; \ + set +e; \ + docker run --privileged \ + --name "$$name" \ + --label v9fs.harness=v9fs-test \ + --user 0:0 \ + -e KERNELBUILD=/workspaces/kernel/.build \ + -v "$$(pwd):/home/v9fs-test/test" \ + -v "$$(pwd)/kernel:/workspaces/kernel" \ + -v "$$(pwd)/tmp:/workspaces/tmp" \ + -w /home/v9fs-test/test \ + $(IMAGE) \ + bash -lc "./scripts/v9fs-run-tests smoke"; \ + rc="$$?"; \ + set -e; \ + docker logs "$$name" >"logs/docker/$${name}.log" 2>&1 || true; \ + docker inspect "$$name" >"logs/docker/$${name}.inspect.json" 2>&1 || true; \ + docker rm -f "$$name" >/dev/null 2>&1 || true; \ + echo "Saved docker logs to logs/docker/$${name}.log"; \ + exit "$$rc" + +docker-clean: + @echo "Cleaning containers labeled v9fs.harness=v9fs-test" + @docker ps -aq --filter "label=v9fs.harness=v9fs-test" | xargs -r docker rm -f >/dev/null 2>&1 || true + +docker-clean-aggressive: + @./scripts/v9fs-docker-clean --aggressive + diff --git a/README.md b/README.md index 37eae0f..66566d7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,38 @@ +# v9fs test harness (clean-slate) + +This branch is being reworked from a clean slate. + +The previous implementation snapshot is preserved at: + +- Git tag: `rework-take-1` +- Directory: `old/rework-take-1/` + +## Current rebuild (take 2) + +This repo now contains a **minimal 9p client smoke test harness**: + +- QEMU runs an **arm64** Linux kernel `Image` +- QEMU exports a host directory via **virtio-9p** +- An initrd runs the smoke test **inside the guest** (no SSH) +- The test exercises basic filesystem operations on the 9p mount + +### Local smoke run (Docker + QEMU) + +1. Build the image: + +```bash +docker pull ghcr.io/v9fs/docker:latest +``` + +1. Put a kernel `Image` at `./kernel/.build/arch/arm64/boot/Image` +2. Run the smoke test: + +```bash +make docker-smoke +``` + +Logs land under `./logs//` (QEMU serial is `qemu.log`; guest writes `guest.log` + `guest.exitcode`). + # v9fs test harness This repository contains **test code and scripts** for exercising the Linux **9p (v9fs)** filesystem. @@ -85,19 +120,20 @@ CI uses the same Docker + QEMU flow as local development, but **kernel builds ar published separately** from the harness tests: 1. A dedicated workflow builds `v9fs/linux` and publishes the arm64 `Image` to - **GitHub Releases** (and GHCR). + **GitHub Releases** (and GHCR). 2. The harness workflows download that published `Image` into `kernel/.build/arch/...` - and run `v9fs-run-tests ...` with `--privileged` so the harness can bind-mount a + and run `v9fs-run-tests ...` with `--privileged` so the harness can bind-mount a stable 9p export root (`/workspaces/share`). ### Workflows -| Workflow | File | When it runs | -| -------- | ---- | ------------ | -| **Publish Linux kernel** | `.github/workflows/linux-kernel-publish.yml` | **`repository_dispatch`** from `v9fs/linux` (recommended) or **`workflow_dispatch`** here. Builds arm64 `Image`, uploads it to a release tag you choose (`kernel-main`, `kernel-nightly`, `kernel-`, …), and pushes a GHCR bundle tagged by the **linux commit SHA** plus your release tag. | -| **CI (push and manual)** | `.github/workflows/demand.yml` | On **every push** (all branches), **manual** dispatch, or **`workflow_call`**. Downloads `Image` from the **`kernel-main`** release by default (override via `kernel_release`). | -| **Mainline** | `.github/workflows/nightly.yml` | **Daily** schedule + **manual** dispatch. Downloads `Image` from **`kernel-nightly`** by default (override via `kernel_release`). | +| Workflow | File | When it runs | +| ------------------------ | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Publish Linux kernel** | `.github/workflows/linux-kernel-publish.yml` | `**repository_dispatch`** from `v9fs/linux` (recommended) or `**workflow_dispatch**` here. Builds arm64 `Image`, uploads it to a release tag you choose (`kernel-main`, `kernel-nightly`, `kernel-`, …), and pushes a GHCR bundle tagged by the **linux commit SHA** plus your release tag. | +| **CI (push and manual)** | `.github/workflows/demand.yml` | On **every push** (all branches), **manual** dispatch, or `**workflow_call`**. Downloads `Image` from the `**kernel-main**` release by default (override via `kernel_release`). | +| **Mainline** | `.github/workflows/nightly.yml` | **Daily** schedule + **manual** dispatch. Downloads `Image` from `**kernel-nightly`** by default (override via `kernel_release`). | + Because GitHub Actions cannot natively “watch” another repository’s pushes, the `v9fs/linux` repo should call `repository_dispatch` on `v9fs/test` when branches change @@ -125,7 +161,6 @@ The publish workflow uploads the **arm64** kernel `Image` as a stable release as - **Rolling nightly**: `https://github.com/v9fs/test/releases/download/kernel-nightly/Image` - **Versioned** (example): `https://github.com/v9fs/test/releases/download/kernel-6.12.0/Image` - Log artifact names (avoid collisions when jobs run in parallel): - CI manual/push: `test-results-ci`, `test-results-latency` diff --git a/TODO.md b/TODO.md index e4ad804..a6e154e 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,12 @@ ## TODO +- Extend smoke test into a small suite (create/rename/unlink, fsync, directory traversal, large file IO). +- Add benchmark stages (fsx, postmark, dbench) to guest-direct CI. +- Add a diod regression suite stage (guest-direct) to exercise the kernel v9fs client. +- Decide whether to adopt a u-root/u-root+cpu initramfs for richer tooling distribution. +- Wire `v9fs/linux` to trigger `repository_dispatch` into `linux-kernel-publish.yml`. + +## TODO + - Decide whether to keep `ubuntu-latest` runners or switch back to self-hosted for KVM acceleration. - Remove or clearly fence legacy SSH-based helpers (`test.bash`, `scripts/cpu`) if they are no longer part of the supported workflow. \ No newline at end of file diff --git a/UROOT.md b/UROOT.md new file mode 100644 index 0000000..72cb5c6 --- /dev/null +++ b/UROOT.md @@ -0,0 +1,59 @@ +# UROOT.md + +Lookaside notes for how **u-root init** behaves in the `v9fs/docker` initrd we boot in this repo. + +Per `AGENTS.md`, this is based on **implementation evidence**, not on secondary docs. + +## What the `v9fs/docker` initrd actually contains + +From inspecting `/home/v9fs-test/initrd.cpio` inside the `ghcr.io/v9fs/docker:latest` image: + +- `init` is a **symlink** to `bbin/cpud` +- `bbin/cpud`, `bbin/gosh`, and `bbin/init` are **symlinks** to `bb` +- `bbin/bb` is the big u-root “busybox” ELF containing applets (including `init`) +- `bin/sh` and `bin/defaultsh` are **symlinks** to `../bbin/gosh` +- there is **no** `bin/mount`, `bin/sed`, etc as standalone files; those operations must come from u-root applets (usually invoked via `/bbin/bb ...`) + +This is why any init/uinit script we provide must avoid assuming a traditional userspace. + +## How u-root decides what to run after `/init` + +The u-root `init` implementation (upstream) tries executables in order: + +- `/inito` (original init, if present) +- `/bbin/uinit` +- `/bin/uinit` +- `/buildbin/uinit` +- `/bin/defaultsh` +- `/bin/sh` + +Source of truth: + +- u-root `init` linux flow: `cmds/core/init/init_linux.go` + - `osInitGo()` builds the command list above. + - `cmdline.GetUinitArgs()` reads `uroot.uinitargs`. + +## Kernel command-line flags u-root reads + +u-root parses `/proc/cmdline` and provides helpers for specific keys: + +- **`uroot.uinitargs`**: a shell-lexed string turned into argv for the `uinit` program. + - Implementation: `pkg/cmdline.(*CmdLine).GetUinitArgs()` calls `shlex.Argv(uinitargs)`. +- **`uroot.initflags`**: a string parsed as a flag-map (used for init-time options like `systemd`). + - Implementation: `pkg/cmdline.(*CmdLine).GetInitFlagMap()`. + +Source of truth: + +- `pkg/cmdline/cmdline.go` (functions `GetUinitArgs`, `GetInitFlagMap`, parsing rules) + +## How we hook guest tests into this + +We patch the base initrd to include an executable **`/bin/uinit`** that: + +- uses `/bbin/bb` applets for `mount`, `sync`, `poweroff`, etc +- mounts the QEMU 9p export (`hostshare`) at `/mnt/9` +- runs `/mnt/9/test/scripts/v9fs-guest-run ` +- syncs and powers off + +This avoids rebuilding u-root and avoids relying on external tools inside the initrd. + diff --git a/.github/workflows/demand.yml b/old/rework-take-1/.github/workflows/demand.yml similarity index 100% rename from .github/workflows/demand.yml rename to old/rework-take-1/.github/workflows/demand.yml diff --git a/old/rework-take-1/.github/workflows/linux-kernel-publish.yml b/old/rework-take-1/.github/workflows/linux-kernel-publish.yml new file mode 100644 index 0000000..d53cbbd --- /dev/null +++ b/old/rework-take-1/.github/workflows/linux-kernel-publish.yml @@ -0,0 +1,153 @@ +# Publish an arm64 `Image` built from v9fs/linux to this repo's GitHub Releases (+ GHCR). +# +# This repo cannot subscribe to pushes on another repo. Use one of: +# - `repository_dispatch` from v9fs/linux (recommended), or +# - `workflow_dispatch` here for manual / automation. +# +# Example repository_dispatch (from v9fs/linux CI or a PAT with repo scope on v9fs/test): +# event_type: publish-kernel-image +# client_payload: +# linux_repository: v9fs/linux +# linux_ref: main +# release_tag: kernel-main +# release_title: kernel-main # optional + +name: Publish Linux kernel (arm64 Image) + +on: + repository_dispatch: + types: [publish-kernel-image] + workflow_dispatch: + inputs: + linux_repository: + description: 'Kernel GitHub repository (owner/name)' + required: true + default: 'v9fs/linux' + type: string + linux_ref: + description: 'Git ref to build (branch, tag, or SHA)' + required: true + default: 'main' + type: string + release_tag: + description: 'Release tag to publish/update (e.g. kernel-main, kernel-nightly, kernel-6.12.0)' + required: true + default: 'kernel-main' + type: string + release_title: + description: 'Optional GitHub release title (defaults to release_tag)' + required: false + default: '' + type: string + +permissions: + contents: write + packages: write + +jobs: + publish: + runs-on: ubuntu-24.04-arm + env: + GHCR_REPO: ghcr.io/${{ github.repository_owner }}/v9fs-test-kernel + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Checkout test harness (Dockerfile + scripts) + uses: actions/checkout@v4 + + - name: Resolve parameters + id: params + run: | + set -euo pipefail + if [ "${GITHUB_EVENT_NAME}" = "repository_dispatch" ]; then + python3 -c "import json,os; ev=json.load(open(os.environ['GITHUB_EVENT_PATH'])); p=ev.get('client_payload') or {}; o=open(os.environ['GITHUB_OUTPUT'],'a',encoding='utf-8'); \ + o.write('linux_repository=%s\n'%(p.get('linux_repository') or 'v9fs/linux')); \ + o.write('linux_ref=%s\n'%(p.get('linux_ref') or 'main')); \ + o.write('release_tag=%s\n'%(p.get('release_tag') or 'kernel-main')); \ + o.write('release_title=%s\n'%(p.get('release_title') or p.get('release_tag') or 'kernel-main'))" + else + title="${{ inputs.release_title }}" + if [ -z "${title}" ]; then title="${{ inputs.release_tag }}"; fi + { + echo "linux_repository=${{ inputs.linux_repository }}" + echo "linux_ref=${{ inputs.linux_ref }}" + echo "release_tag=${{ inputs.release_tag }}" + echo "release_title=${title}" + } >>"$GITHUB_OUTPUT" + fi + + - name: Checkout kernel under build + uses: actions/checkout@v4 + with: + repository: ${{ steps.params.outputs.linux_repository }} + ref: ${{ steps.params.outputs.linux_ref }} + path: linux + + - name: Install ORAS + uses: oras-project/setup-oras@v1 + + - name: Determine kernel source SHA + id: kernelsha + run: | + set -euo pipefail + sha="$(git -C linux rev-parse HEAD)" + echo "sha=$sha" >>"$GITHUB_OUTPUT" + echo "KERNEL_SHA=$sha" >>"$GITHUB_ENV" + echo "Kernel SHA: $sha" + + - name: Build Docker environment + run: docker build -t v9fs-test-env:local . + + - name: Build kernel (QEMU guest kernel image) + run: | + mkdir -p "$GITHUB_WORKSPACE/tmp" "$GITHUB_WORKSPACE/kernel" + docker run --rm --privileged \ + -v "$GITHUB_WORKSPACE:/home/v9fs-test/test" \ + -v "$GITHUB_WORKSPACE/linux:/workspaces/linux" \ + -v "$GITHUB_WORKSPACE/kernel:/workspaces/kernel" \ + -v "$GITHUB_WORKSPACE/tmp:/workspaces/tmp" \ + -w /home/v9fs-test/test \ + v9fs-test-env:local \ + bash -lc "v9fs-build-kernel && v9fs-export-kernel /workspaces/linux /workspaces/linux/.build /workspaces/kernel" + + - name: Publish arm64 Image as release asset + env: + TAG: ${{ steps.params.outputs.release_tag }} + TITLE: ${{ steps.params.outputs.release_title }} + run: | + set -euo pipefail + img="kernel/.build/arch/arm64/boot/Image" + if [ ! -f "$img" ]; then + echo "ERROR: missing $img" + find kernel/.build -maxdepth 6 -type f | sed 's|^| |' + exit 2 + fi + + gh release view "$TAG" >/dev/null 2>&1 || \ + gh release create "$TAG" --title "$TITLE" --notes "Automated arm64 kernel Image from ${GITHUB_REPOSITORY}@${GITHUB_SHA} (linux ${KERNEL_SHA})." --prerelease + cp -f "$img" Image + gh release upload "$TAG" Image --clobber + + - name: Package kernel image(s) for GHCR + run: | + set -euo pipefail + mkdir -p dist + mapfile -t files < <( + find kernel/.build/arch -type f \( -name '*Image' -o -name 'bzImage' \) -print | sort + ) + if [ "${#files[@]}" -eq 0 ]; then + echo "ERROR: no kernel images found under kernel/.build/arch" + find kernel -maxdepth 5 -type f | sed 's|^| |' + exit 2 + fi + tar -czf "dist/kernel-image.tar.gz" "${files[@]}" + + - name: Publish kernel image package to GHCR (OCI artifact) + env: + ORAS_EXPERIMENTAL_OCI_ARTIFACT: "1" + run: | + set -euo pipefail + echo "${{ secrets.GITHUB_TOKEN }}" | oras login ghcr.io -u "${{ github.actor }}" --password-stdin + + oras push "${GHCR_REPO}:linux-${KERNEL_SHA}" \ + "dist/kernel-image.tar.gz:application/gzip" + oras tag "${GHCR_REPO}:linux-${KERNEL_SHA}" "${{ steps.params.outputs.release_tag }}" diff --git a/.github/workflows/nightly.yml b/old/rework-take-1/.github/workflows/nightly.yml similarity index 100% rename from .github/workflows/nightly.yml rename to old/rework-take-1/.github/workflows/nightly.yml diff --git a/old/rework-take-1/.gitignore b/old/rework-take-1/.gitignore new file mode 100644 index 0000000..e2907e3 --- /dev/null +++ b/old/rework-take-1/.gitignore @@ -0,0 +1,5 @@ +logs/* +kernel/ +tmp/ +initrd.cpio +qemu.pid diff --git a/old/rework-take-1/CHANGES.md b/old/rework-take-1/CHANGES.md new file mode 100644 index 0000000..b192678 --- /dev/null +++ b/old/rework-take-1/CHANGES.md @@ -0,0 +1,11 @@ +## Unreleased + +- Start `rework` branch workflow and add baseline project docs (`README.md`, `CHANGES.md`, `TODO.md`). +- Add Docker + QEMU environment for local macOS runs and align GitHub Actions to use it. +- Run the suite **inside the QEMU guest** via initrd cmdline flags (`v9fs.run=1`, `v9fs.tests`, …) and a small guest runner (`scripts/v9fs-guest-run`), avoiding SSH/host port forwarding. +- Stabilize 9p exports for guest tests by bind-mounting the workspace/kernel/tmp/testbins under `/workspaces/share` (`scripts/v9fs-run-tests`). +- Give nightly/on-demand log artifacts **unique names** so parallel jobs do not collide on `actions/upload-artifact`. +- Ignore common local build outputs (`kernel/`, `tmp/`, generated initrd/pid files) in `.gitignore`. +- **Split workflows**: add `.github/workflows/linux-kernel-publish.yml` to build/publish **`v9fs/linux`** arm64 `Image` on **`repository_dispatch`** (from linux) or **`workflow_dispatch`**, publishing to **GitHub Releases** (`kernel-main`, `kernel-nightly`, `kernel-`, …) plus **GHCR** (`linux-` + release tag). +- **Harness CI** (`.github/workflows/demand.yml`, `.github/workflows/nightly.yml`) no longer builds the kernel; they **`gh release download`** the published `Image` (defaults: `kernel-main` / `kernel-nightly`) and run QEMU tests on **ARM64** runners. + diff --git a/Dockerfile b/old/rework-take-1/Dockerfile similarity index 100% rename from Dockerfile rename to old/rework-take-1/Dockerfile diff --git a/LICENSE b/old/rework-take-1/LICENSE similarity index 100% rename from LICENSE rename to old/rework-take-1/LICENSE diff --git a/old/rework-take-1/README.md b/old/rework-take-1/README.md new file mode 100644 index 0000000..37eae0f --- /dev/null +++ b/old/rework-take-1/README.md @@ -0,0 +1,133 @@ +# v9fs test harness + +This repository contains **test code and scripts** for exercising the Linux **9p (v9fs)** filesystem. + +The kernel source under test lives in the upstream repository: + +- `https://github.com/v9fs/linux` + +## What lives here + +- **CI workflows**: automation to build/run tests against a chosen kernel revision +- **Test code**: focused repros and regression tests for v9fs behavior +- **Scripts**: helpers to run locally and/or in CI + +## How we work in this repo + +- **Development branch**: all active work happens on `rework` +- **Change log**: keep `CHANGES.md` updated for every change set +- **Work tracking**: keep `TODO.md` updated as items are added/removed + +## Quick start + +This repo intentionally does *not* vendor the kernel source. Most workflows/scripts will: + +1. Fetch `github.com/v9fs/linux` (or a fork/branch you specify) +2. Build the kernel (or use a provided artifact) +3. Run the tests in this repository against that kernel + +### Local (macOS) via Docker + QEMU + +Clone the kernel repo beside this repo: + +```bash +git clone https://github.com/v9fs/linux ../linux +``` + +Build the test environment image: + +```bash +docker build -t v9fs-test-env:local . +``` + +Build the kernel once (and export it as a reusable artifact): + +```bash +mkdir -p ./tmp ./kernel +docker run --rm --privileged \ + -v "$PWD:/home/v9fs-test/test" \ + -v "$PWD/../linux:/workspaces/linux" \ + -v "$PWD/kernel:/workspaces/kernel" \ + -v "$PWD/tmp:/workspaces/tmp" \ + -w /home/v9fs-test/test \ + v9fs-test-env:local \ + bash -lc "v9fs-build-kernel && v9fs-export-kernel /workspaces/linux /workspaces/linux/.build /workspaces/kernel" +``` + +Then run tests repeatedly without rebuilding the kernel: + +```bash +mkdir -p ./tmp +docker run --rm --privileged \ + -e KERNELBUILD=/workspaces/kernel/.build \ + -v "$PWD:/home/v9fs-test/test" \ + -v "$PWD/kernel:/workspaces/kernel" \ + -v "$PWD/tmp:/workspaces/tmp" \ + -w /home/v9fs-test/test \ + v9fs-test-env:local \ + bash -lc "v9fs-run-tests short ci" +``` + +## How tests run (guest-direct) + +`v9fs-run-tests` boots QEMU with an initrd that mounts the host-exported workspace +over **9p** and then runs the suite **inside the guest** (no SSH/port-forwarding). + +The host-visible output is primarily the QEMU serial log: + +- `logs//qemu.log` + +The guest mounts the repo at `/mnt/9/test` (see `scripts/v9fs-guest-run`). + +## GitHub Actions + +CI uses the same Docker + QEMU flow as local development, but **kernel builds are +published separately** from the harness tests: + +1. A dedicated workflow builds `v9fs/linux` and publishes the arm64 `Image` to + **GitHub Releases** (and GHCR). +2. The harness workflows download that published `Image` into `kernel/.build/arch/...` + and run `v9fs-run-tests ...` with `--privileged` so the harness can bind-mount a + stable 9p export root (`/workspaces/share`). + +### Workflows + + +| Workflow | File | When it runs | +| -------- | ---- | ------------ | +| **Publish Linux kernel** | `.github/workflows/linux-kernel-publish.yml` | **`repository_dispatch`** from `v9fs/linux` (recommended) or **`workflow_dispatch`** here. Builds arm64 `Image`, uploads it to a release tag you choose (`kernel-main`, `kernel-nightly`, `kernel-`, …), and pushes a GHCR bundle tagged by the **linux commit SHA** plus your release tag. | +| **CI (push and manual)** | `.github/workflows/demand.yml` | On **every push** (all branches), **manual** dispatch, or **`workflow_call`**. Downloads `Image` from the **`kernel-main`** release by default (override via `kernel_release`). | +| **Mainline** | `.github/workflows/nightly.yml` | **Daily** schedule + **manual** dispatch. Downloads `Image` from **`kernel-nightly`** by default (override via `kernel_release`). | + +Because GitHub Actions cannot natively “watch” another repository’s pushes, the +`v9fs/linux` repo should call `repository_dispatch` on `v9fs/test` when branches change +(see the header comment in `linux-kernel-publish.yml` for an example payload). + +### Kernel images as packages (GHCR) + +Published kernels are also pushed to GitHub Packages (GHCR) as an OCI artifact: + +- **Package**: `ghcr.io/v9fs/v9fs-test-kernel` +- **Tags**: + - `linux-` (immutable) + - The **release tag** you passed (e.g. `kernel-main`, `kernel-nightly`, `kernel-6.12.0`) + +```bash +oras pull ghcr.io/v9fs/v9fs-test-kernel:linux- -o . +tar -tzf kernel-image.tar.gz | head +``` + +### Kernel images as direct downloads (GitHub Releases) + +The publish workflow uploads the **arm64** kernel `Image` as a stable release asset: + +- **Rolling mainline**: `https://github.com/v9fs/test/releases/download/kernel-main/Image` +- **Rolling nightly**: `https://github.com/v9fs/test/releases/download/kernel-nightly/Image` +- **Versioned** (example): `https://github.com/v9fs/test/releases/download/kernel-6.12.0/Image` + + +Log artifact names (avoid collisions when jobs run in parallel): + +- CI manual/push: `test-results-ci`, `test-results-latency` +- Nightly: `test-results-regression`, `test-results-latency` + diff --git a/old/rework-take-1/TODO.md b/old/rework-take-1/TODO.md new file mode 100644 index 0000000..e4ad804 --- /dev/null +++ b/old/rework-take-1/TODO.md @@ -0,0 +1,4 @@ +## TODO + +- Decide whether to keep `ubuntu-latest` runners or switch back to self-hosted for KVM acceleration. +- Remove or clearly fence legacy SSH-based helpers (`test.bash`, `scripts/cpu`) if they are no longer part of the supported workflow. \ No newline at end of file diff --git a/fstabs-regress/virtio-mmap b/old/rework-take-1/fstabs-regress/virtio-mmap similarity index 100% rename from fstabs-regress/virtio-mmap rename to old/rework-take-1/fstabs-regress/virtio-mmap diff --git a/fstabs-regress/virtio-nocache b/old/rework-take-1/fstabs-regress/virtio-nocache similarity index 100% rename from fstabs-regress/virtio-nocache rename to old/rework-take-1/fstabs-regress/virtio-nocache diff --git a/fstabs-regress/virtio-ra b/old/rework-take-1/fstabs-regress/virtio-ra similarity index 100% rename from fstabs-regress/virtio-ra rename to old/rework-take-1/fstabs-regress/virtio-ra diff --git a/fstabs-short-old/qemu-loose b/old/rework-take-1/fstabs-short-old/qemu-loose similarity index 100% rename from fstabs-short-old/qemu-loose rename to old/rework-take-1/fstabs-short-old/qemu-loose diff --git a/fstabs-short-old/qemu-mmap b/old/rework-take-1/fstabs-short-old/qemu-mmap similarity index 100% rename from fstabs-short-old/qemu-mmap rename to old/rework-take-1/fstabs-short-old/qemu-mmap diff --git a/fstabs-short-old/virtio-fscache b/old/rework-take-1/fstabs-short-old/virtio-fscache similarity index 100% rename from fstabs-short-old/virtio-fscache rename to old/rework-take-1/fstabs-short-old/virtio-fscache diff --git a/fstabs-short-old/virtio-loose b/old/rework-take-1/fstabs-short-old/virtio-loose similarity index 100% rename from fstabs-short-old/virtio-loose rename to old/rework-take-1/fstabs-short-old/virtio-loose diff --git a/fstabs-short-old/virtio-mmap b/old/rework-take-1/fstabs-short-old/virtio-mmap similarity index 100% rename from fstabs-short-old/virtio-mmap rename to old/rework-take-1/fstabs-short-old/virtio-mmap diff --git a/fstabs-short-old/virtio-nocache b/old/rework-take-1/fstabs-short-old/virtio-nocache similarity index 100% rename from fstabs-short-old/virtio-nocache rename to old/rework-take-1/fstabs-short-old/virtio-nocache diff --git a/fstabs-short/qemu-loose b/old/rework-take-1/fstabs-short/qemu-loose similarity index 100% rename from fstabs-short/qemu-loose rename to old/rework-take-1/fstabs-short/qemu-loose diff --git a/fstabs-short/qemu-mmap b/old/rework-take-1/fstabs-short/qemu-mmap similarity index 100% rename from fstabs-short/qemu-mmap rename to old/rework-take-1/fstabs-short/qemu-mmap diff --git a/fstabs-short/virtio-fscache b/old/rework-take-1/fstabs-short/virtio-fscache similarity index 100% rename from fstabs-short/virtio-fscache rename to old/rework-take-1/fstabs-short/virtio-fscache diff --git a/fstabs-short/virtio-loose b/old/rework-take-1/fstabs-short/virtio-loose similarity index 100% rename from fstabs-short/virtio-loose rename to old/rework-take-1/fstabs-short/virtio-loose diff --git a/fstabs-short/virtio-mmap b/old/rework-take-1/fstabs-short/virtio-mmap similarity index 100% rename from fstabs-short/virtio-mmap rename to old/rework-take-1/fstabs-short/virtio-mmap diff --git a/fstabs-short/virtio-nocache b/old/rework-take-1/fstabs-short/virtio-nocache similarity index 100% rename from fstabs-short/virtio-nocache rename to old/rework-take-1/fstabs-short/virtio-nocache diff --git a/fstabs-short/virtio-ra b/old/rework-take-1/fstabs-short/virtio-ra similarity index 100% rename from fstabs-short/virtio-ra rename to old/rework-take-1/fstabs-short/virtio-ra diff --git a/fstabs-smoke/virtio-fscache b/old/rework-take-1/fstabs-smoke/virtio-fscache similarity index 100% rename from fstabs-smoke/virtio-fscache rename to old/rework-take-1/fstabs-smoke/virtio-fscache diff --git a/good/fscache/dbench.log b/old/rework-take-1/good/fscache/dbench.log similarity index 100% rename from good/fscache/dbench.log rename to old/rework-take-1/good/fscache/dbench.log diff --git a/good/fscache/dbench.time b/old/rework-take-1/good/fscache/dbench.time similarity index 100% rename from good/fscache/dbench.time rename to old/rework-take-1/good/fscache/dbench.time diff --git a/good/fscache/fsx-mmap.log b/old/rework-take-1/good/fscache/fsx-mmap.log similarity index 100% rename from good/fscache/fsx-mmap.log rename to old/rework-take-1/good/fscache/fsx-mmap.log diff --git a/good/fscache/fsx-mmap.time b/old/rework-take-1/good/fscache/fsx-mmap.time similarity index 100% rename from good/fscache/fsx-mmap.time rename to old/rework-take-1/good/fscache/fsx-mmap.time diff --git a/good/fscache/fsx.log b/old/rework-take-1/good/fscache/fsx.log similarity index 100% rename from good/fscache/fsx.log rename to old/rework-take-1/good/fscache/fsx.log diff --git a/good/fscache/fsx.time b/old/rework-take-1/good/fscache/fsx.time similarity index 100% rename from good/fscache/fsx.time rename to old/rework-take-1/good/fscache/fsx.time diff --git a/good/fscache/ldconfig.log b/old/rework-take-1/good/fscache/ldconfig.log similarity index 100% rename from good/fscache/ldconfig.log rename to old/rework-take-1/good/fscache/ldconfig.log diff --git a/good/fscache/ldconfig.time b/old/rework-take-1/good/fscache/ldconfig.time similarity index 100% rename from good/fscache/ldconfig.time rename to old/rework-take-1/good/fscache/ldconfig.time diff --git a/good/fscache/postmark.log b/old/rework-take-1/good/fscache/postmark.log similarity index 100% rename from good/fscache/postmark.log rename to old/rework-take-1/good/fscache/postmark.log diff --git a/good/fscache/postmark.time b/old/rework-take-1/good/fscache/postmark.time similarity index 100% rename from good/fscache/postmark.time rename to old/rework-take-1/good/fscache/postmark.time diff --git a/good/fscache/results.log b/old/rework-take-1/good/fscache/results.log similarity index 100% rename from good/fscache/results.log rename to old/rework-take-1/good/fscache/results.log diff --git a/good/loose/dbench.log b/old/rework-take-1/good/loose/dbench.log similarity index 100% rename from good/loose/dbench.log rename to old/rework-take-1/good/loose/dbench.log diff --git a/good/loose/dbench.time b/old/rework-take-1/good/loose/dbench.time similarity index 100% rename from good/loose/dbench.time rename to old/rework-take-1/good/loose/dbench.time diff --git a/good/loose/fsx-mmap.log b/old/rework-take-1/good/loose/fsx-mmap.log similarity index 100% rename from good/loose/fsx-mmap.log rename to old/rework-take-1/good/loose/fsx-mmap.log diff --git a/good/loose/fsx-mmap.time b/old/rework-take-1/good/loose/fsx-mmap.time similarity index 100% rename from good/loose/fsx-mmap.time rename to old/rework-take-1/good/loose/fsx-mmap.time diff --git a/good/loose/fsx.log b/old/rework-take-1/good/loose/fsx.log similarity index 100% rename from good/loose/fsx.log rename to old/rework-take-1/good/loose/fsx.log diff --git a/good/loose/fsx.time b/old/rework-take-1/good/loose/fsx.time similarity index 100% rename from good/loose/fsx.time rename to old/rework-take-1/good/loose/fsx.time diff --git a/good/loose/ldconfig.log b/old/rework-take-1/good/loose/ldconfig.log similarity index 100% rename from good/loose/ldconfig.log rename to old/rework-take-1/good/loose/ldconfig.log diff --git a/good/loose/ldconfig.time b/old/rework-take-1/good/loose/ldconfig.time similarity index 100% rename from good/loose/ldconfig.time rename to old/rework-take-1/good/loose/ldconfig.time diff --git a/good/loose/postmark.log b/old/rework-take-1/good/loose/postmark.log similarity index 100% rename from good/loose/postmark.log rename to old/rework-take-1/good/loose/postmark.log diff --git a/good/loose/postmark.time b/old/rework-take-1/good/loose/postmark.time similarity index 100% rename from good/loose/postmark.time rename to old/rework-take-1/good/loose/postmark.time diff --git a/good/loose/results.log b/old/rework-take-1/good/loose/results.log similarity index 100% rename from good/loose/results.log rename to old/rework-take-1/good/loose/results.log diff --git a/good/mmap/dbench.log b/old/rework-take-1/good/mmap/dbench.log similarity index 100% rename from good/mmap/dbench.log rename to old/rework-take-1/good/mmap/dbench.log diff --git a/good/mmap/dbench.time b/old/rework-take-1/good/mmap/dbench.time similarity index 100% rename from good/mmap/dbench.time rename to old/rework-take-1/good/mmap/dbench.time diff --git a/good/mmap/fsx-mmap.log b/old/rework-take-1/good/mmap/fsx-mmap.log similarity index 100% rename from good/mmap/fsx-mmap.log rename to old/rework-take-1/good/mmap/fsx-mmap.log diff --git a/good/mmap/fsx-mmap.time b/old/rework-take-1/good/mmap/fsx-mmap.time similarity index 100% rename from good/mmap/fsx-mmap.time rename to old/rework-take-1/good/mmap/fsx-mmap.time diff --git a/good/mmap/fsx.log b/old/rework-take-1/good/mmap/fsx.log similarity index 100% rename from good/mmap/fsx.log rename to old/rework-take-1/good/mmap/fsx.log diff --git a/good/mmap/fsx.time b/old/rework-take-1/good/mmap/fsx.time similarity index 100% rename from good/mmap/fsx.time rename to old/rework-take-1/good/mmap/fsx.time diff --git a/good/mmap/ldconfig.log b/old/rework-take-1/good/mmap/ldconfig.log similarity index 100% rename from good/mmap/ldconfig.log rename to old/rework-take-1/good/mmap/ldconfig.log diff --git a/good/mmap/ldconfig.time b/old/rework-take-1/good/mmap/ldconfig.time similarity index 100% rename from good/mmap/ldconfig.time rename to old/rework-take-1/good/mmap/ldconfig.time diff --git a/good/mmap/postmark.log b/old/rework-take-1/good/mmap/postmark.log similarity index 100% rename from good/mmap/postmark.log rename to old/rework-take-1/good/mmap/postmark.log diff --git a/good/mmap/postmark.time b/old/rework-take-1/good/mmap/postmark.time similarity index 100% rename from good/mmap/postmark.time rename to old/rework-take-1/good/mmap/postmark.time diff --git a/good/mmap/results.log b/old/rework-take-1/good/mmap/results.log similarity index 100% rename from good/mmap/results.log rename to old/rework-take-1/good/mmap/results.log diff --git a/good/nocache/dbench.log b/old/rework-take-1/good/nocache/dbench.log similarity index 100% rename from good/nocache/dbench.log rename to old/rework-take-1/good/nocache/dbench.log diff --git a/good/nocache/dbench.time b/old/rework-take-1/good/nocache/dbench.time similarity index 100% rename from good/nocache/dbench.time rename to old/rework-take-1/good/nocache/dbench.time diff --git a/good/nocache/fsx-mmap.log b/old/rework-take-1/good/nocache/fsx-mmap.log similarity index 100% rename from good/nocache/fsx-mmap.log rename to old/rework-take-1/good/nocache/fsx-mmap.log diff --git a/good/nocache/fsx-mmap.time b/old/rework-take-1/good/nocache/fsx-mmap.time similarity index 100% rename from good/nocache/fsx-mmap.time rename to old/rework-take-1/good/nocache/fsx-mmap.time diff --git a/good/nocache/fsx.log b/old/rework-take-1/good/nocache/fsx.log similarity index 100% rename from good/nocache/fsx.log rename to old/rework-take-1/good/nocache/fsx.log diff --git a/good/nocache/fsx.time b/old/rework-take-1/good/nocache/fsx.time similarity index 100% rename from good/nocache/fsx.time rename to old/rework-take-1/good/nocache/fsx.time diff --git a/good/nocache/ldconfig.log b/old/rework-take-1/good/nocache/ldconfig.log similarity index 100% rename from good/nocache/ldconfig.log rename to old/rework-take-1/good/nocache/ldconfig.log diff --git a/good/nocache/ldconfig.time b/old/rework-take-1/good/nocache/ldconfig.time similarity index 100% rename from good/nocache/ldconfig.time rename to old/rework-take-1/good/nocache/ldconfig.time diff --git a/good/nocache/postmark.log b/old/rework-take-1/good/nocache/postmark.log similarity index 100% rename from good/nocache/postmark.log rename to old/rework-take-1/good/nocache/postmark.log diff --git a/good/nocache/postmark.time b/old/rework-take-1/good/nocache/postmark.time similarity index 100% rename from good/nocache/postmark.time rename to old/rework-take-1/good/nocache/postmark.time diff --git a/good/nocache/results.log b/old/rework-take-1/good/nocache/results.log similarity index 100% rename from good/nocache/results.log rename to old/rework-take-1/good/nocache/results.log diff --git a/good/ra/dbench.log b/old/rework-take-1/good/ra/dbench.log similarity index 100% rename from good/ra/dbench.log rename to old/rework-take-1/good/ra/dbench.log diff --git a/good/ra/dbench.time b/old/rework-take-1/good/ra/dbench.time similarity index 100% rename from good/ra/dbench.time rename to old/rework-take-1/good/ra/dbench.time diff --git a/good/ra/fsx-mmap.log b/old/rework-take-1/good/ra/fsx-mmap.log similarity index 100% rename from good/ra/fsx-mmap.log rename to old/rework-take-1/good/ra/fsx-mmap.log diff --git a/good/ra/fsx-mmap.time b/old/rework-take-1/good/ra/fsx-mmap.time similarity index 100% rename from good/ra/fsx-mmap.time rename to old/rework-take-1/good/ra/fsx-mmap.time diff --git a/good/ra/fsx.log b/old/rework-take-1/good/ra/fsx.log similarity index 100% rename from good/ra/fsx.log rename to old/rework-take-1/good/ra/fsx.log diff --git a/good/ra/fsx.time b/old/rework-take-1/good/ra/fsx.time similarity index 100% rename from good/ra/fsx.time rename to old/rework-take-1/good/ra/fsx.time diff --git a/good/ra/ldconfig.log b/old/rework-take-1/good/ra/ldconfig.log similarity index 100% rename from good/ra/ldconfig.log rename to old/rework-take-1/good/ra/ldconfig.log diff --git a/good/ra/ldconfig.time b/old/rework-take-1/good/ra/ldconfig.time similarity index 100% rename from good/ra/ldconfig.time rename to old/rework-take-1/good/ra/ldconfig.time diff --git a/good/ra/postmark.log b/old/rework-take-1/good/ra/postmark.log similarity index 100% rename from good/ra/postmark.log rename to old/rework-take-1/good/ra/postmark.log diff --git a/good/ra/postmark.time b/old/rework-take-1/good/ra/postmark.time similarity index 100% rename from good/ra/postmark.time rename to old/rework-take-1/good/ra/postmark.time diff --git a/good/ra/results.log b/old/rework-take-1/good/ra/results.log similarity index 100% rename from good/ra/results.log rename to old/rework-take-1/good/ra/results.log diff --git a/parser/dbench.py b/old/rework-take-1/parser/dbench.py similarity index 100% rename from parser/dbench.py rename to old/rework-take-1/parser/dbench.py diff --git a/qemu-new.bash b/old/rework-take-1/qemu-new.bash similarity index 100% rename from qemu-new.bash rename to old/rework-take-1/qemu-new.bash diff --git a/qemu-old.bash b/old/rework-take-1/qemu-old.bash similarity index 100% rename from qemu-old.bash rename to old/rework-take-1/qemu-old.bash diff --git a/old/rework-take-1/qemu.bash b/old/rework-take-1/qemu.bash new file mode 100755 index 0000000..b709bf5 --- /dev/null +++ b/old/rework-take-1/qemu.bash @@ -0,0 +1,52 @@ +#!/bin/bash + +# ^a+x to terminate + +export ARCH=${ARCH:-`uname -m`} +export KERNELBUILD=${KERNELBUILD:-"/workspaces/linux/.build/"} +export INITRD=${INITRD:-"/home/v9fs-test/initrd.cpio"} +export LOG=${QEMULOG:-"/home/v9fs-test/qemu.log"} +export PIDFILE=${PIDFILE:-"/home/v9fs-test/qemu.pid"} + +if test -f "${PIDFILE}"; then + kill `cat ${PIDFILE}` +fi + +if [ $ARCH == "aarch64" ] +then + export QEMU="qemu-system-aarch64" + export KERNEL="${KERNELBUILD}/arch/arm64/boot/Image" + export MACHINE=virt + export APPEND="earlycon console=ttyAMA0" + export QEMUCPU=${QEMUCPU:-"cortex-a57"} + export EXTRA="" +elif [ $ARCH == "x86_64" ] +then + export QEMU="qemu-system-x86_64" + export KERNEL="${KERNELBUILD}/arch/x86_64/boot/bzImage" + export MACHINE=q35 + export APPEND="console=ttyS0" + export QEMUCPU=${QEMUCPU:-"max"} + export EXTRA="-debugcon file:debug.log -global isa-debugcon.iobase=0x402" +fi + +${QEMU} -kernel \ + ${KERNEL} \ + -cpu ${QEMUCPU} \ + -machine ${MACHINE} \ + -s \ + -smp 4 \ + -m 8192m \ + -initrd ${INITRD} \ + -object rng-random,filename=/dev/urandom,id=rng0 \ + -device virtio-rng-pci,rng=rng0 \ + -device virtio-net-pci,netdev=n1 \ + -netdev user,id=n1 \ + -serial file:${LOG} \ + -fsdev local,security_model=none,writeout=immediate,id=fsdev0,path=${FSDEV_PATH:-/} \ + -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hostshare \ + -append "${APPEND} ${EXTRA_APPEND:-}" \ + ${EXTRA} \ + -daemonize -display none -pidfile ${PIDFILE} + +# -fsdev local,security_model=passthrough,id=fsdev0,path=/ \ diff --git a/scripts/cpu b/old/rework-take-1/scripts/cpu similarity index 100% rename from scripts/cpu rename to old/rework-take-1/scripts/cpu diff --git a/old/rework-take-1/scripts/v9fs-build-initrd b/old/rework-take-1/scripts/v9fs-build-initrd new file mode 100644 index 0000000..5a1edec --- /dev/null +++ b/old/rework-take-1/scripts/v9fs-build-initrd @@ -0,0 +1,153 @@ +#!/usr/bin/env bash +set -euo pipefail + +out="${1:-/home/v9fs-test/initrd.cpio}" +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT + +mkdir -p \ + "$work"/{bin,sbin,etc,proc,sys,dev,run,tmp,mnt/9,mnt/root,root,root/.ssh,home/v9fs-test/.ssh} + +cat >"$work/init" <<'EOF' +#!/bin/sh +set -eu + +export PATH=/bin:/sbin + +mount -t proc proc /proc +mount -t sysfs sysfs /sys +mount -t devtmpfs devtmpfs /dev || true +mkdir -p /dev/pts +mount -t devpts devpts /dev/pts || true + +mkdir -p /run /tmp + +ip link set lo up || true +ip link set eth0 up || true +if command -v udhcpc >/dev/null 2>&1; then + udhcpc -i eth0 -q -t 5 -n || true +fi + +mkdir -p /mnt/9 /mnt/root /root + +# Mount hostshare so we can access the test repo/scripts. +if ! mount -t 9p -o trans=virtio,version=9p2000.L,cache=loose hostshare /mnt/9; then + echo "initrd: WARNING: failed to mount hostshare on /mnt/9" +fi + +# If cpu/scp uploaded an fstab, mount it. (Errors are ok; tests will surface failures.) +if [ -f /etc/fstab ]; then + mkdir -p /mnt/root /mnt/9 + mount -a || true +fi + +mkdir -p /home/v9fs-test/.ssh +chmod 700 /home/v9fs-test/.ssh +mkdir -p /root/.ssh +chmod 700 /root/.ssh + +if [ -f /home/v9fs-test/.ssh/authorized_keys ]; then + chmod 600 /home/v9fs-test/.ssh/authorized_keys +fi +if [ -f /root/.ssh/authorized_keys ]; then + chmod 600 /root/.ssh/authorized_keys +fi + +# Start ssh server for the host-side `cpu` helper. +# Option A: run tests directly in guest (no SSH transport). +cmdline="$(cat /proc/cmdline 2>/dev/null || true)" +case "$cmdline" in + *v9fs.run=1*) + tests="$(echo "$cmdline" | sed -n 's/.*v9fs.tests=\([^ ]*\).*/\1/p')" + type="$(echo "$cmdline" | sed -n 's/.*v9fs.type=\([^ ]*\).*/\1/p')" + check="$(echo "$cmdline" | sed -n 's/.*v9fs.check=\([^ ]*\).*/\1/p')" + ts="$(echo "$cmdline" | sed -n 's/.*v9fs.ts=\([^ ]*\).*/\1/p')" + : "${tests:=smoke}" + : "${type:=ci}" + : "${check:=1}" + if [ -n "${ts:-}" ]; then + export TIMESTAMP="$ts" + fi + + echo "initrd: running guest tests tests=$tests type=$type check=$check" + if [ -f /mnt/9/test/scripts/v9fs-guest-run ]; then + /bin/sh /mnt/9/test/scripts/v9fs-guest-run "$tests" "$type" "$check" || true + else + echo "initrd: missing /mnt/9/test/scripts/v9fs-guest-run" + ls -la /mnt/9/test/scripts 2>/dev/null || true + ls -la /mnt/9/test 2>/dev/null || true + fi + sync 2>/dev/null || true + echo "initrd: powering off" + poweroff -f || halt -f || reboot -f + ;; +esac + +echo "initrd up; interactive shell" +exec sh +EOF +chmod +x "$work/init" + +# Minimal /etc files +cat >"$work/etc/passwd" <<'EOF' +root:x:0:0:root:/root:/bin/sh +EOF +cat >"$work/etc/group" <<'EOF' +root:x:0: +EOF + +# Authorized keys for root login (dropbear checks /root/.ssh and /home/ too; we use /home/v9fs-test). +pubkey="$(cat /home/v9fs-test/.ssh/identity.pub)" +mkdir -p "$work/home/v9fs-test/.ssh" +printf '%s\n' "$pubkey" >"$work/home/v9fs-test/.ssh/authorized_keys" +mkdir -p "$work/root/.ssh" +printf '%s\n' "$pubkey" >"$work/root/.ssh/authorized_keys" + +# Pre-generate a host key for dropbear so boot doesn't block on keygen. +mkdir -p "$work/etc/dropbear" +if command -v dropbearkey >/dev/null 2>&1; then + dropbearkey -t ed25519 -f "$work/etc/dropbear/dropbear_ed25519_host_key" >/dev/null 2>&1 || true +fi + +# Busybox static provides core utils (sh, mount, udhcpc, etc.) +cp /bin/busybox "$work/bin/busybox" +for a in sh mount mkdir ln ls cat echo sleep uname ps kill ip udhcpc netstat poweroff halt reboot date awk sed diff cp rm sort; do + ln -s /bin/busybox "$work/bin/$a" 2>/dev/null || true +done + +# Dropbear + keygen (dynamic; copy required libs) +cp /usr/sbin/dropbear "$work/sbin/dropbear" +cp /usr/bin/dropbearkey "$work/bin/dropbearkey" + +copy_libs() { + local bin="$1" + ldd "$bin" | awk '/=> \//{print $3} /^\//{print $1}' | while read -r lib; do + mkdir -p "$work/$(dirname "$lib")" + cp -L "$lib" "$work/$lib" + done +} +copy_libs /usr/sbin/dropbear +copy_libs /usr/bin/dropbearkey + +# Some environments omit the dynamic loader from `ldd` parsing; ensure it exists. +if [ -f /lib/ld-linux-aarch64.so.1 ]; then + mkdir -p "$work/lib" + cp -L /lib/ld-linux-aarch64.so.1 "$work/lib/ld-linux-aarch64.so.1" +fi +if [ -f /lib64/ld-linux-x86-64.so.2 ]; then + mkdir -p "$work/lib64" + cp -L /lib64/ld-linux-x86-64.so.2 "$work/lib64/ld-linux-x86-64.so.2" +fi + +# Bench/test helpers are executed inside the initrd (from 9p), so the initrd +# must include their shared library dependencies. +for b in /usr/bin/dbench /usr/bin/postmark /usr/local/bin/fsx; do + if [ -x "$b" ]; then + copy_libs "$b" + fi +done + +(cd "$work" && find . -print0 | cpio --null -ov --format=newc) >"$out" + +echo "Wrote initrd to $out" + diff --git a/scripts/v9fs-build-kernel b/old/rework-take-1/scripts/v9fs-build-kernel similarity index 100% rename from scripts/v9fs-build-kernel rename to old/rework-take-1/scripts/v9fs-build-kernel diff --git a/scripts/v9fs-entrypoint b/old/rework-take-1/scripts/v9fs-entrypoint similarity index 100% rename from scripts/v9fs-entrypoint rename to old/rework-take-1/scripts/v9fs-entrypoint diff --git a/scripts/v9fs-export-kernel b/old/rework-take-1/scripts/v9fs-export-kernel similarity index 100% rename from scripts/v9fs-export-kernel rename to old/rework-take-1/scripts/v9fs-export-kernel diff --git a/old/rework-take-1/scripts/v9fs-guest-run b/old/rework-take-1/scripts/v9fs-guest-run new file mode 100755 index 0000000..9c125de --- /dev/null +++ b/old/rework-take-1/scripts/v9fs-guest-run @@ -0,0 +1,78 @@ +#!/bin/sh +set -eu + +tests="${1:-smoke}" +type="${2:-ci}" +check="${3:-1}" + +repo="/mnt/9/test" +ts="${TIMESTAMP:-$(date +%s)}" + +PATH_MNTDIR="${PATH_MNTDIR:-/mnt/9/tmp}" +TESTBIN="${TESTBIN:-/mnt/9/testbin}" + +mkdir -p "$repo/logs/$ts" +rm -f "$repo/logs/current" +ln -s "$ts" "$repo/logs/current" + +echo "GUEST: TYPE=$type TESTS=$tests CHECK=$check" +echo "GUEST: repo=$repo" +echo "GUEST: PATH_MNTDIR=$PATH_MNTDIR" +echo "GUEST: TESTBIN=$TESTBIN" + +mkdir -p "$PATH_MNTDIR" +rm -rf "$PATH_MNTDIR"/* + +rc=0 + +for f in "$repo"/fstabs-"$tests"/*; do + ft="$(basename "$f")" + mode="$(echo "$ft" | awk -F- '{print $1}')" + cfg="$(echo "$ft" | awk -F- '{print $2}')" + + outdir="$repo/logs/$ts/$mode/$cfg" + mkdir -p "$outdir" + + echo "GUEST: running configuration $mode $cfg" + + # Ensure mountpoints exist + mkdir -p /mnt/9 /mnt/root /dev /sys /proc + + # Apply the provided fstab inside guest + cp "$f" /etc/fstab + umount /mnt/9 2>/dev/null || true + umount /mnt/root 2>/dev/null || true + mount -a || true + + results="/tmp/results.log" + : >"$results" + + for name in dbench fsx fsx-mmap ldconfig postmark; do + t="$repo/tests/$type/$name.bash" + [ -f "$t" ] || continue + echo -n "...running test $name ..." | tee -a "$results" + + LOG="$outdir/$name" + export LOG PATH_MNTDIR TESTBIN + + if /bin/sh "$t" >"$LOG.log" 2>&1; then + echo "SUCCESS" | tee -a "$results" + else + echo "FAIL" | tee -a "$results" + rc=1 + fi + done + + if [ "$check" = "1" ]; then + if ! diff -b "$results" "$repo/good/$cfg/results.log" >/dev/null 2>&1; then + echo "GUEST: Configuration $f FAILED" + cat "$results" + rc=1 + fi + fi +done + +echo "$rc" >"$repo/logs/$ts/guest.exitcode" 2>/dev/null || true +sync 2>/dev/null || true +echo "GUEST: tests done rc=$rc" +exit "$rc" diff --git a/old/rework-take-1/scripts/v9fs-run-tests b/old/rework-take-1/scripts/v9fs-run-tests new file mode 100644 index 0000000..77bad06 --- /dev/null +++ b/old/rework-take-1/scripts/v9fs-run-tests @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +set -euo pipefail + +tests="${1:-short}" +type="${2:-ci}" +check="${3:-1}" + +# Ensure artifacts created by root inside Docker are readable by the +# GitHub Actions runner user when uploading `logs/` as an artifact. +umask 022 + +# Default to the container-provided tools exposed to the guest via 9p. +export TESTBIN="${TESTBIN:-/home/v9fs-test/testbin}" + +export TIMESTAMP="${TIMESTAMP:-$(date +%s)}" +mkdir -p "logs/${TIMESTAMP}" +export QEMULOG="logs/${TIMESTAMP}/qemu.log" +export PIDFILE="/home/v9fs-test/qemu.pid" + +# Build a stable export root so 9p sees our bind mounts. +mkdir -p /workspaces/share +mkdir -p /workspaces/share/test /workspaces/share/tmp /workspaces/share/kernel /workspaces/share/testbin +mountpoint -q /workspaces/share/test || mount --bind /home/v9fs-test/test /workspaces/share/test +mountpoint -q /workspaces/share/tmp || mount --bind /workspaces/tmp /workspaces/share/tmp +mountpoint -q /workspaces/share/kernel || mount --bind /workspaces/kernel /workspaces/share/kernel +mountpoint -q /workspaces/share/testbin || mount --bind /home/v9fs-test/testbin /workspaces/share/testbin + +export FSDEV_PATH="/workspaces/share" + +# Run the suite inside the guest and poweroff when complete. +export EXTRA_APPEND="v9fs.run=1 v9fs.tests=${tests} v9fs.type=${type} v9fs.check=${check} v9fs.ts=${TIMESTAMP}" + +/home/v9fs-test/test/qemu.bash + +# Wait for QEMU to exit (guest powers off). +if [ -f "${PIDFILE}" ]; then + pid="$(cat "${PIDFILE}")" + while kill -0 "${pid}" 2>/dev/null; do + sleep 1 + done +fi + +echo "QEMU run complete; logs at ${QEMULOG}" + +# Make logs readable for artifact upload on the host runner. +chmod -R a+rX "logs/${TIMESTAMP}" 2>/dev/null || true + +# The guest writes a status marker into the shared logs directory. +# Use it to fail CI while still running the whole suite. +guest_rc_file="logs/${TIMESTAMP}/guest.exitcode" +for _ in 1 2 3 4 5; do + [ -f "${guest_rc_file}" ] && break + sleep 1 +done + +if [ ! -f "${guest_rc_file}" ]; then + echo "WARNING: missing ${guest_rc_file}; treating as failure" + exit 2 +fi + +guest_rc="$(cat "${guest_rc_file}" 2>/dev/null || echo 1)" +if [ "${guest_rc}" != "0" ]; then + echo "Guest reported failures (rc=${guest_rc})" + exit "${guest_rc}" +fi + diff --git a/test.bash b/old/rework-take-1/test.bash similarity index 100% rename from test.bash rename to old/rework-take-1/test.bash diff --git a/tests/ci/dbench.bash b/old/rework-take-1/tests/ci/dbench.bash similarity index 100% rename from tests/ci/dbench.bash rename to old/rework-take-1/tests/ci/dbench.bash diff --git a/tests/ci/fsx-mmap.bash b/old/rework-take-1/tests/ci/fsx-mmap.bash similarity index 100% rename from tests/ci/fsx-mmap.bash rename to old/rework-take-1/tests/ci/fsx-mmap.bash diff --git a/tests/ci/fsx.bash b/old/rework-take-1/tests/ci/fsx.bash similarity index 100% rename from tests/ci/fsx.bash rename to old/rework-take-1/tests/ci/fsx.bash diff --git a/tests/ci/ldconfig.bash b/old/rework-take-1/tests/ci/ldconfig.bash similarity index 100% rename from tests/ci/ldconfig.bash rename to old/rework-take-1/tests/ci/ldconfig.bash diff --git a/tests/ci/postmark.bash b/old/rework-take-1/tests/ci/postmark.bash similarity index 100% rename from tests/ci/postmark.bash rename to old/rework-take-1/tests/ci/postmark.bash diff --git a/tests/latency/dbench.bash b/old/rework-take-1/tests/latency/dbench.bash similarity index 100% rename from tests/latency/dbench.bash rename to old/rework-take-1/tests/latency/dbench.bash diff --git a/qemu.bash b/qemu.bash index b709bf5..1eeb5db 100755 --- a/qemu.bash +++ b/qemu.bash @@ -1,52 +1,63 @@ -#!/bin/bash +#!/usr/bin/env bash +set -euo pipefail -# ^a+x to terminate - -export ARCH=${ARCH:-`uname -m`} -export KERNELBUILD=${KERNELBUILD:-"/workspaces/linux/.build/"} -export INITRD=${INITRD:-"/home/v9fs-test/initrd.cpio"} -export LOG=${QEMULOG:-"/home/v9fs-test/qemu.log"} -export PIDFILE=${PIDFILE:-"/home/v9fs-test/qemu.pid"} +ARCH="${ARCH:-$(uname -m)}" +KERNELBUILD="${KERNELBUILD:-/workspaces/kernel/.build}" +INITRD="${INITRD:-/home/v9fs-test/initrd.cpio}" +LOG="${QEMULOG:-/home/v9fs-test/qemu.log}" +PIDFILE="${PIDFILE:-/home/v9fs-test/qemu.pid}" +FSDEV_PATH="${FSDEV_PATH:-/workspaces/share}" +QEMU_DAEMONIZE="${QEMU_DAEMONIZE:-0}" +QEMU_STREAM_LOG="${QEMU_STREAM_LOG:-1}" if test -f "${PIDFILE}"; then - kill `cat ${PIDFILE}` + kill "$(cat "${PIDFILE}")" 2>/dev/null || true fi -if [ $ARCH == "aarch64" ] -then - export QEMU="qemu-system-aarch64" - export KERNEL="${KERNELBUILD}/arch/arm64/boot/Image" - export MACHINE=virt - export APPEND="earlycon console=ttyAMA0" - export QEMUCPU=${QEMUCPU:-"cortex-a57"} - export EXTRA="" -elif [ $ARCH == "x86_64" ] -then - export QEMU="qemu-system-x86_64" - export KERNEL="${KERNELBUILD}/arch/x86_64/boot/bzImage" - export MACHINE=q35 - export APPEND="console=ttyS0" - export QEMUCPU=${QEMUCPU:-"max"} - export EXTRA="-debugcon file:debug.log -global isa-debugcon.iobase=0x402" +if [ "${ARCH}" = "aarch64" ]; then + QEMU="qemu-system-aarch64" + KERNEL="${KERNELBUILD}/arch/arm64/boot/Image" + MACHINE="virt" + APPEND="earlycon console=ttyAMA0" + QEMUCPU="${QEMUCPU:-cortex-a57}" + EXTRA="" +else + echo "Unsupported ARCH=${ARCH} (expected aarch64)" + exit 2 fi -${QEMU} -kernel \ - ${KERNEL} \ - -cpu ${QEMUCPU} \ - -machine ${MACHINE} \ - -s \ - -smp 4 \ - -m 8192m \ - -initrd ${INITRD} \ - -object rng-random,filename=/dev/urandom,id=rng0 \ - -device virtio-rng-pci,rng=rng0 \ - -device virtio-net-pci,netdev=n1 \ - -netdev user,id=n1 \ - -serial file:${LOG} \ - -fsdev local,security_model=none,writeout=immediate,id=fsdev0,path=${FSDEV_PATH:-/} \ - -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hostshare \ - -append "${APPEND} ${EXTRA_APPEND:-}" \ - ${EXTRA} \ - -daemonize -display none -pidfile ${PIDFILE} +qemu_args=( + -kernel "${KERNEL}" + -cpu "${QEMUCPU}" + -machine "${MACHINE}" + -smp 4 + -m 4096m + -initrd "${INITRD}" + -object rng-random,filename=/dev/urandom,id=rng0 + -device virtio-rng-pci,rng=rng0 + -device virtio-net-pci,netdev=n1 + -netdev user,id=n1,hostfwd=tcp:0.0.0.0:17010-:17010 + -fsdev "local,security_model=none,writeout=immediate,id=fsdev0,path=${FSDEV_PATH}" + -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=hostshare + -append "${APPEND} ${EXTRA_APPEND:-}" +) + +if [ "${QEMU_DAEMONIZE}" = "1" ]; then + qemu_args+=( + -serial "file:${LOG}" + ${EXTRA} + -daemonize -display none -pidfile "${PIDFILE}" + ) + exec "${QEMU}" "${qemu_args[@]}" +fi -# -fsdev local,security_model=passthrough,id=fsdev0,path=/ \ +# Foreground mode: stream serial to Docker logs and also tee to LOG. +if [ "${QEMU_STREAM_LOG}" = "1" ]; then + # Write pidfile ourselves in foreground mode. + echo "$$" >"${PIDFILE}" 2>/dev/null || true + # Use -nographic so QEMU routes serial+monitor to stdio once. + "${QEMU}" "${qemu_args[@]}" ${EXTRA} -nographic 2>&1 | tee "${LOG}" +else + echo "$$" >"${PIDFILE}" 2>/dev/null || true + exec "${QEMU}" "${qemu_args[@]}" ${EXTRA} -nographic +fi diff --git a/scripts/v9fs-build-initrd b/scripts/v9fs-build-initrd old mode 100644 new mode 100755 index 5a1edec..373495c --- a/scripts/v9fs-build-initrd +++ b/scripts/v9fs-build-initrd @@ -1,153 +1,343 @@ #!/usr/bin/env bash set -euo pipefail -out="${1:-/home/v9fs-test/initrd.cpio}" +out="${1:-/opt/v9fs/initrd.patched.cpio}" work="$(mktemp -d)" trap 'rm -rf "$work"' EXIT -mkdir -p \ - "$work"/{bin,sbin,etc,proc,sys,dev,run,tmp,mnt/9,mnt/root,root,root/.ssh,home/v9fs-test/.ssh} +# We run inside the `v9fs/docker` image, which provides a base initrd at this path. +base="${UROOT_BASE:-/opt/v9fs/initrd.cpio}" +if [ ! -f "$base" ]; then + echo "ERROR: base initrd not found at $base" + exit 2 +fi -cat >"$work/init" <<'EOF' +# Build a tiny /init script that uses traditional userspace binaries we copy in +# (mount/ls/sh), not u-root applets (which may not include mount/ls in this image). +init="$work/init" +cat >"$init" <<'EOF' #!/bin/sh set -eu +exec >/dev/console 2>&1 -export PATH=/bin:/sbin - -mount -t proc proc /proc -mount -t sysfs sysfs /sys +mkdir -p /proc /sys /dev +mount -t proc proc /proc || true +mount -t sysfs sysfs /sys || true mount -t devtmpfs devtmpfs /dev || true -mkdir -p /dev/pts -mount -t devpts devpts /dev/pts || true - -mkdir -p /run /tmp -ip link set lo up || true -ip link set eth0 up || true -if command -v udhcpc >/dev/null 2>&1; then - udhcpc -i eth0 -q -t 5 -n || true +# Best-effort: start cpud in background (not used for smoke path). +if [ -x /bbin/cpud ]; then + echo "init: starting cpud in background" + /bbin/cpud >/dev/console 2>&1 & fi -mkdir -p /mnt/9 /mnt/root /root +mkdir -p /run /tmp /mnt/9 -# Mount hostshare so we can access the test repo/scripts. -if ! mount -t 9p -o trans=virtio,version=9p2000.L,cache=loose hostshare /mnt/9; then - echo "initrd: WARNING: failed to mount hostshare on /mnt/9" -fi +tests="__V9FS_TESTS__" +echo "init: tests=${tests}" -# If cpu/scp uploaded an fstab, mount it. (Errors are ok; tests will surface failures.) -if [ -f /etc/fstab ]; then - mkdir -p /mnt/root /mnt/9 - mount -a || true -fi +if mount -t 9p -o trans=virtio,version=9p2000.L,cache=loose hostshare /mnt/9; then + echo "init: mounted hostshare at /mnt/9" + if [ -x /mnt/9/usr/sbin/chroot ] && [ -x /mnt/9/usr/bin/bash ]; then + echo "init: chroot -> /mnt/9, running container bash+ls" + rc=0 + case "${tests}" in + smoke) + /mnt/9/usr/sbin/chroot /mnt/9 /bin/bash -lc 'set -e; ls -la /; ls -la /home/v9fs-test/test | head' || rc=$? + ;; + fsx) + /mnt/9/usr/sbin/chroot /mnt/9 /bin/bash -lc 'set -e; mkdir -p /workspaces/tmpdir; rm -f /workspaces/tmpdir/fsx.testfile; /workspaces/tmp/testbin/fsx/fsx -N 1000 -R -W /workspaces/tmpdir/fsx.testfile' || rc=$? + ;; + postmark) + /mnt/9/usr/sbin/chroot /mnt/9 /bin/bash -lc 'set -e; mkdir -p /workspaces/tmpdir/postmark; cd /workspaces/tmpdir/postmark; /workspaces/tmp/testbin/postmark/postmark </dev/null || true + ' || rc=$? + ;; + *) + echo "init: ERROR: unknown tests=${tests}" + rc=2 + ;; + esac + else + echo "init: missing /usr/sbin/chroot or /bin/bash in exported root; falling back to initrd ls" + ls -la /mnt/9 || true + rc=2 + fi +else + echo "init: ERROR: mount hostshare failed" + rc=2 fi -# Start ssh server for the host-side `cpu` helper. -# Option A: run tests directly in guest (no SSH transport). -cmdline="$(cat /proc/cmdline 2>/dev/null || true)" -case "$cmdline" in - *v9fs.run=1*) - tests="$(echo "$cmdline" | sed -n 's/.*v9fs.tests=\([^ ]*\).*/\1/p')" - type="$(echo "$cmdline" | sed -n 's/.*v9fs.type=\([^ ]*\).*/\1/p')" - check="$(echo "$cmdline" | sed -n 's/.*v9fs.check=\([^ ]*\).*/\1/p')" - ts="$(echo "$cmdline" | sed -n 's/.*v9fs.ts=\([^ ]*\).*/\1/p')" - : "${tests:=smoke}" - : "${type:=ci}" - : "${check:=1}" - if [ -n "${ts:-}" ]; then - export TIMESTAMP="$ts" - fi - - echo "initrd: running guest tests tests=$tests type=$type check=$check" - if [ -f /mnt/9/test/scripts/v9fs-guest-run ]; then - /bin/sh /mnt/9/test/scripts/v9fs-guest-run "$tests" "$type" "$check" || true - else - echo "initrd: missing /mnt/9/test/scripts/v9fs-guest-run" - ls -la /mnt/9/test/scripts 2>/dev/null || true - ls -la /mnt/9/test 2>/dev/null || true - fi - sync 2>/dev/null || true - echo "initrd: powering off" - poweroff -f || halt -f || reboot -f - ;; -esac - -echo "initrd up; interactive shell" -exec sh -EOF -chmod +x "$work/init" - -# Minimal /etc files -cat >"$work/etc/passwd" <<'EOF' -root:x:0:0:root:/root:/bin/sh -EOF -cat >"$work/etc/group" <<'EOF' -root:x:0: -EOF - -# Authorized keys for root login (dropbear checks /root/.ssh and /home/ too; we use /home/v9fs-test). -pubkey="$(cat /home/v9fs-test/.ssh/identity.pub)" -mkdir -p "$work/home/v9fs-test/.ssh" -printf '%s\n' "$pubkey" >"$work/home/v9fs-test/.ssh/authorized_keys" -mkdir -p "$work/root/.ssh" -printf '%s\n' "$pubkey" >"$work/root/.ssh/authorized_keys" - -# Pre-generate a host key for dropbear so boot doesn't block on keygen. -mkdir -p "$work/etc/dropbear" -if command -v dropbearkey >/dev/null 2>&1; then - dropbearkey -t ed25519 -f "$work/etc/dropbear/dropbear_ed25519_host_key" >/dev/null 2>&1 || true +repo="/mnt/9/home/v9fs-test/test" +if [ -d "$repo" ]; then + mkdir -p "$repo/logs" || true + echo "${rc:-2}" >"$repo/logs/guest.exitcode" || true + echo "init: tests=${tests} rc=${rc:-2}" >"$repo/logs/guest.log" || true fi -# Busybox static provides core utils (sh, mount, udhcpc, etc.) -cp /bin/busybox "$work/bin/busybox" -for a in sh mount mkdir ln ls cat echo sleep uname ps kill ip udhcpc netstat poweroff halt reboot date awk sed diff cp rm sort; do - ln -s /bin/busybox "$work/bin/$a" 2>/dev/null || true +sync || true +echo "init: requesting poweroff via sysrq" +echo o >/proc/sysrq-trigger || true + +# Never exit PID1; if poweroff fails, just idle. +echo "init: poweroff returned; idling" +while true; do + sleep 1 || true done +EOF +chmod +x "$init" -# Dropbear + keygen (dynamic; copy required libs) -cp /usr/sbin/dropbear "$work/sbin/dropbear" -cp /usr/bin/dropbearkey "$work/bin/dropbearkey" +# Bake the selected test into /init (guest environment has no V9FS_TESTS). +sed -i "s/__V9FS_TESTS__/${V9FS_TESTS:-smoke}/g" "$init" +# Collect required userland binaries and their shared libs from the container FS. +mkdir -p "$work/rootfs" +copy_file() { + local src="$1" dst="$2" + mkdir -p "$work/rootfs/$(dirname "$dst")" + cp -L "$src" "$work/rootfs/$dst" +} copy_libs() { local bin="$1" - ldd "$bin" | awk '/=> \//{print $3} /^\//{print $1}' | while read -r lib; do - mkdir -p "$work/$(dirname "$lib")" - cp -L "$lib" "$work/$lib" + ldd "$bin" | awk '/=> \//{print $3}; /^\//{print $1}' | while read -r lib; do + [ -n "$lib" ] || continue + [ -f "$lib" ] || continue + copy_file "$lib" "$lib" done } -copy_libs /usr/sbin/dropbear -copy_libs /usr/bin/dropbearkey -# Some environments omit the dynamic loader from `ldd` parsing; ensure it exists. -if [ -f /lib/ld-linux-aarch64.so.1 ]; then - mkdir -p "$work/lib" - cp -L /lib/ld-linux-aarch64.so.1 "$work/lib/ld-linux-aarch64.so.1" -fi -if [ -f /lib64/ld-linux-x86-64.so.2 ]; then - mkdir -p "$work/lib64" - cp -L /lib64/ld-linux-x86-64.so.2 "$work/lib64/ld-linux-x86-64.so.2" -fi +need_bins=( + sh mount ls mkdir sync sleep +) +for b in "${need_bins[@]}"; do + p="$(command -v "$b" || true)" + [ -n "$p" ] || { echo "ERROR: missing $b in image"; exit 2; } + copy_file "$p" "/bin/$b" + copy_libs "$p" +done -# Bench/test helpers are executed inside the initrd (from 9p), so the initrd -# must include their shared library dependencies. -for b in /usr/bin/dbench /usr/bin/postmark /usr/local/bin/fsx; do - if [ -x "$b" ]; then - copy_libs "$b" +# Also ensure dynamic loader exists if not captured above. +for ldso in /lib/ld-linux-aarch64.so.1 /lib64/ld-linux-aarch64.so.1; do + if [ -f "$ldso" ]; then + copy_file "$ldso" "$ldso" fi done -(cd "$work" && find . -print0 | cpio --null -ov --format=newc) >"$out" +# Overlay our /init onto the base initrd using a tiny Python newc (cpio) rewriter. +# This avoids needing `cpio`, `busybox`, or a working u-root build toolchain. +python3 - "$base" "$init" "$work/rootfs" "$out" <<'PY' +import os, sys, stat, time + +base_path, init_path, extra_root, out_path = sys.argv[1:5] + +def align4(x: int) -> int: + return (x + 3) & ~3 + +def read_exact(f, n: int) -> bytes: + b = f.read(n) + if len(b) != n: + raise EOFError(f"short read: wanted {n}, got {len(b)}") + return b + +def parse_hex(b: bytes) -> int: + return int(b.decode("ascii"), 16) + +def write_header(w, **fields): + # newc header fields are 8 hex bytes each (except magic) + parts = [ + b"070701", + f"{fields['ino']:08x}".encode(), + f"{fields['mode']:08x}".encode(), + f"{fields['uid']:08x}".encode(), + f"{fields['gid']:08x}".encode(), + f"{fields['nlink']:08x}".encode(), + f"{fields['mtime']:08x}".encode(), + f"{fields['filesize']:08x}".encode(), + f"{fields['devmajor']:08x}".encode(), + f"{fields['devminor']:08x}".encode(), + f"{fields['rdevmajor']:08x}".encode(), + f"{fields['rdevminor']:08x}".encode(), + f"{fields['namesize']:08x}".encode(), + f"{fields['check']:08x}".encode(), + ] + w.write(b"".join(parts)) + +def iter_newc_entries(f): + while True: + try: + hdr = read_exact(f, 110) + except EOFError: + # Some initrds may omit a TRAILER!!! record and just end. + break + if hdr[:6] != b"070701": + raise ValueError(f"bad magic: {hdr[:6]!r}") + + ino = parse_hex(hdr[6:14]) + mode = parse_hex(hdr[14:22]) + uid = parse_hex(hdr[22:30]) + gid = parse_hex(hdr[30:38]) + nlink = parse_hex(hdr[38:46]) + mtime = parse_hex(hdr[46:54]) + filesize = parse_hex(hdr[54:62]) + devmajor = parse_hex(hdr[62:70]) + devminor = parse_hex(hdr[70:78]) + rdevmajor = parse_hex(hdr[78:86]) + rdevminor = parse_hex(hdr[86:94]) + namesize = parse_hex(hdr[94:102]) + check = parse_hex(hdr[102:110]) + + name = read_exact(f, namesize) + # name includes trailing NUL + if not name.endswith(b"\x00"): + raise ValueError("name missing NUL terminator") + name_str = name[:-1].decode("utf-8", "replace") + + # pad after name to 4-byte boundary from start of header + pad = align4(110 + namesize) - (110 + namesize) + if pad: + read_exact(f, pad) + + data = read_exact(f, filesize) if filesize else b"" + pad = align4(filesize) - filesize + if pad: + read_exact(f, pad) + + yield { + "ino": ino, "mode": mode, "uid": uid, "gid": gid, "nlink": nlink, "mtime": mtime, + "filesize": filesize, "devmajor": devmajor, "devminor": devminor, + "rdevmajor": rdevmajor, "rdevminor": rdevminor, "namesize": namesize, "check": check, + "name": name_str, "data": data, + } + + if name_str == "TRAILER!!!": + break + +def write_entry(w, name: str, mode: int, uid: int, gid: int, data: bytes): + if not name.endswith("\x00"): + name_bytes = name.encode("utf-8") + b"\x00" + else: + name_bytes = name.encode("utf-8") + namesize = len(name_bytes) + write_header( + w, + ino=0, + mode=mode, + uid=uid, + gid=gid, + nlink=1, + mtime=int(time.time()), + filesize=len(data), + devmajor=0, devminor=0, rdevmajor=0, rdevminor=0, + namesize=namesize, + check=0, + ) + w.write(name_bytes) + # pad to 4-byte boundary after name + pad = align4(110 + namesize) - (110 + namesize) + if pad: + w.write(b"\x00" * pad) + if data: + w.write(data) + pad = align4(len(data)) - len(data) + if pad: + w.write(b"\x00" * pad) + +def write_symlink(w, name: str, target: str, uid: int = 0, gid: int = 0): + mode = stat.S_IFLNK | 0o777 + write_entry(w, name, mode, uid, gid, target.encode("utf-8")) + +def write_dir(w, name: str, mode: int = 0o755, uid: int = 0, gid: int = 0): + write_entry(w, name, stat.S_IFDIR | mode, uid, gid, b"") + +def main(): + init_data = open(init_path, "rb").read() + init_mode = stat.S_IFREG | 0o755 + + extras = {} # path -> (mode, data) + for root, _, files in os.walk(extra_root): + for fn in files: + src = os.path.join(root, fn) + rel = os.path.relpath(src, extra_root) + rel = rel.lstrip("./") + st = os.lstat(src) + if stat.S_ISLNK(st.st_mode): + tgt = os.readlink(src).encode("utf-8") + extras[rel] = (stat.S_IFLNK | 0o777, tgt) + elif stat.S_ISREG(st.st_mode): + extras[rel] = (stat.S_IFREG | (st.st_mode & 0o777), open(src, "rb").read()) + + replaced_init = False + replace_names = set(extras.keys()) + replace_names.add("init") + + tmp_out = out_path + ".tmp" + with open(base_path, "rb") as rf, open(tmp_out, "wb") as wf: + for ent in iter_newc_entries(rf): + name = ent["name"] + if name == "TRAILER!!!": + break + # If we are replacing this path, skip the base entry so we don't + # rely on the kernel's behavior for duplicate cpio entries. + norm = name.lstrip("/") + if norm in replace_names: + continue + write_entry(wf, name, ent["mode"], ent["uid"], ent["gid"], ent["data"]) + + if not replaced_init: + write_entry(wf, "init", init_mode, 0, 0, init_data) + + # Add extra binaries/libs (and overwrite if absent in base). + # Ensure parent directories exist in the archive. + dirs = set() + for p in extras.keys(): + parts = p.split("/") + for i in range(1, len(parts)): + d = "/".join(parts[:i]) + if d: + dirs.add(d) + for d in sorted(dirs, key=lambda s: (s.count("/"), s)): + write_dir(wf, d) + + for path, (mode, data) in extras.items(): + write_entry(wf, path, mode, 0, 0, data) + + # trailer + write_entry(wf, "TRAILER!!!", stat.S_IFREG | 0o644, 0, 0, b"") + + os.replace(tmp_out, out_path) + os.chmod(out_path, 0o644) +if __name__ == "__main__": + main() +PY echo "Wrote initrd to $out" diff --git a/scripts/v9fs-docker-clean b/scripts/v9fs-docker-clean new file mode 100755 index 0000000..be09cc9 --- /dev/null +++ b/scripts/v9fs-docker-clean @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat <<'EOF' +Usage: ./scripts/v9fs-docker-clean [--aggressive] + +Default: remove containers labeled v9fs.harness=v9fs-test. +Aggressive: additionally remove any container that has this repo bind-mounted. +Always captures docker logs + inspect before removing. +EOF +} + +aggressive=0 +case "${1:-}" in + "" ) ;; + --aggressive) aggressive=1 ;; + -h|--help) usage; exit 0 ;; + *) echo "Unknown arg: $1" >&2; usage; exit 2 ;; +esac + +repo_root="$(cd "$(dirname "$0")/.." && pwd)" +label="v9fs.harness=v9fs-test" +ts="$(date +%s)" +outdir="${repo_root}/logs/docker/clean-${ts}" +mkdir -p "${outdir}" + +capture_and_remove() { + local id="$1" + local name + name="$(docker inspect -f '{{.Name}}' "$id" 2>/dev/null | sed 's#^/##' || true)" + [ -n "$name" ] || name="$id" + + docker logs "$id" >"${outdir}/${name}.log" 2>&1 || true + docker inspect "$id" >"${outdir}/${name}.inspect.json" 2>&1 || true + docker rm -f "$id" >/dev/null 2>&1 || true +} + +ids=() + +# 1) Labeled containers (always). +while IFS= read -r id; do + [ -n "$id" ] && ids+=("$id") +done < <(docker ps -aq --filter "label=${label}" || true) + +# 2) Aggressive: any container with the repo bind-mounted. +if [ "${aggressive}" = "1" ]; then + while IFS= read -r id; do + [ -n "$id" ] || continue + # Matches containers mounting the repo path anywhere. + if docker inspect -f '{{range .Mounts}}{{println .Source}}{{end}}' "$id" 2>/dev/null | /usr/bin/grep -Fqx "${repo_root}"; then + ids+=("$id") + fi + done < <(docker ps -aq || true) +fi + +# De-dupe. +uniq_ids=() +seen=" " +for id in "${ids[@]}"; do + case "$seen" in + *" $id "*) continue ;; + *) seen="${seen}${id} "; uniq_ids+=("$id") ;; + esac +done + +if [ "${#uniq_ids[@]}" -eq 0 ]; then + echo "No containers to clean (label=${label}${aggressive:+ or mount=${repo_root}})" + exit 0 +fi + +echo "Capturing logs+inspect to ${outdir}" +echo "Removing ${#uniq_ids[@]} container(s): ${uniq_ids[*]}" +for id in "${uniq_ids[@]}"; do + capture_and_remove "$id" +done + +echo "Done." + diff --git a/scripts/v9fs-guest-run b/scripts/v9fs-guest-run index 9c125de..352761c 100755 --- a/scripts/v9fs-guest-run +++ b/scripts/v9fs-guest-run @@ -5,74 +5,60 @@ tests="${1:-smoke}" type="${2:-ci}" check="${3:-1}" -repo="/mnt/9/test" +# When invoked via `cpu`, we want to write logs into the host-mounted repo. +repo="/mnt/9/workspaces/share/test" ts="${TIMESTAMP:-$(date +%s)}" -PATH_MNTDIR="${PATH_MNTDIR:-/mnt/9/tmp}" -TESTBIN="${TESTBIN:-/mnt/9/testbin}" - -mkdir -p "$repo/logs/$ts" +logdir="$repo/logs/$ts" +mkdir -p "$logdir" rm -f "$repo/logs/current" ln -s "$ts" "$repo/logs/current" +guestlog="$logdir/guest.log" +rcfile="$logdir/guest.exitcode" + +exec >"$guestlog" 2>&1 + echo "GUEST: TYPE=$type TESTS=$tests CHECK=$check" echo "GUEST: repo=$repo" -echo "GUEST: PATH_MNTDIR=$PATH_MNTDIR" -echo "GUEST: TESTBIN=$TESTBIN" +echo "GUEST: mounted /mnt/9: $(mount | grep ' /mnt/9 ' || true)" -mkdir -p "$PATH_MNTDIR" -rm -rf "$PATH_MNTDIR"/* +tmpdir="/mnt/9/workspaces/share/tmpdir" +mkdir -p "$tmpdir" rc=0 -for f in "$repo"/fstabs-"$tests"/*; do - ft="$(basename "$f")" - mode="$(echo "$ft" | awk -F- '{print $1}')" - cfg="$(echo "$ft" | awk -F- '{print $2}')" - - outdir="$repo/logs/$ts/$mode/$cfg" - mkdir -p "$outdir" - - echo "GUEST: running configuration $mode $cfg" - - # Ensure mountpoints exist - mkdir -p /mnt/9 /mnt/root /dev /sys /proc - - # Apply the provided fstab inside guest - cp "$f" /etc/fstab - umount /mnt/9 2>/dev/null || true - umount /mnt/root 2>/dev/null || true - mount -a || true - - results="/tmp/results.log" - : >"$results" - - for name in dbench fsx fsx-mmap ldconfig postmark; do - t="$repo/tests/$type/$name.bash" - [ -f "$t" ] || continue - echo -n "...running test $name ..." | tee -a "$results" - - LOG="$outdir/$name" - export LOG PATH_MNTDIR TESTBIN - - if /bin/sh "$t" >"$LOG.log" 2>&1; then - echo "SUCCESS" | tee -a "$results" - else - echo "FAIL" | tee -a "$results" - rc=1 - fi - done - - if [ "$check" = "1" ]; then - if ! diff -b "$results" "$repo/good/$cfg/results.log" >/dev/null 2>&1; then - echo "GUEST: Configuration $f FAILED" - cat "$results" - rc=1 - fi - fi -done - -echo "$rc" >"$repo/logs/$ts/guest.exitcode" 2>/dev/null || true +case "$tests" in + smoke) + t="$tmpdir/smoke.$$" + mkdir -p "$t" + + echo "GUEST: write/read basic file" + printf 'hello-9p\n' >"$t/hello.txt" || rc=1 + [ "$(cat "$t/hello.txt" 2>/dev/null || true)" = "hello-9p" ] || rc=1 + + echo "GUEST: rename + append" + mv "$t/hello.txt" "$t/renamed.txt" || rc=1 + printf 'more\n' >>"$t/renamed.txt" || rc=1 + grep -q 'more' "$t/renamed.txt" || rc=1 + + echo "GUEST: mkdir/list/rmdir" + mkdir -p "$t/dirA" || rc=1 + ls -la "$t" >/dev/null 2>&1 || rc=1 + rmdir "$t/dirA" || rc=1 + + echo "GUEST: unlink cleanup" + rm -f "$t/renamed.txt" || rc=1 + rmdir "$t" || rc=1 + ;; + *) + echo "GUEST: unknown tests=$tests" + rc=2 + ;; +esac + +echo "$rc" >"$rcfile" 2>/dev/null || true sync 2>/dev/null || true -echo "GUEST: tests done rc=$rc" +echo "GUEST: done rc=$rc" exit "$rc" + diff --git a/scripts/v9fs-prepare-benchmarks b/scripts/v9fs-prepare-benchmarks new file mode 100755 index 0000000..8373ba6 --- /dev/null +++ b/scripts/v9fs-prepare-benchmarks @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Build/install benchmark binaries into /workspaces/tmp/testbin so they are +# visible to the guest via the QEMU 9p root export (FSDEV_PATH=/). + +root="${TESTBIN_ROOT:-/workspaces/tmp/testbin}" +mkdir -p "${root}"/{fsx,postmark,dbench} + +need_apt=0 +if [ ! -x "${root}/dbench/dbench" ] || [ ! -x "${root}/postmark/postmark" ]; then + need_apt=1 +fi + +if [ "${need_apt}" = "1" ]; then + export DEBIAN_FRONTEND=noninteractive + apt-get update -y + apt-get install -y --no-install-recommends dbench postmark +fi + +if [ ! -x "${root}/dbench/dbench" ]; then + cp -f /usr/bin/dbench "${root}/dbench/dbench" +fi +if [ ! -f "${root}/dbench/client.txt" ]; then + if [ -f /usr/share/dbench/client.txt ]; then + cp -f /usr/share/dbench/client.txt "${root}/dbench/client.txt" + fi +fi + +if [ ! -x "${root}/postmark/postmark" ]; then + cp -f /usr/bin/postmark "${root}/postmark/postmark" +fi + +if [ ! -x "${root}/fsx/fsx" ]; then + tmp="$(mktemp -d)" + trap 'rm -rf "$tmp"' EXIT + curl -fsSL \ + https://raw.githubusercontent.com/freebsd/freebsd-src/main/tools/regression/fsx/fsx.c \ + -o "${tmp}/fsx.c" + gcc -O2 -D_GNU_SOURCE -D_POSIX_C_SOURCE=200809L -include stdint.h -include time.h \ + -Wall -Wextra "${tmp}/fsx.c" -o "${root}/fsx/fsx" + chmod +x "${root}/fsx/fsx" +fi + +chmod -R a+rX "${root}" || true + +echo "Prepared benchmarks under ${root}:" +ls -la "${root}/fsx/fsx" "${root}/postmark/postmark" "${root}/dbench/dbench" || true + diff --git a/scripts/v9fs-prepare-diod-regression b/scripts/v9fs-prepare-diod-regression new file mode 100755 index 0000000..6ebe964 --- /dev/null +++ b/scripts/v9fs-prepare-diod-regression @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Prepare a build tree for the diod sharness regression suite. +# Output is placed under /workspaces/tmp so it is visible to the guest via 9p-root export. + +src="${DIOD_SRC:-/workspaces/tmp/diod-src}" +build_link="${DIOD_BUILD:-/workspaces/tmp/diod-build}" +repo="${DIOD_REPO:-https://github.com/chaos/diod}" +ref="${DIOD_REF:-master}" + +export DEBIAN_FRONTEND=noninteractive +apt-get update -y +apt-get install -y --no-install-recommends \ + autoconf automake libtool pkg-config \ + gcc make \ + lua5.1 liblua5.1-0-dev \ + libcap-dev libmunge-dev libwrap0-dev \ + tclsh rsync perl \ + sudo \ + acl \ + dbench postmark + +# Clone/update source. +if [ ! -d "${src}/.git" ]; then + rm -rf "${src}" + git clone --depth 1 "${repo}" "${src}" +fi +git -C "${src}" fetch --depth 1 origin "${ref}" || true +git -C "${src}" checkout -q "${ref}" || true + +# Autotools bootstrap. +(cd "${src}" && ./autogen.sh) + +# Build in a content-addressed directory (avoids cleanup issues with mixed uid mappings). +build_real="/workspaces/tmp/diod-build-$(git -C "${src}" rev-parse --short HEAD)" +mkdir -p "${build_real}" + +(cd "${build_real}" && "${src}/configure") +(cd "${build_real}" && make -j"$(nproc)") + +# Record the build directory for the guest (symlinks can be awkward across 9p+bind mounts). +echo "${build_real}" > /workspaces/tmp/diod-build.LATEST + +# The guest writes test logs into the build tree; ensure it's writable over 9p +# regardless of uid mapping. +chmod -R a+rwX "${src}" "${build_real}" || true + +echo "Prepared diod build at ${build_real}" +ls -la "${build_real}/t" | head -n 50 || true + diff --git a/scripts/v9fs-run-tests b/scripts/v9fs-run-tests old mode 100644 new mode 100755 index 77bad06..2e3b8d2 --- a/scripts/v9fs-run-tests +++ b/scripts/v9fs-run-tests @@ -1,60 +1,88 @@ #!/usr/bin/env bash set -euo pipefail -tests="${1:-short}" -type="${2:-ci}" -check="${3:-1}" +tests="${1:-smoke}" # Ensure artifacts created by root inside Docker are readable by the # GitHub Actions runner user when uploading `logs/` as an artifact. umask 022 -# Default to the container-provided tools exposed to the guest via 9p. -export TESTBIN="${TESTBIN:-/home/v9fs-test/testbin}" - export TIMESTAMP="${TIMESTAMP:-$(date +%s)}" mkdir -p "logs/${TIMESTAMP}" export QEMULOG="logs/${TIMESTAMP}/qemu.log" export PIDFILE="/home/v9fs-test/qemu.pid" -# Build a stable export root so 9p sees our bind mounts. -mkdir -p /workspaces/share -mkdir -p /workspaces/share/test /workspaces/share/tmp /workspaces/share/kernel /workspaces/share/testbin -mountpoint -q /workspaces/share/test || mount --bind /home/v9fs-test/test /workspaces/share/test -mountpoint -q /workspaces/share/tmp || mount --bind /workspaces/tmp /workspaces/share/tmp -mountpoint -q /workspaces/share/kernel || mount --bind /workspaces/kernel /workspaces/share/kernel -mountpoint -q /workspaces/share/testbin || mount --bind /home/v9fs-test/testbin /workspaces/share/testbin +# Export the whole container root over 9p. +export FSDEV_PATH="/" +export KERNELBUILD="${KERNELBUILD:-/workspaces/kernel/.build}" + +# Prepare benchmark binaries when requested. +case "${tests}" in + smoke) ;; + fsx|postmark|dbench) + ./scripts/v9fs-prepare-benchmarks + ;; + diod-regression) + ./scripts/v9fs-prepare-diod-regression + ;; + *) + echo "ERROR: unknown tests=${tests} (expected smoke|fsx|postmark|dbench|diod-regression)" + exit 2 + ;; +esac + +# Tell v9fs-build-initrd which test to bake in. +export V9FS_TESTS="${tests}" -export FSDEV_PATH="/workspaces/share" +# Patch the image-provided initrd so u-root runs our `/bin/uinit`. +export INITRD="${INITRD:-/opt/v9fs/initrd.patched.cpio}" +./scripts/v9fs-build-initrd "${INITRD}" -# Run the suite inside the guest and poweroff when complete. -export EXTRA_APPEND="v9fs.run=1 v9fs.tests=${tests} v9fs.type=${type} v9fs.check=${check} v9fs.ts=${TIMESTAMP}" +# Pass timestamp so uinit writes guest.exitcode into logs//. +export EXTRA_APPEND="v9fs.ts=${TIMESTAMP} v9fs.tests=${tests}" + +# Run QEMU daemonized, but stream qemu.log into docker logs. +export QEMU_DAEMONIZE=1 +export QEMU_STREAM_LOG=0 /home/v9fs-test/test/qemu.bash -# Wait for QEMU to exit (guest powers off). +# Stream guest serial log into the docker log while we proceed. +tail_pid="" +if [ -f "${QEMULOG}" ]; then + tail -n +1 -F "${QEMULOG}" & + tail_pid="$!" +fi + +# Wait for QEMU to exit (guest should power off). if [ -f "${PIDFILE}" ]; then pid="$(cat "${PIDFILE}")" - while kill -0 "${pid}" 2>/dev/null; do + for _ in $(seq 1 120); do + kill -0 "${pid}" 2>/dev/null || break sleep 1 done + kill -0 "${pid}" 2>/dev/null && echo "WARNING: QEMU still running; killing" && kill "${pid}" 2>/dev/null || true fi -echo "QEMU run complete; logs at ${QEMULOG}" +if [ -n "${tail_pid}" ]; then + kill "${tail_pid}" 2>/dev/null || true +fi -# Make logs readable for artifact upload on the host runner. chmod -R a+rX "logs/${TIMESTAMP}" 2>/dev/null || true -# The guest writes a status marker into the shared logs directory. -# Use it to fail CI while still running the whole suite. guest_rc_file="logs/${TIMESTAMP}/guest.exitcode" -for _ in 1 2 3 4 5; do - [ -f "${guest_rc_file}" ] && break - sleep 1 -done +guest_rc_src="logs/guest.exitcode" +guest_log_src="logs/guest.log" + +if [ -f "${guest_rc_src}" ]; then + cp -f "${guest_rc_src}" "${guest_rc_file}" 2>/dev/null || true +fi +if [ -f "${guest_log_src}" ]; then + cp -f "${guest_log_src}" "logs/${TIMESTAMP}/guest.log" 2>/dev/null || true +fi if [ ! -f "${guest_rc_file}" ]; then - echo "WARNING: missing ${guest_rc_file}; treating as failure" + echo "WARNING: missing ${guest_rc_file} (and ${guest_rc_src}); treating as failure" exit 2 fi @@ -64,3 +92,5 @@ if [ "${guest_rc}" != "0" ]; then exit "${guest_rc}" fi +echo "OK: guest rc=0 (logs at logs/${TIMESTAMP}/)" +