Skip to content

feat: improve struct_ops codegen lifecycle and test coverage#17

Open
SiyuanSun0736 wants to merge 2 commits intomultikernel:mainfrom
SiyuanSun0736:main
Open

feat: improve struct_ops codegen lifecycle and test coverage#17
SiyuanSun0736 wants to merge 2 commits intomultikernel:mainfrom
SiyuanSun0736:main

Conversation

@SiyuanSun0736
Copy link
Copy Markdown
Contributor

Summary

This PR improves struct_ops code generation and strengthens regression coverage across both eBPF and userspace paths.

Key changes:

  • keep internal struct_ops function calls as direct calls instead of lowering them into implicit tail calls
  • synthesize a stable default name for struct_ops instances when the kernel struct expects a name field but the impl block omits it
  • improve userspace struct_ops lifecycle generation around main so registration results, attach helpers, wait flow, and detach/cleanup behavior are emitted more reliably
  • expand struct_ops tests to cover runtime helpers, cleanup hooks, attach/detach helpers, EPERM diagnostics, and direct-call behavior

Details

In IR generation:

  • tracked the current program type during lowering so struct_ops methods are not treated like normal attributed programs for implicit tail-call lowering
  • added logic to preserve direct calls for internal struct_ops method-to-method invocations
  • generated a default struct_ops name automatically when the target kernel struct has a name field and no explicit static field is provided

In userspace code generation:

  • improved detection of the main registration flow for struct_ops programs
  • preserved the registration result through generated attach/wait/detach lifecycle code
  • strengthened lifecycle generation so cleanup helpers, unregister waiting, and detach handling are validated by tests

In examples and tests:

  • updated the struct_ops example to include an internal helper-style call path through undo_cwnd -> ssthresh
  • added regression coverage ensuring struct_ops internal calls remain direct calls
  • expanded userspace struct_ops codegen tests to verify runtime helper emission, cleanup registration, attach/detach helpers, and struct_ops-specific load diagnostics

Testing

  • extended struct_ops code generation unit tests
  • added regression coverage for direct internal calls in struct_ops impl blocks
  • added userspace codegen assertions for runtime helpers and cleanup flow

… 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.
Copilot AI review requested due to automatic review settings April 10, 2026 14:07
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_ops method-to-method invocations and avoid lowering them into implicit tail calls.
  • Synthesize a stable default struct_ops instance name when the kernel struct has a name field and the impl omits it.
  • Improve userspace main() lifecycle generation for struct_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.

Comment on lines +2990 to +2992
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;
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
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;

Copilot uses AI. Check for mistakes.
Comment on lines +2834 to +2837
let lifecycle_code = sprintf {|if (%s != 0) {
%s = %s;
return 1;
}
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
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)")
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
(contains_substr c_code "bpf_tail_call(ctx, &prog_array, 0)")
(contains_substr c_code "bpf_tail_call(ctx, &prog_array")

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants