Skip to content

Commit 42fe0c2

Browse files
committed
Add support for pinned ring buffer operations in eBPF C code generation.
1 parent ae389d7 commit 42fe0c2

File tree

2 files changed

+93
-35
lines changed

2 files changed

+93
-35
lines changed

src/ebpf_c_codegen.ml

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2042,7 +2042,19 @@ let generate_ringbuf_operation ctx ringbuf_val op =
20422042
match op with
20432043
| RingbufReserve result_val ->
20442044
(* Generate bpf_ringbuf_reserve_dynptr call - modern dynptr API *)
2045-
let ringbuf_str = generate_c_value ctx ringbuf_val in
2045+
(* Handle pinned ring buffers specially to avoid address-of-rvalue issues *)
2046+
let ringbuf_str = match ringbuf_val.value_desc with
2047+
| IRVariable name when List.mem name ctx.pinned_globals ->
2048+
(* For pinned ring buffers, create a temporary pointer variable *)
2049+
let temp_var = fresh_var ctx "pinned_ringbuf" in
2050+
emit_line ctx (sprintf "void *%s;" temp_var);
2051+
emit_line ctx (sprintf "{ struct __pinned_globals *__pg = get_pinned_globals();" );
2052+
emit_line ctx (sprintf " %s = __pg ? __pg->%s : NULL; }" temp_var name);
2053+
temp_var
2054+
| _ ->
2055+
(* Regular ring buffer - use address-of *)
2056+
"&" ^ (generate_c_value ctx ringbuf_val)
2057+
in
20462058
let result_str = generate_c_value ctx result_val in
20472059

20482060
(* Calculate proper size based on the result type *)
@@ -2064,27 +2076,20 @@ let generate_ringbuf_operation ctx ringbuf_val op =
20642076
| _ -> "unknown type"))
20652077
in
20662078

2067-
(* Declare dynptr for the reservation - track it for later submit/discard *)
2068-
let base_name = match result_val.value_desc with
2069-
| IRRegister reg -> sprintf "ptr_%d" reg
2079+
(* Get consistent variable name for the result register *)
2080+
let result_var_name = match result_val.value_desc with
2081+
| IRRegister reg -> get_meaningful_var_name ctx reg result_val.val_type
20702082
| IRVariable name -> name
20712083
| _ -> "dynptr_data"
20722084
in
2073-
let dynptr_var = base_name ^ "_dynptr" in
2085+
2086+
(* Declare dynptr for the reservation - track it for later submit/discard *)
2087+
let dynptr_var = result_var_name ^ "_dynptr" in
20742088
emit_line ctx (sprintf "struct bpf_dynptr %s;" dynptr_var);
20752089

2076-
(* Ensure the data pointer variable is declared before the if block *)
2077-
(match result_val.value_desc with
2078-
| IRRegister reg ->
2079-
let c_type = ebpf_type_from_ir_type result_val.val_type in
2080-
let var_name = get_meaningful_var_name ctx reg result_val.val_type in
2081-
if not (Hashtbl.mem ctx.declared_registers reg) then (
2082-
emit_line ctx (sprintf "%s %s;" c_type var_name);
2083-
Hashtbl.add ctx.declared_registers reg ()
2084-
)
2085-
| _ -> ());
2090+
(* The data pointer variable will be declared by the function's register collection phase *)
20862091

2087-
emit_line ctx (sprintf "if (bpf_ringbuf_reserve_dynptr(&%s, %s, 0, &%s) == 0) {"
2092+
emit_line ctx (sprintf "if (bpf_ringbuf_reserve_dynptr(%s, %s, 0, &%s) == 0) {"
20882093
ringbuf_str size dynptr_var);
20892094

20902095
(* Get data pointer from dynptr *)
@@ -2337,11 +2342,16 @@ let rec generate_c_instruction ctx ir_instr =
23372342

23382343
| IRDeclareVariable (dest_val, typ, init_expr_opt) ->
23392344
(* Variable declaration with optional initialization *)
2340-
let var_name = get_meaningful_var_name ctx
2341-
(match dest_val.value_desc with
2342-
| IRRegister reg -> reg
2343-
| _ -> failwith "IRDeclareVariable target must be a register")
2344-
typ in
2345+
let var_name = match dest_val.value_desc with
2346+
| IRRegister reg ->
2347+
let name = get_meaningful_var_name ctx reg typ in
2348+
(* Store the declared name for this register to ensure consistency *)
2349+
Hashtbl.replace ctx.register_name_hints reg (String.split_on_char '_' name |> List.hd);
2350+
(* Mark this register as declared *)
2351+
Hashtbl.add ctx.declared_registers reg ();
2352+
name
2353+
| _ -> failwith "IRDeclareVariable target must be a register"
2354+
in
23452355

23462356
(* Special handling for different types in variable declarations *)
23472357
(match typ with
@@ -3296,8 +3306,14 @@ let collect_registers_in_function ir_func =
32963306
collect_in_value dest_val; collect_in_value flag_val
32973307
| IRObjectDelete ptr_val ->
32983308
collect_in_value ptr_val
3299-
| IRRingbufOp (ringbuf_val, _) ->
3300-
collect_in_value ringbuf_val
3309+
| IRRingbufOp (ringbuf_val, op) ->
3310+
collect_in_value ringbuf_val;
3311+
(* Also collect registers from ring buffer operation arguments *)
3312+
(match op with
3313+
| RingbufReserve result_val -> collect_in_value result_val
3314+
| RingbufSubmit data_val -> collect_in_value data_val
3315+
| RingbufDiscard data_val -> collect_in_value data_val
3316+
| RingbufOnEvent _ -> ())
33013317
in
33023318
List.iter (fun block ->
33033319
List.iter collect_in_instr block.instructions
@@ -3392,21 +3408,17 @@ let generate_c_function ctx ir_func =
33923408
(match dest_val.value_desc with
33933409
| IRRegister reg -> declared_registers := reg :: !declared_registers
33943410
| _ -> ())
3395-
| IRRingbufOp (_, op) ->
3396-
(* Collect registers that will be declared by ringbuf operations *)
3397-
(match op with
3398-
| RingbufReserve result_val ->
3399-
(match result_val.value_desc with
3400-
| IRRegister reg -> declared_registers := reg :: !declared_registers
3401-
| _ -> ())
3402-
| _ -> ())
3411+
(* IRRingbufOp registers are now handled by the regular collection process *)
34033412
| _ -> ()
34043413
) block.instructions
34053414
) ir_func.basic_blocks;
34063415

34073416
(* Declare temporary variables for all registers *)
34083417
let register_variable_map = collect_register_variable_mapping ir_func in
34093418

3419+
(* Add all pre-declared registers to the context's declared_registers hashtable *)
3420+
List.iter (fun reg -> Hashtbl.add ctx.declared_registers reg ()) !declared_registers;
3421+
34103422
(* Generate proper variable declarations for all registers *)
34113423
let register_declarations = ref [] in
34123424
List.iter (fun (reg, reg_type) ->
@@ -3434,14 +3446,16 @@ let generate_c_function ctx ir_func =
34343446
let var_name = get_meaningful_var_name ctx reg reg_type in
34353447
let decl = generate_ebpf_c_declaration reg_type var_name in
34363448
register_declarations := (decl ^ ";") :: !register_declarations;
3449+
Hashtbl.add ctx.declared_registers reg ();
34373450
"" (* Skip the simple type processing below *)
34383451
)
34393452
in
34403453
(* Handle simple types that use alias names *)
34413454
if effective_type <> "" then (
34423455
let var_name = get_meaningful_var_name ctx reg reg_type in
34433456
let simple_decl = sprintf "%s %s;" effective_type var_name in
3444-
register_declarations := simple_decl :: !register_declarations
3457+
register_declarations := simple_decl :: !register_declarations;
3458+
Hashtbl.add ctx.declared_registers reg ()
34453459
)
34463460
)
34473461
) all_registers;
@@ -3766,9 +3780,9 @@ let compile_multi_to_c_with_tail_calls
37663780

37673781
(* First pass: collect all callbacks *)
37683782
let temp_ctx = create_c_context () in
3769-
List.iter (fun ir_prog ->
3770-
generate_c_function temp_ctx ir_prog.entry_function
3771-
) ir_multi_prog.programs;
3783+
List.iter (fun ir_prog ->
3784+
generate_c_function temp_ctx ir_prog.entry_function
3785+
) ir_multi_prog.programs;
37723786

37733787
(* Emit collected callbacks BEFORE the actual functions *)
37743788
if temp_ctx.pending_callbacks <> [] then (

tests/test_ringbuf.ml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,49 @@ fn main() -> i32 { return 0 }
405405
check bool "Should have at least one eBPF program" false true
406406
)
407407

408+
(** Test eBPF C code generation for pinned ringbuf operations *)
409+
let test_pinned_ringbuf_ebpf_codegen () =
410+
let program = {|
411+
struct SecurityEvent {
412+
event_id: u32,
413+
severity: u32
414+
}
415+
416+
pin var security_events : ringbuf<SecurityEvent>(8192)
417+
418+
@xdp fn security_monitor(ctx: *xdp_md) -> xdp_action {
419+
var reserved = security_events.reserve()
420+
if (reserved != null) {
421+
reserved->event_id = 42
422+
reserved->severity = 1
423+
security_events.submit(reserved)
424+
}
425+
return XDP_PASS
426+
}
427+
428+
fn main() -> i32 { return 0 }
429+
|} in
430+
let ast = parse_string program in
431+
let ir_multi = generate_ir ast in
432+
if List.length ir_multi.programs > 0 then (
433+
(* Use the multi-program generation like the real compiler *)
434+
let c_code = Ebpf_c_codegen.generate_c_multi_program ir_multi in
435+
(* Test that pinned ring buffer uses temporary variable approach *)
436+
check bool "eBPF C code should contain pinned_ringbuf temporary variable" true
437+
(contains_substr c_code "pinned_ringbuf");
438+
check bool "eBPF C code should contain get_pinned_globals call" true
439+
(contains_substr c_code "get_pinned_globals");
440+
check bool "eBPF C code should contain bpf_ringbuf_reserve_dynptr with temp var" true
441+
(contains_substr c_code "bpf_ringbuf_reserve_dynptr(pinned_ringbuf");
442+
check bool "eBPF C code should contain bpf_ringbuf_submit_dynptr" true
443+
(contains_substr c_code "bpf_ringbuf_submit_dynptr");
444+
(* Test that it doesn't contain the problematic compound expression *)
445+
check bool "eBPF C code should not contain address-of compound expression" false
446+
(contains_substr c_code "&({ struct")
447+
) else (
448+
check bool "Should have at least one eBPF program" false true
449+
)
450+
408451
(** Test that ringbuf programs can be processed through the full pipeline *)
409452
let test_ringbuf_full_pipeline () =
410453
let program = {|
@@ -1101,6 +1144,7 @@ let () =
11011144
"code generation", [
11021145
test_case "IR generation" `Quick test_ringbuf_ir_generation;
11031146
test_case "eBPF C code generation" `Quick test_ringbuf_ebpf_codegen;
1147+
test_case "pinned ringbuf eBPF C code generation" `Quick test_pinned_ringbuf_ebpf_codegen;
11041148
test_case "full pipeline processing" `Quick test_ringbuf_full_pipeline;
11051149
];
11061150
"on_event functionality", [

0 commit comments

Comments
 (0)