feat: improve struct_ops codegen lifecycle and test coverage#17
feat: improve struct_ops codegen lifecycle and test coverage#17SiyuanSun0736 wants to merge 2 commits intomultikernel:mainfrom
Conversation
… tests - Added `undo_cwnd` function to `minimal_congestion_control` implementation. - Updated test for struct_ops compilation completeness to verify direct calls. - Introduced new test for ensuring internal calls remain direct instead of tail calls.
There was a problem hiding this comment.
Pull request overview
This PR refines struct_ops code generation to better preserve intended call semantics (direct internal calls), auto-fill missing instance metadata (default .name), and generate a more reliable userspace registration/attach/wait/detach lifecycle, with expanded regression tests across both eBPF and userspace outputs.
Changes:
- Preserve direct calls for internal
struct_opsmethod-to-method invocations and avoid lowering them into implicit tail calls. - Synthesize a stable default
struct_opsinstance name when the kernel struct has anamefield and the impl omits it. - Improve userspace
main()lifecycle generation forstruct_ops(runtime checks, link lifetime, attach/wait/detach/cleanup) and expand tests accordingly.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/ir_generator.ml |
Tracks current program type to prevent implicit tail-call lowering for struct_ops, and synthesizes a default instance name when omitted. |
src/userspace_codegen.ml |
Reworks struct_ops registration to use generated attach/detach helpers, adds runtime helpers/cleanup/wait flow, and adds EPERM diagnostics. |
tests/test_struct_ops.ml |
Expands userspace lifecycle assertions, adds synthesized-name coverage, and adds a regression test ensuring internal calls stay direct. |
examples/struct_ops_simple.ks |
Updates the example to include an internal helper-style call path (undo_cwnd -> ssthresh). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/userspace_codegen.ml
Outdated
| fprintf(stderr, "struct_ops loading requires root privileges or CAP_BPF/CAP_SYS_ADMIN.\n"); | ||
| fprintf(stderr, "Run this example with sudo, for example: sudo ./%s\n"); | ||
| return -1; |
There was a problem hiding this comment.
ensure_struct_ops_privileges() claims struct_ops loading works with root or CAP_BPF/CAP_SYS_ADMIN, but it only checks geteuid() == 0 and hard-fails otherwise. This will incorrectly reject setups that run unprivileged with the required capabilities. Consider either (a) making this a warning and letting load/attach fail naturally, (b) checking capabilities explicitly, or (c) updating the message to match the actual requirement (root only).
| fprintf(stderr, "struct_ops loading requires root privileges or CAP_BPF/CAP_SYS_ADMIN.\n"); | |
| fprintf(stderr, "Run this example with sudo, for example: sudo ./%s\n"); | |
| return -1; | |
| fprintf(stderr, "Warning: struct_ops loading typically requires root privileges or CAP_BPF/CAP_SYS_ADMIN.\n"); | |
| fprintf(stderr, "Continuing anyway; loading may still succeed if this process has the required capabilities.\n"); | |
| fprintf(stderr, "If it fails with a permission error, try: sudo ./%s\n"); | |
| return 0; |
| let lifecycle_code = sprintf {|if (%s != 0) { | ||
| %s = %s; | ||
| return 1; | ||
| } |
There was a problem hiding this comment.
The generated struct_ops lifecycle code sets the registration result variable but then returns a hard-coded 1 on attach failure (if (attach_status != 0) { ... return 1; }). This loses the actual error code produced by attach_struct_ops_*() and deviates from the user-written return result; pattern. Returning the real registration result (or propagating attach_status) would better preserve semantics and diagnostics.
tests/test_struct_ops.ml
Outdated
| check bool "struct_ops direct call emitted" true | ||
| (contains_substr c_code "ssthresh(sk)"); | ||
| check bool "struct_ops tail call not emitted" false | ||
| (contains_substr c_code "bpf_tail_call(ctx, &prog_array, 0)") |
There was a problem hiding this comment.
This regression test asserts that no tail call is emitted by searching for the exact string bpf_tail_call(ctx, &prog_array, 0). Tail-call indices (or emitted formatting) can change without changing the underlying bug, which could let regressions slip through. Consider checking for a more general bpf_tail_call(ctx, &prog_array/bpf_tail_call( substring (or absence of prog_array) instead of a specific index literal.
| (contains_substr c_code "bpf_tail_call(ctx, &prog_array, 0)") | |
| (contains_substr c_code "bpf_tail_call(ctx, &prog_array") |
…s for runtime helpers and cleanup functions
Summary
This PR improves struct_ops code generation and strengthens regression coverage across both eBPF and userspace paths.
Key changes:
Details
In IR generation:
In userspace code generation:
In examples and tests:
Testing