Skip to content
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

Add helpers for querying info from existing bpf programs #675

Merged
merged 3 commits into from
Nov 18, 2023

Conversation

preuss-adam
Copy link
Contributor

@preuss-adam preuss-adam commented Jul 18, 2023

This change adds additional getter functions to Aya's BPF objects to extract the map ids of a loaded program and extract the map name from a loaded map. It also supports loading a map object by fd, similar to what already exists for program objects.


This change is Reviewable

@netlify
Copy link

netlify bot commented Jul 18, 2023

Deploy Preview for aya-rs-docs ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit cae2910
🔍 Latest deploy log https://app.netlify.com/sites/aya-rs-docs/deploys/65568c4507bed0000891ff43
😎 Deploy Preview https://deploy-preview-675--aya-rs-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@astoycos
Copy link
Member

astoycos commented Jul 18, 2023

@preuss-adam #637 should have most of what you need

Minus the additions to the map API I'm thinking we should go ahead and have something equivalent to the loaded_programs API for maps... Probably just loaded_maps so that we can link Programs to maps and vice versa regardless of if they were loaded via aya or not.

@preuss-adam
Copy link
Contributor Author

@astoycos
Thanks. I’ll rebase my other two commits once that PR goes in. For the loaded_maps were you thinking something like just adding a pub struct MapInfo(bpf_map_info); and the associated iterator?

@astoycos
Copy link
Member

For the loaded_maps were you thinking something like just adding a pub struct MapInfo(bpf_map_info); and the associated iterator?

Yeah at least for the first go that's what I was thinking! I'd also like to do the same for bpf_links as well

@mergify
Copy link

mergify bot commented Sep 1, 2023

@preuss-adam, this pull request is now in conflict and requires a rebase.

@mergify mergify bot added needs-rebase aya This is about aya (userspace) labels Sep 1, 2023
@mergify mergify bot removed the needs-rebase label Oct 10, 2023
@preuss-adam
Copy link
Contributor Author

@astoycos please see latest changes

@astoycos astoycos self-assigned this Oct 25, 2023
@astoycos
Copy link
Member

Will take a look here, sorry for the delay @preuss-adam !

@tamird tamird requested a review from astoycos October 25, 2023 14:27
Copy link
Member

@tamird tamird left a comment

Choose a reason for hiding this comment

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

Reviewed 2 of 2 files at r1, all commit messages.
Reviewable status: all files reviewed, 8 unresolved discussions (waiting on @astoycos and @preuss-adam)


aya/src/maps/mod.rs line 553 at r1 (raw file):

    }

    /// Loads a map from a map id

missing period.


