-
Notifications
You must be signed in to change notification settings - Fork 286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
aya,aya-ebpf: implement RingBuf #629
Conversation
✅ Deploy Preview for aya-rs-docs ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify site configuration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TFTR!
I renamed the files from ringbuf
to ring_buf
to match the convention elsewhere.
e701bec
to
59dfdde
Compare
Okay, this is RFAL. |
59ea0cb
to
1cb308b
Compare
@dave-tucker Thanks for the review! Adjusting the size when loading the probe makes sense. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright @dave-tucker, this is RFAL.
ba53b67
to
c843ef2
Compare
69551ef
to
018a2bd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tamird I replaced the scheduler tracepoint tests with uprobes that share infrastructure with the other tests.The repro no longer occurred in the old formulation because the fetch_add
did some additional synchronization. Now I keep track of the consumer position separately in the structure. It works out well enough. With that I was able to get a reliable repro when changing the ordering in a number of iterations that doesn't take too long even under software emulation.
Reviewable status: 6 of 33 files reviewed, all discussions resolved (waiting on @alessandrod and @tamird)
b3ebefb
to
0e7d694
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 20 of 20 files at r62, 7 of 7 files at r63, 2 of 2 files at r64, 1 of 1 files at r65, 18 of 18 files at r66, 27 of 27 files at r67, 9 of 9 files at r68, 8 of 8 files at r69, 7 of 7 files at r70, 14 of 16 files at r71, 2 of 2 files at r72, all commit messages.
Reviewable status: all files reviewed, 10 unresolved discussions (waiting on @ajwerner and @alessandrod)
aya/src/maps/ring_buf.rs
line 132 at r72 (raw file):
let Self { data, consumer: ConsumerPos { pos, .. },
nit: destructure the metadata so we can see it only contains the mmap
aya/src/maps/ring_buf.rs
line 172 at r72 (raw file):
// Load the initial value of the consumer position. After this point we'll be the only // thread ever writing to this value, and we'll do it based on our local bookkeeping. We // just mmap'ed this data, so we can assume that the kernel has flushed the any previous
"the any"
aya/src/maps/ring_buf.rs
line 173 at r72 (raw file):
// thread ever writing to this value, and we'll do it based on our local bookkeeping. We // just mmap'ed this data, so we can assume that the kernel has flushed the any previous // consumers writes, so Relaxed would almost certainly work, but because this is
this is too many commas for one sentence.
aya/src/maps/ring_buf.rs
line 277 at r72 (raw file):
} = self; let pos = unsafe { mmap.ptr.cast().as_ref() }; let data_pages = mmap.as_ref().get(*data_offset..).unwrap();
should we panic with words here?
aya/src/maps/ring_buf.rs
line 324 at r72 (raw file):
let header_ptr = data .get(offset..offset + core::mem::size_of::<AtomicU32>()) .expect("offset out of bounds")
here and below should we panic with values?
test/integration-test/src/tests/ring_buf.rs
line 1 at r72 (raw file):
use core::panic;
why core?
test/integration-test/src/tests/ring_buf.rs
line 44 at r72 (raw file):
unsafe impl Pod for Registers {} /// Generate a variable length vector of u64s.
what is this comment?
test/integration-test/src/tests/ring_buf.rs
line 112 at r72 (raw file):
let WithData( RingBufTest { ref mut ring_buf,
why are these ref? aren't you destructuring an owned value here?
same below
test/integration-test/src/tests/ring_buf.rs
line 184 at r72 (raw file):
let raw_fd = ring_buf.as_raw_fd(); let async_fd = AsyncFd::new(raw_fd).unwrap();
this should hold the ring_buf rather than its fd
test/integration-test/src/tests/ring_buf.rs
line 409 at r72 (raw file):
impl WriterThread { // This is enough messages for the test to fail relatively reliably in a hardware accelerated VM // with 2 vCPUs when the bug was present.
nit: "when the ring buffer implementation uses xxx rather than xxx" so the reader knows how to reverify the claim - it won't be in git.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: all files reviewed, 11 unresolved discussions (waiting on @ajwerner and @alessandrod)
aya/src/maps/mod.rs
line 270 at r69 (raw file):
/// A [`HashMap`] map that uses a LRU eviction policy. LruHashMap(MapData), /// A [`PerCpuArray`] map
I added this commit, but if you wouldn't mind adding the missing periods in it, that'd be good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: all files reviewed, 9 unresolved discussions (waiting on @alessandrod and @tamird)
aya/src/maps/mod.rs
line 270 at r69 (raw file):
Previously, tamird (Tamir Duberstein) wrote…
I added this commit, but if you wouldn't mind adding the missing periods in it, that'd be good.
Done.
aya/src/maps/ring_buf.rs
line 132 at r72 (raw file):
Previously, tamird (Tamir Duberstein) wrote…
nit: destructure the metadata so we can see it only contains the mmap
Done.
aya/src/maps/ring_buf.rs
line 172 at r72 (raw file):
Previously, tamird (Tamir Duberstein) wrote…
"the any"
Reworded.
aya/src/maps/ring_buf.rs
line 173 at r72 (raw file):
Previously, tamird (Tamir Duberstein) wrote…
this is too many commas for one sentence.
Done.
aya/src/maps/ring_buf.rs
line 277 at r72 (raw file):
Previously, tamird (Tamir Duberstein) wrote…
should we panic with words here?
Done.
aya/src/maps/ring_buf.rs
line 324 at r72 (raw file):
Previously, tamird (Tamir Duberstein) wrote…
here and below should we panic with values?
Done.
test/integration-test/src/tests/ring_buf.rs
line 1 at r72 (raw file):
Previously, tamird (Tamir Duberstein) wrote…
why core?
Done.
test/integration-test/src/tests/ring_buf.rs
line 44 at r72 (raw file):
Previously, tamird (Tamir Duberstein) wrote…
what is this comment?
Done.
test/integration-test/src/tests/ring_buf.rs
line 112 at r72 (raw file):
Previously, tamird (Tamir Duberstein) wrote…
why are these ref? aren't you destructuring an owned value here?
same below
Done.
test/integration-test/src/tests/ring_buf.rs
line 184 at r72 (raw file):
Previously, tamird (Tamir Duberstein) wrote…
this should hold the ring_buf rather than its fd
Done.
test/integration-test/src/tests/ring_buf.rs
line 409 at r72 (raw file):
Previously, tamird (Tamir Duberstein) wrote…
nit: "when the ring buffer implementation uses xxx rather than xxx" so the reader knows how to reverify the claim - it won't be in git.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 23 of 23 files at r73, 7 of 7 files at r74, 16 of 16 files at r75, all commit messages.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @ajwerner and @alessandrod)
aya/src/maps/ring_buf.rs
line 57 at r75 (raw file):
} impl<T: core::borrow::Borrow<MapData>> RingBuf<T> {
can you purge core
? we're using std here.
test/integration-test/src/tests/ring_buf.rs
line 25 at r75 (raw file):
} impl core::ops::Add for Registers {
can you purge core
? we're using std here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @alessandrod and @tamird)
aya/src/maps/ring_buf.rs
line 57 at r75 (raw file):
Previously, tamird (Tamir Duberstein) wrote…
can you purge
core
? we're using std here.
Done.
test/integration-test/src/tests/ring_buf.rs
line 25 at r75 (raw file):
Previously, tamird (Tamir Duberstein) wrote…
can you purge
core
? we're using std here.
Done. Also imported some more things to remove some qualifications.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 3 of 3 files at r76, all commit messages.
Reviewable status: complete! all files reviewed, all discussions resolved (waiting on @alessandrod)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks so much to everyone who's worked on this - the quality of the code, comments, readability etc in this PR is amazing.
Left a few minor comments.
bpf/aya-bpf/src/maps/ring_buf.rs
Outdated
} | ||
|
||
impl RingBuf { | ||
/// Declare a BPF ring buffer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: here and everywhere, use eBPF not BPF
eBPF is the name of the linux technology, BPF is the old thing and (unfortunately) the name of the
LLVM target. They're used interchangeably in the kernel but I think we should consitently use eBPF in
aya.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
bpf/aya-bpf/src/maps/ring_buf.rs
Outdated
/// Declare a BPF ring buffer. | ||
/// | ||
/// The linux kernel requires that `byte_size` be a power-of-2 multiple of the page size. | ||
/// The loading program may coerce the size when loading the map. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's an unnecessary newline after the period. Sentences follow each other. They're only separated by whitespaces.
Paragraphs are separated by two newlines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
// Use the loader API to control the size of the ring_buf. | ||
let mut bpf = BpfLoader::new() | ||
.btf(Some(Btf::from_sys_fs().unwrap()).as_ref()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is btf strictly required?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not needed, not sure why this was here.
Self(RingBufTest::new(), { | ||
let mut rng = rand::thread_rng(); | ||
// Generate more entries than there is space so we can test dropping entries. | ||
let n = rng.gen_range(1..=RING_BUF_MAX_ENTRIES * 2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment doesn't match the actual value of n. This sometimes will test
values over RING_BUF_MAX_ENTRIES. I don't think this kind of randomness here is
good? We should make sure both cases (in range, out of range) are always tested.
In general randomness in tests should be avoided. A test fails, I run it
again and I have new inputs, so it may or may not fail. In this case the random
values for the entries are probably fine, but imo n
shouldn't be random here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I parameterized the tests on N and then added test cases so that the data sizes for the relevant tests are both under and over capacity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: all files reviewed, 12 unresolved discussions (waiting on @ajwerner and @alessandrod)
aya/Cargo.toml
line 23 at r76 (raw file):
Previously, alessandrod (Alessandro Decina) wrote…
Why this? default features include read which is a much larger superset of
read_core and compression, which we don't need.
The workspace Cargo.toml already says "default-features = false" so this is redundant.
Extracted into #812.
aya/src/maps/bloom_filter.rs
line 85 at r76 (raw file):
Previously, alessandrod (Alessandro Decina) wrote…
Here and everywhere else, why import from libc and not std::ffi? We've talked
about making the libc dependency optional, this seems a change in the wrong direction
I didn't know that was a goal. Extracted the opposite change into #811.
aya/src/maps/mod.rs
line 305 at r76 (raw file):
Previously, alessandrod (Alessandro Decina) wrote…
This sort of changes are good, but please next time consider splitting them in a
separate PR. This PR is already large and takes time to review, while sorting
variants is trivial and can be merged right away. It makes both PRs easier to
review and this one easier to rebase.
@ajwerner, this pull request is now in conflict and requires a rebase. |
mmap() is needed for the ring buffer implementation, so move it to a common module
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 25 of 25 files at r77, 9 of 9 files at r78, 16 of 16 files at r79, all commit messages.
Dismissed @alessandrod from 3 discussions.
Reviewable status: all files reviewed, 9 unresolved discussions (waiting on @ajwerner and @alessandrod)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TFTR!
bpf/aya-bpf/src/maps/ring_buf.rs
Outdated
/// | ||
/// Returns `None` if the ring buffer is full. | ||
/// | ||
/// Note: `T` must be aligned to no more than 8 bytes; it's not possible to fulfill larger |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've left handling larger alignments as an exercise for the reader; it's not clear it really comes up in practice.
The const_assert feature requires generic_const_exprs which is an incomplete feature. It makes me uneasy to opt into an incomplete feature. The hope is to make it always use const_assert once that feature is not marked as incomplete.
bpf/aya-bpf/src/maps/ring_buf.rs
Outdated
} | ||
|
||
impl RingBuf { | ||
/// Declare a BPF ring buffer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
bpf/aya-bpf/src/maps/ring_buf.rs
Outdated
/// Declare a BPF ring buffer. | ||
/// | ||
/// The linux kernel requires that `byte_size` be a power-of-2 multiple of the page size. | ||
/// The loading program may coerce the size when loading the map. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
// Use the loader API to control the size of the ring_buf. | ||
let mut bpf = BpfLoader::new() | ||
.btf(Some(Btf::from_sys_fs().unwrap()).as_ref()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not needed, not sure why this was here.
Self(RingBufTest::new(), { | ||
let mut rng = rand::thread_rng(); | ||
// Generate more entries than there is space so we can test dropping entries. | ||
let n = rng.gen_range(1..=RING_BUF_MAX_ENTRIES * 2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I parameterized the tests on N and then added test cases so that the data sizes for the relevant tests are both under and over capacity.
This implements the userspace binding for RingBuf. Instead of streaming the samples as heap buffers, the process_ring function takes a callback to which we pass the event's byte region, roughly following [libbpf]'s API design. This avoids a copy and allows marking the consumer pointer in a timely manner. [libbpf]: https://github.com/libbpf/libbpf/blob/master/src/ringbuf.c Additionally, integration tests are added to demonstrate the usage of the new APIs and to ensure that they work end-to-end. Co-authored-by: William Findlay <[email protected]> Co-authored-by: Tatsuyuki Ishi <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 3 of 3 files at r80, 4 of 4 files at r81, all commit messages.
Reviewable status: all files reviewed, 8 unresolved discussions (waiting on @alessandrod)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great, thanks again everyone 🙏🎉
This is awesome news since that feature was highly anticipated! 🙏 Hopefully it's going to be released soon such that everyone can integrate it easily. Thanks everyone! |
Based on @ishitatsuyuki's work in #294.
Note that this does not add any sort of special asynchronous support for reading from the ring buffer from userspace outside of what is offered by
AsyncFd
. Nevertheless, with this change, theRingBuf
is broadly useful.Closes: #12
Closes: #201
This change is