Skip to content

Commit bcb682d

Browse files
chantradanielocfb
authored andcommitted
libbpf-rs: Add suport for repeat and duration in ProgramInput and ProgramOutput
The former, when non-zero, is used to run the program `repeat` times. The latter is the number of ns a run took on average. A test was added to confirm the behaviour. The program bumps a counter, said counter is verified to match `repeat`. Signed-off-by: Manu Bretelle <[email protected]>
1 parent 9c9ca16 commit bcb682d

File tree

4 files changed

+79
-0
lines changed

4 files changed

+79
-0
lines changed

libbpf-rs/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Unreleased
1414
- Implemented `Sync` for `Link`
1515
- Updated `libbpf-sys` dependency to `1.5.0`
1616
- Added `Program::attach_perf_event_with_opts` for attaching to perf events.
17+
- Added `ProgramInput::repeat` field to run a test multiple times.
18+
- Added `ProgramOutput::duration` field which represent the average duration per repetition.
1719

1820

1921
0.25.0-beta.1

libbpf-rs/src/program.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use std::path::Path;
2222
use std::ptr;
2323
use std::ptr::NonNull;
2424
use std::slice;
25+
use std::time::Duration;
2526

2627
use libbpf_sys::bpf_func_id;
2728

@@ -604,6 +605,9 @@ pub struct Input<'dat> {
604605
pub cpu: u32,
605606
/// The 'flags' value passed to the kernel.
606607
pub flags: u32,
608+
/// How many times to repeat the test run. A value of 0 will result in 1 run.
609+
// 0 being forced to 1 by the kernel: https://elixir.bootlin.com/linux/v6.2.11/source/net/bpf/test_run.c#L352
610+
pub repeat: u32,
607611
/// The struct is non-exhaustive and open to extension.
608612
#[doc(hidden)]
609613
pub _non_exhaustive: (),
@@ -621,6 +625,8 @@ pub struct Output<'dat> {
621625
pub context: Option<&'dat mut [u8]>,
622626
/// Output data filled by the program.
623627
pub data: Option<&'dat mut [u8]>,
628+
/// Average duration per repetition.
629+
pub duration: Duration,
624630
/// The struct is non-exhaustive and open to extension.
625631
#[doc(hidden)]
626632
pub _non_exhaustive: (),
@@ -1373,6 +1379,7 @@ impl<'obj> ProgramMut<'obj> {
13731379
mut data_out,
13741380
cpu,
13751381
flags,
1382+
repeat,
13761383
_non_exhaustive: (),
13771384
} = input;
13781385

@@ -1399,13 +1406,17 @@ impl<'obj> ProgramMut<'obj> {
13991406
opts.data_size_out = data_out.map(|data| data.len() as _).unwrap_or(0);
14001407
opts.cpu = cpu;
14011408
opts.flags = flags;
1409+
// safe to cast back to an i32. While the API uses an `int`: https://elixir.bootlin.com/linux/v6.2.11/source/tools/lib/bpf/bpf.h#L446
1410+
// the kernel user api uses __u32: https://elixir.bootlin.com/linux/v6.2.11/source/include/uapi/linux/bpf.h#L1430
1411+
opts.repeat = repeat as i32;
14021412

14031413
let rc = unsafe { libbpf_sys::bpf_prog_test_run_opts(self.as_fd().as_raw_fd(), &mut opts) };
14041414
let () = util::parse_ret(rc)?;
14051415
let output = Output {
14061416
return_value: opts.retval,
14071417
context: unsafe { slice_from_array(opts.ctx_out.cast(), opts.ctx_size_out as _) },
14081418
data: unsafe { slice_from_array(opts.data_out.cast(), opts.data_size_out as _) },
1419+
duration: Duration::from_nanos(opts.duration.into()),
14091420
_non_exhaustive: (),
14101421
};
14111422
Ok(output)

libbpf-rs/tests/bin/src/run_prog.bpf.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66

77
char _license[] SEC("license") = "GPL";
88

9+
struct {
10+
__uint(type, BPF_MAP_TYPE_ARRAY);
11+
__uint(key_size, sizeof(u32));
12+
__uint(value_size, sizeof(u32));
13+
__uint(max_entries, 1);
14+
} test_counter_map SEC(".maps");
15+
916
SEC("struct_ops/test_1")
1017
int BPF_PROG(test_1, struct bpf_dummy_ops_state *state)
1118
{
@@ -33,6 +40,18 @@ int BPF_PROG(test_2, struct bpf_dummy_ops_state *state, int a1,
3340
return 0;
3441
}
3542

43+
SEC("xdp")
44+
int xdp_counter(struct xdp_md *ctx)
45+
{
46+
u32 key = 0;
47+
u32 *value = bpf_map_lookup_elem(&test_counter_map, &key);
48+
if (value) {
49+
*value += 1;
50+
return XDP_PASS;
51+
}
52+
return XDP_DROP;
53+
}
54+
3655
SEC(".struct_ops")
3756
struct bpf_dummy_ops dummy_1 = {
3857
.test_1 = (void *)test_1,

libbpf-rs/tests/test.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2151,3 +2151,50 @@ fn test_run_prog_fail() {
21512151
let input = ProgramInput::default();
21522152
let _err = prog.test_run(input).unwrap_err();
21532153
}
2154+
2155+
/// Check that we can run a program with test_run with `repeat` set.
2156+
/// We set a counter in the program which we bump each time we run the
2157+
/// program.
2158+
/// We check that the counter is equal to the value of `repeat`.
2159+
/// We also check that the duration is non-zero.
2160+
#[tag(root)]
2161+
#[test]
2162+
fn test_run_prog_repeat_and_duration() {
2163+
let repeat = 100;
2164+
let payload: [u8; 16] = [
2165+
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, // src mac
2166+
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, // dst mac
2167+
0x08, 0x00, // ethertype
2168+
0x00, 0x00, // payload
2169+
];
2170+
let mut obj = get_test_object("run_prog.bpf.o");
2171+
let prog = get_prog_mut(&mut obj, "xdp_counter");
2172+
2173+
let input: ProgramInput<'_> = ProgramInput {
2174+
data_in: Some(&payload),
2175+
repeat,
2176+
..Default::default()
2177+
};
2178+
2179+
let output = prog.test_run(input).unwrap();
2180+
2181+
let map = get_map(&obj, "test_counter_map");
2182+
2183+
let counter = map
2184+
.lookup(&0u32.to_ne_bytes(), MapFlags::ANY)
2185+
.expect("failed to lookup counter")
2186+
.expect("failed to retrieve value");
2187+
2188+
assert_eq!(output.return_value, libbpf_sys::XDP_PASS);
2189+
assert_eq!(
2190+
counter,
2191+
repeat.to_ne_bytes(),
2192+
"counter {} != repeat {repeat}",
2193+
u32::from_ne_bytes(counter.clone().try_into().unwrap())
2194+
);
2195+
assert_ne!(
2196+
output.duration,
2197+
Duration::ZERO,
2198+
"duration should be non-zero"
2199+
);
2200+
}

0 commit comments

Comments
 (0)