aya/src/maps/mod.rs line 554 at r1 (raw file):

    /// Loads a map from a map id
    pub fn from_id(id: u32) -> Result<Self, MapError> {

this is public API, you'll need to run cargo +nightly xtask public-api --bless to update the API fixtures.


aya/src/maps/mod.rs line 555 at r1 (raw file):

    /// Loads a map from a map id
    pub fn from_id(id: u32) -> Result<Self, MapError> {
        Self::from_fd(bpf_map_get_fd_by_id(id)?)

nit: consider bpf_map_get_fd_by_id(id).and_then(Self::from_fd); the ? inside an expresion can be hard to read.


aya/src/maps/mod.rs line 621 at r1 (raw file):

    }

    /// Returns the name of the map

missing period.


aya/src/maps/mod.rs line 622 at r1 (raw file):

    /// Returns the name of the map
    pub fn name(&self) -> Result<String, MapError> {

this needs a test.


aya/src/maps/mod.rs line 625 at r1 (raw file):

        let info = bpf_map_get_info_by_fd(self.fd.as_fd())?;
        Ok(
            unsafe { std::str::from_utf8(std::mem::transmute(info.name.as_slice())).unwrap() }

is there a reason we can't use CStr::from_from_bytes_until_nul here? or even CStr::from_ptr which would avoid the neeed for the transmute.


aya/src/maps/mod.rs line 849 at r1 (raw file):

/// iteration can't be performed, for example the caller does not have the necessary privileges,
/// a single item will be yielded containing the error that occurred.
pub fn loaded_maps() -> impl Iterator<Item = Result<MapData, MapError>> {

this needs a test.


aya/src/maps/mod.rs line 852 at r1 (raw file):

    iter_map_ids()
        .map(|id| {
            let id = id?;

nit: might be clearer as id.and_then(bpf_map_get_fd_by_id).and_then(MapData::from_fd)

Copy link
Member

@astoycos astoycos left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @preuss-adam!

So some general design things as mentioned in the review...specifically

There's kind of two separate APIs here

  1. A wrapper around the kernel's bpf_map_info struct
  2. Aya's Map object

I think it would be cool to have a from_map_info API as well as the from_id one you've provided here.

Also we probably should add more methods on MapInfo for the relevant/useful fields from https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/bpf.h#L6358

I'm thinking at the very least for the following:

struct bpf_map_info {
	__u32 type;
	__u32 id;
	__u32 key_size;
	__u32 value_size;
	__u32 max_entries;
	__u32 map_flags;
	char  name[BPF_OBJ_NAME_LEN];

lastly we'll need some testing coverage for all of this functionality. Which ultimately should include

  1. Loading an aya::Map from a map ID
  2. Listing all aya::map::MapInfos
  3. Calling all methods on aya::map::MapInfos
  4. Converting a MapInfo into a Map

If you want to break this work into multiple PRs as well that's completely fine.

/// next map id, get the map fd, or the [`MapData`] fail. In cases where
/// iteration can't be performed, for example the caller does not have the necessary privileges,
/// a single item will be yielded containing the error that occurred.
pub fn loaded_maps() -> impl Iterator<Item = Result<MapData, MapError>> {
Copy link
Member

Choose a reason for hiding this comment

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

So I think we want to align with the api we provide for programs on this one. Therefore we should probably return a MapInfo which looks like

/// Provides information about a loaded Map, like name, id and statistics
#[derive(Debug)]
pub struct MapInfo(bpf_map_info);

rather then reading directly into MapData which wasn't designed to actually reflect all of the kernel state.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good. Removed this for now and will do this in follow up PR.

@@ -612,6 +618,16 @@ impl MapData {
fd
}

/// Returns the name of the map
pub fn name(&self) -> Result<String, MapError> {
Copy link
Member

Choose a reason for hiding this comment

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

again let's use the same method as ProgramInfo.name() here https://github.com/aya-rs/aya/blob/main/aya/src/programs/mod.rs#L1004

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can't quite make it like this yet, as we don't have the struct to reference. Will do as part of follow up PR once we have MapInfo(bpf_map_info);.

pub(crate) fn bpf_map_get_fd_by_id(map_id: u32) -> Result<OwnedFd, SyscallError> {
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };

attr.__bindgen_anon_6.__bindgen_anon_1.prog_id = map_id;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
attr.__bindgen_anon_6.__bindgen_anon_1.prog_id = map_id;
attr.__bindgen_anon_6.__bindgen_anon_1.map_id = map_id;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops. Fixed

@mergify
Copy link

mergify bot commented Oct 26, 2023

Hey @alessandrod, this pull request changes the Aya Public API and requires your review.

@mergify mergify bot requested a review from alessandrod October 26, 2023 21:12
@mergify mergify bot added api/needs-review Makes an API change that needs review test A PR that improves test cases or CI labels Oct 26, 2023
@preuss-adam
Copy link
Contributor Author

Thank you both for the review comments. For now, I've pulled out the last commit with loaded_maps as that will require a bit more reworking. The other two commits have been updated.

@tamird
Copy link
Member

tamird commented Oct 26, 2023

Thank you both for the review comments. For now, I've pulled out the last commit with loaded_maps as that will require a bit more reworking. The other two commits have been updated.

Could you respond to the comments (in reviewable, where applicable, please)?

Copy link
Contributor Author

@preuss-adam preuss-adam left a comment

Choose a reason for hiding this comment

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

Reviewable status: 0 of 3 files reviewed, 9 unresolved discussions (waiting on @alessandrod, @astoycos, and @tamird)


aya/src/maps/mod.rs line 553 at r1 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

missing period.

Done.


aya/src/maps/mod.rs line 554 at r1 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

this is public API, you'll need to run cargo +nightly xtask public-api --bless to update the API fixtures.

Done.


aya/src/maps/mod.rs line 621 at r1 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

missing period.

Done.


aya/src/maps/mod.rs line 622 at r1 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

this needs a test.

Done.


aya/src/maps/mod.rs line 625 at r1 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

is there a reason we can't use CStr::from_from_bytes_until_nul here? or even CStr::from_ptr which would avoid the neeed for the transmute.

Done.


aya/src/maps/mod.rs line 849 at r1 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

this needs a test.

This will come in as part of the follow up PR. Commit removed for now.


aya/src/maps/mod.rs line 852 at r1 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

nit: might be clearer as id.and_then(bpf_map_get_fd_by_id).and_then(MapData::from_fd)

Done

@tamird tamird requested a review from astoycos October 26, 2023 23:27
Copy link
Member

@tamird tamird left a 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 r2, 1 of 1 files at r3, all commit messages.
Reviewable status: all files reviewed, 4 unresolved discussions (waiting on @alessandrod and @astoycos)


aya/src/maps/mod.rs line 625 at r1 (raw file):

Previously, preuss-adam wrote…

Done.

I think we should reconcile this with https://github.com/aya-rs/aya/blob/main/aya/src/programs/mod.rs#L1004 as @astoycos suggested. It's weird that we have different implementations for these things.

Copy link
Contributor Author

@preuss-adam preuss-adam left a comment

Choose a reason for hiding this comment

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

Should be all addressed now.

Reviewable status: 0 of 3 files reviewed, 4 unresolved discussions (waiting on @alessandrod, @astoycos, and @tamird)


aya/src/maps/mod.rs line 622 at r1 (raw file):

Previously, preuss-adam wrote…

Can't quite make it like this yet, as we don't have the struct to reference. Will do as part of follow up PR once we have MapInfo(bpf_map_info);.

Done. New commit on this PR.


aya/src/maps/mod.rs line 625 at r1 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

I think we should reconcile this with https://github.com/aya-rs/aya/blob/main/aya/src/programs/mod.rs#L1004 as @astoycos suggested. It's weird that we have different implementations for these things.

Done. New commit on this PR.


aya/src/maps/mod.rs line 849 at r1 (raw file):

Previously, preuss-adam wrote…

Sounds good. Removed this for now and will do this in follow up PR.

Done. New commit on this PR.

Copy link
Member

@tamird tamird left a 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 r4, all commit messages.
Reviewable status: all files reviewed, 7 unresolved discussions (waiting on @alessandrod, @astoycos, and @preuss-adam)


aya/src/maps/mod.rs line 625 at r1 (raw file):

Previously, preuss-adam wrote…

Done. New commit on this PR.

I think I don't follow. The implementation still seems to be duplicated rather than shared.


aya/src/maps/mod.rs line 661 at r4 (raw file):

        let info = MapInfo::new_from_fd(fd.as_fd())?;
        Ok(Self {
            obj: parse_map_info(info.0, PinningType::None),

consider writing let MapInfo(info) = MapInfo::new_from_fd(...)? to avoid this .0.


aya/src/maps/mod.rs line 719 at r4 (raw file):

    }

    /// Returns information about a loaded map with the [`MapInfo`] structure.

consider avoiding repeating the signature in the commit. also "a map" is weird, it's this map, isn't it?


aya/src/maps/mod.rs line 943 at r4 (raw file):

    }

    /// The name of the map as a &str. If the name was not valid unicode, None is returned.

s/was/is/

@preuss-adam preuss-adam force-pushed the apreuss-bpf-helpers branch 2 times, most recently from 64f5499 to b138a5a Compare October 31, 2023 20:51
Copy link
Contributor Author

@preuss-adam preuss-adam left a 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, 7 unresolved discussions (waiting on @alessandrod, @astoycos, and @tamird)


aya/src/maps/mod.rs line 625 at r1 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

I think I don't follow. The implementation still seems to be duplicated rather than shared.

Depends how you interpret "same method" :) In an earlier version, it did it completely differently. I could put the common code into util if you think that would be cleaner?


aya/src/maps/mod.rs line 661 at r4 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

consider writing let MapInfo(info) = MapInfo::new_from_fd(...)? to avoid this .0.

fixed


aya/src/maps/mod.rs line 719 at r4 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

consider avoiding repeating the signature in the commit. also "a map" is weird, it's this map, isn't it?

yes, fixed.


aya/src/maps/mod.rs line 943 at r4 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

s/was/is/

fixed

Copy link
Member

@tamird tamird left a comment

Choose a reason for hiding this comment

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

Reviewed 1 of 1 files at r5, all commit messages.
Reviewable status: all files reviewed, 5 unresolved discussions (waiting on @alessandrod, @astoycos, and @preuss-adam)


aya/src/maps/mod.rs line 625 at r1 (raw file):

Previously, preuss-adam wrote…

Depends how you interpret "same method" :) In an earlier version, it did it completely differently. I could put the common code into util if you think that would be cleaner?

Yeah, I think it would be preferable to avoid duplication.


aya/src/maps/mod.rs line 943 at r4 (raw file):

Previously, preuss-adam wrote…

fixed

on second thought, do we need this method? the caller just as easily do this, right?

Copy link
Contributor Author

@preuss-adam preuss-adam left a 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, 5 unresolved discussions (waiting on @alessandrod, @astoycos, and @tamird)


aya/src/maps/mod.rs line 943 at r4 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

on second thought, do we need this method? the caller just as easily do this, right?

Not really. Only reason is ProgramInfo has it.

Copy link
Contributor Author

@preuss-adam preuss-adam left a 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, 5 unresolved discussions (waiting on @alessandrod, @astoycos, and @tamird)


aya/src/maps/mod.rs line 625 at r1 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

Yeah, I think it would be preferable to avoid duplication.

done

Copy link
Member

@tamird tamird left a 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 r6, all commit messages.
Reviewable status: all files reviewed, 5 unresolved discussions (waiting on @alessandrod, @astoycos, and @preuss-adam)


aya/src/util.rs line 374 at r6 (raw file):

        .unwrap_or(0);

    // The name field is defined as [std::os::raw::c_char; 16]. c_char may be signed or

this comment is talking about something at quite some distance. maybe the signature of this function should be bpf_name: &[std::os::raw::c_char; 16] so that this contract is obvious? then you can just talk about the cast and why it's done.


aya/src/maps/mod.rs line 943 at r4 (raw file):

Previously, preuss-adam wrote…

Not really. Only reason is ProgramInfo has it.

will let @astoycos weigh in on whether either method makes sense.

@preuss-adam
Copy link
Contributor Author

@astoycos Any other thoughts on this PR? What are the next steps to be able to submit? Thanks.

Copy link
Member

@astoycos astoycos left a comment

Choose a reason for hiding this comment

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

Sorry for the delay I was at Kubcon NA last week 😢 , This is looking really good to me just a few small things, thanks for the hard work especially with the unit tests 🥳 (I'll have to go back and add the same for the loaded_programs api)

One last thing can you also add a smoke test like https://github.com/aya-rs/aya/blob/main/test/integration-test/src/tests/smoke.rs#L74 It should be super simple

will let @astoycos weigh in on whether either method makes sense.

Providing the map name as a &str just makes it easier for the user, I like keeping it consistent with the ProgramInfo API 👍

Lastly looks like we need a cargo xtask public-api --bless

Thanks!

aya/src/maps/mod.rs Outdated Show resolved Hide resolved
aya/src/maps/mod.rs Outdated Show resolved Hide resolved
aya/src/util.rs Outdated
@@ -364,6 +364,18 @@ pub(crate) fn bytes_of_slice<T: Pod>(val: &[T]) -> &[u8] {
unsafe { slice::from_raw_parts(val.as_ptr().cast(), size) }
}

pub(crate) fn bytes_of_bpf_name(bpf_name: &[i8]) -> &[u8] {
Copy link
Member

Choose a reason for hiding this comment

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

This won't be i8 for all architectures unfortunately (I've run into this before)

I think you'd either need to make use of generics OR just duplicate the logic for now unless @tamird Knows of a rusty way around this :)

Copy link
Member

Choose a reason for hiding this comment

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

Or maybe use a ffi c type?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. Works well with the ffi c type.

This adds support to loading maps by fd similarly to the way programs
can be loaded by fd.
@preuss-adam
Copy link
Contributor Author

@astoycos No problem. Changes are pushed. Thanks.

@preuss-adam preuss-adam force-pushed the apreuss-bpf-helpers branch 3 times, most recently from 0a15a93 to f5af331 Compare November 16, 2023 19:56
This makes the APIs for loading maps and programs more similar.
Copy link
Member

@astoycos astoycos left a comment

Choose a reason for hiding this comment

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

LGTM

Thanks for all the hard work @preuss-adam I'm looking forward to this

Just need @alessandrod To checkout the API changes

@tamird tamird requested a review from astoycos November 16, 2023 21:12
Copy link
Member

@tamird tamird left a comment

Choose a reason for hiding this comment

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

Reviewed 5 of 5 files at r7, 4 of 4 files at r8, 3 of 3 files at r9, all commit messages.
Dismissed @astoycos from 5 discussions.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on @alessandrod, @astoycos, and @preuss-adam)


aya/src/util.rs line 367 at r6 (raw file):

Previously, preuss-adam wrote…

Done. Works well with the ffi c type.

Do we need the leading :: on ::core?


aya/src/util.rs line 374 at r6 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

this comment is talking about something at quite some distance. maybe the signature of this function should be bpf_name: &[std::os::raw::c_char; 16] so that this contract is obvious? then you can just talk about the cast and why it's done.

Ping this is still unresolved.

Copy link
Member

@tamird tamird left a comment

Choose a reason for hiding this comment

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

Reviewed 1 of 1 files at r10, all commit messages.
Dismissed @astoycos from a discussion.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @alessandrod, @astoycos, and @preuss-adam)


aya/src/util.rs line 374 at r6 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

Ping this is still unresolved.

I still think the comment shouldn't repeat the API, and there's no "name field" in sight. I think you just want to say that c_char can be signed or unsigned - though I still don't understand why that leads to us using from_raw_parts...as opposed to what?

Copy link
Contributor Author

@preuss-adam preuss-adam left a 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, 1 unresolved discussion (waiting on @alessandrod, @astoycos, and @tamird)


aya/src/util.rs line 374 at r6 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

I still think the comment shouldn't repeat the API, and there's no "name field" in sight. I think you just want to say that c_char can be signed or unsigned - though I still don't understand why that leads to us using from_raw_parts...as opposed to what?

Yeah, I think the comment doesn't really add anything here anymore. I've just removed it.

Copy link
Contributor Author

@preuss-adam preuss-adam left a comment

Choose a reason for hiding this comment

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

Reviewable status: 5 of 6 files reviewed, 1 unresolved discussion (waiting on @alessandrod, @astoycos, and @tamird)


aya/src/util.rs line 367 at r6 (raw file):

Previously, tamird (Tamir Duberstein) wrote…

Do we need the leading :: on ::core?

nope. removed.

Copy link
Member

@tamird tamird left a comment

Choose a reason for hiding this comment

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

Reviewed 1 of 1 files at r11, all commit messages.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on @alessandrod and @astoycos)

Copy link
Collaborator

@alessandrod alessandrod left a comment

Choose a reason for hiding this comment

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

Looks great, thanks everyone!

@alessandrod alessandrod merged commit 15faca8 into aya-rs:main Nov 18, 2023
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api/needs-review Makes an API change that needs review aya This is about aya (userspace) test A PR that improves test cases or CI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants