From 5f6552c714a83d1c64f0fa91430061412807cb3c Mon Sep 17 00:00:00 2001 From: Sherlock Holo Date: Thu, 29 May 2025 17:47:21 +0800 Subject: [PATCH 1/2] feat: add compio support for aya-log --- Cargo.toml | 2 ++ aya-log/Cargo.toml | 12 +++++++-- aya-log/src/lib.rs | 24 +++++++++++++++-- aya/Cargo.toml | 2 ++ aya/src/lib.rs | 6 ++++- aya/src/maps/mod.rs | 19 ++++++++++--- aya/src/maps/perf/async_perf_event_array.rs | 28 +++++++++++++++++++ aya/src/maps/perf/mod.rs | 30 ++++++++++++++++++--- 8 files changed, 110 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3edb5d25..48facbf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,7 @@ rust-version = "1.85.0" anyhow = { version = "1", default-features = false } assert_matches = { version = "1.5.0", default-features = false } async-io = { version = "2.0", default-features = false } +async-global-executor = { version = "3.1.0", default-features = false } base64 = { version = "0.22.1", default-features = false } bindgen = { version = "0.71", default-features = false } bitflags = { version = "2.2.1", default-features = false } @@ -71,6 +72,7 @@ bytes = { version = "1", default-features = false } cargo_metadata = { version = "0.19.0", default-features = false } clap = { version = "4", default-features = false } const-assert = { version = "1.0.1", default-features = false } +compio = { version = "0.14.0", default-features = false } dialoguer = { version = "0.11", default-features = false } diff = { version = "0.1.13", default-features = false } env_logger = { version = "0.11", default-features = false } diff --git a/aya-log/Cargo.toml b/aya-log/Cargo.toml index be672108..8dbdefa5 100644 --- a/aya-log/Cargo.toml +++ b/aya-log/Cargo.toml @@ -16,13 +16,21 @@ rust-version.workspace = true [lints] workspace = true +[features] +default = ["async_tokio"] +async_tokio = ["aya/async_tokio", "dep:tokio"] +async_std = ["aya/async_std", "dep:async-global-executor"] +async_compio = ["aya/async_compio", "dep:compio"] + [dependencies] -aya = { path = "../aya", version = "^0.13.1", features = ["async_tokio"] } +aya = { path = "../aya", version = "^0.13.1" } aya-log-common = { path = "../aya-log-common", version = "^0.1.15", default-features = false } bytes = { workspace = true } log = { workspace = true } thiserror = { workspace = true } -tokio = { workspace = true, features = ["rt"] } +tokio = { workspace = true, features = ["rt"], optional = true } +async-global-executor = { workspace = true, optional = true } +compio = { workspace = true, optional = true } [dev-dependencies] env_logger = { workspace = true } diff --git a/aya-log/src/lib.rs b/aya-log/src/lib.rs index ab1fb850..827ea1d2 100644 --- a/aya-log/src/lib.rs +++ b/aya-log/src/lib.rs @@ -165,7 +165,8 @@ impl EbpfLogger { let mut buf = logs.open(cpu_id, None)?; let log = logger.clone(); - tokio::spawn(async move { + + let fut = async move { let mut buffers = vec![BytesMut::with_capacity(LOG_BUF_CAPACITY); 10]; loop { @@ -175,7 +176,26 @@ impl EbpfLogger { log_buf(buf.as_ref(), &*log).unwrap(); } } - }); + }; + + #[cfg(feature = "async_tokio")] + { + tokio::spawn(fut); + } + + #[cfg(all(not(feature = "async_tokio"), feature = "async_std"))] + { + async_global_executor::spawn(fut).detach(); + } + + #[cfg(all( + not(feature = "async_tokio"), + not(feature = "async_std"), + feature = "async_compio" + ))] + { + compio::runtime::spawn(fut).detach(); + } } Ok(()) } diff --git a/aya/Cargo.toml b/aya/Cargo.toml index 3d0618da..e41d85ea 100644 --- a/aya/Cargo.toml +++ b/aya/Cargo.toml @@ -22,6 +22,7 @@ async-io = { workspace = true, optional = true } aya-obj = { path = "../aya-obj", version = "^0.2.1", features = ["std"] } bitflags = { workspace = true } bytes = { workspace = true } +compio = { workspace = true, optional = true, features = ["default"] } hashbrown = { workspace = true } libc = { workspace = true } log = { workspace = true } @@ -36,6 +37,7 @@ tempfile = { workspace = true } [features] async_std = ["dep:async-io"] async_tokio = ["tokio/net"] +async_compio = ["dep:compio"] default = [] [package.metadata.docs.rs] diff --git a/aya/src/lib.rs b/aya/src/lib.rs index 8f37fd78..f881f7b8 100644 --- a/aya/src/lib.rs +++ b/aya/src/lib.rs @@ -68,7 +68,11 @@ //unused_results, )] #![cfg_attr( - all(feature = "async_tokio", feature = "async_std"), + all( + feature = "async_tokio", + feature = "async_std", + feature = "async_compio" + ), expect(unused_crate_dependencies) )] diff --git a/aya/src/maps/mod.rs b/aya/src/maps/mod.rs index 21bb2b4c..0bc1c6f8 100644 --- a/aya/src/maps/mod.rs +++ b/aya/src/maps/mod.rs @@ -92,8 +92,19 @@ pub use bloom_filter::BloomFilter; pub use hash_map::{HashMap, PerCpuHashMap}; pub use info::{MapInfo, MapType, loaded_maps}; pub use lpm_trie::LpmTrie; -#[cfg(any(feature = "async_tokio", feature = "async_std"))] -#[cfg_attr(docsrs, doc(cfg(any(feature = "async_tokio", feature = "async_std"))))] +#[cfg(any( + feature = "async_tokio", + feature = "async_std", + feature = "async_compio" +))] +#[cfg_attr( + docsrs, + doc(cfg(any( + feature = "async_tokio", + feature = "async_std", + feature = "async_compio" + ))) +)] pub use perf::AsyncPerfEventArray; pub use perf::PerfEventArray; pub use queue::Queue; @@ -487,8 +498,8 @@ impl_try_from_map!(() { SockMap, StackTraceMap, XskMap, - #[cfg(any(feature = "async_tokio", feature = "async_std"))] - #[cfg_attr(docsrs, doc(cfg(any(feature = "async_tokio", feature = "async_std"))))] + #[cfg(any(feature = "async_tokio", feature = "async_std", feature = "async_compio"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "async_tokio", feature = "async_std", feature = "async_compio"))))] AsyncPerfEventArray from PerfEventArray, }); diff --git a/aya/src/maps/perf/async_perf_event_array.rs b/aya/src/maps/perf/async_perf_event_array.rs index ecc7babb..deb2e3c1 100644 --- a/aya/src/maps/perf/async_perf_event_array.rs +++ b/aya/src/maps/perf/async_perf_event_array.rs @@ -1,3 +1,9 @@ +#[cfg(all( + not(feature = "async_tokio"), + not(feature = "async_std"), + feature = "async_compio" +))] +use std::os::fd::AsRawFd as _; use std::{ borrow::{Borrow, BorrowMut}, path::Path, @@ -10,6 +16,18 @@ use std::{ #[cfg(all(not(feature = "async_tokio"), feature = "async_std"))] use async_io::Async; use bytes::BytesMut; +#[cfg(all( + not(feature = "async_tokio"), + not(feature = "async_std"), + feature = "async_compio" +))] +use compio::{ + driver::{ + SharedFd, + op::{Interest, PollOnce}, + }, + runtime, +}; #[cfg(feature = "async_tokio")] use tokio::io::unix::AsyncFd; @@ -172,6 +190,16 @@ impl> AsyncPerfEventArrayBuffer { unsafe { buf.get_mut() } }; + #[cfg(all( + not(feature = "async_tokio"), + not(feature = "async_std"), + feature = "async_compio" + ))] + { + let poll_once = PollOnce::new(SharedFd::new(buf.as_raw_fd()), Interest::Readable); + runtime::submit(poll_once).await.0?; + } + let events = buf.read_events(buffers)?; const EMPTY: Events = Events { read: 0, lost: 0 }; if events != EMPTY { diff --git a/aya/src/maps/perf/mod.rs b/aya/src/maps/perf/mod.rs index 765b052d..974c4210 100644 --- a/aya/src/maps/perf/mod.rs +++ b/aya/src/maps/perf/mod.rs @@ -2,14 +2,36 @@ //! `perf` API. //! //! See [`PerfEventArray`] and [`AsyncPerfEventArray`]. -#[cfg(any(feature = "async_tokio", feature = "async_std"))] -#[cfg_attr(docsrs, doc(cfg(any(feature = "async_tokio", feature = "async_std"))))] +#[cfg(any( + feature = "async_tokio", + feature = "async_std", + feature = "async_compio" +))] +#[cfg_attr( + docsrs, + doc(cfg(any( + feature = "async_tokio", + feature = "async_std", + feature = "async_compio" + ))) +)] mod async_perf_event_array; mod perf_buffer; mod perf_event_array; -#[cfg(any(feature = "async_tokio", feature = "async_std"))] -#[cfg_attr(docsrs, doc(cfg(any(feature = "async_tokio", feature = "async_std"))))] +#[cfg(any( + feature = "async_tokio", + feature = "async_std", + feature = "async_compio" +))] +#[cfg_attr( + docsrs, + doc(cfg(any( + feature = "async_tokio", + feature = "async_std", + feature = "async_compio" + ))) +)] pub use async_perf_event_array::*; pub use perf_buffer::*; pub use perf_event_array::*; From 29b5eef4b7274f93c85e8885d94257817f40412d Mon Sep 17 00:00:00 2001 From: Sherlock Holo Date: Fri, 30 May 2025 22:46:05 +0800 Subject: [PATCH 2/2] make async feature mutual exclusivity In previous version, user can enable async_tokio and async_std features, however when both of them are enabled, tokio will be ignored. After add async_compio feature, when two or three async features are enabled, user may confuse which one will be used. IMO the better way is make these async features are mutual exclusivity, user should only enable one, if more than async feautres are enabled, will compile fail with compile_error! macro. Also if none of the async feature is enabled in aya-log, will compile fail too --- aya-log/Cargo.toml | 1 - aya-log/src/lib.rs | 25 ++++++++++---- aya/src/lib.rs | 3 +- aya/src/maps/perf/async_perf_event_array.rs | 36 ++++++++++----------- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/aya-log/Cargo.toml b/aya-log/Cargo.toml index 8dbdefa5..42e8c1b8 100644 --- a/aya-log/Cargo.toml +++ b/aya-log/Cargo.toml @@ -17,7 +17,6 @@ rust-version.workspace = true workspace = true [features] -default = ["async_tokio"] async_tokio = ["aya/async_tokio", "dep:tokio"] async_std = ["aya/async_std", "dep:async-global-executor"] async_compio = ["aya/async_compio", "dep:compio"] diff --git a/aya-log/src/lib.rs b/aya-log/src/lib.rs index 827ea1d2..bc0ec593 100644 --- a/aya-log/src/lib.rs +++ b/aya-log/src/lib.rs @@ -59,6 +59,23 @@ use std::{ const MAP_NAME: &str = "AYA_LOGS"; +// check async feature mutual exclusivity +#[cfg(all(feature = "async_tokio", feature = "async_std"))] +compile_error!("Cannot enable both async_tokio and async_std features at the same time"); + +#[cfg(all(feature = "async_tokio", feature = "async_compio"))] +compile_error!("Cannot enable both async_tokio and async_compio features at the same time"); + +#[cfg(all(feature = "async_std", feature = "async_compio"))] +compile_error!("Cannot enable both async_std and async_compio features at the same time"); + +#[cfg(not(any( + feature = "async_tokio", + feature = "async_std", + feature = "async_compio" +)))] +compile_error!("Must enable at least one async feature: async_tokio, async_std, or async_compio"); + use aya::{ Ebpf, Pod, maps::{ @@ -183,16 +200,12 @@ impl EbpfLogger { tokio::spawn(fut); } - #[cfg(all(not(feature = "async_tokio"), feature = "async_std"))] + #[cfg(feature = "async_std")] { async_global_executor::spawn(fut).detach(); } - #[cfg(all( - not(feature = "async_tokio"), - not(feature = "async_std"), - feature = "async_compio" - ))] + #[cfg(feature = "async_compio")] { compio::runtime::spawn(fut).detach(); } diff --git a/aya/src/lib.rs b/aya/src/lib.rs index f881f7b8..1c8b4ad3 100644 --- a/aya/src/lib.rs +++ b/aya/src/lib.rs @@ -24,13 +24,14 @@ //! * Support for function call relocation and global data maps, which //! allows eBPF programs to make **function calls** and use **global variables //! and initializers**. -//! * **Async support** with both [tokio] and [async-std]. +//! * **Async support** with [tokio], [async-std] and [compio]. //! * Easy to deploy and fast to build: aya doesn't require a kernel build or //! compiled headers, and not even a C toolchain; a release build completes in a matter //! of seconds. //! //! [tokio]: https://docs.rs/tokio //! [async-std]: https://docs.rs/async-std +//! [compio]: https://docs.rs/compio #![doc( html_logo_url = "https://aya-rs.dev/assets/images/crabby.svg", diff --git a/aya/src/maps/perf/async_perf_event_array.rs b/aya/src/maps/perf/async_perf_event_array.rs index deb2e3c1..d225cf03 100644 --- a/aya/src/maps/perf/async_perf_event_array.rs +++ b/aya/src/maps/perf/async_perf_event_array.rs @@ -1,8 +1,14 @@ -#[cfg(all( - not(feature = "async_tokio"), - not(feature = "async_std"), - feature = "async_compio" -))] +// check async feature mutual exclusivity +#[cfg(all(feature = "async_tokio", feature = "async_std"))] +compile_error!("Cannot enable both async_tokio and async_std features at the same time"); + +#[cfg(all(feature = "async_tokio", feature = "async_compio"))] +compile_error!("Cannot enable both async_tokio and async_compio features at the same time"); + +#[cfg(all(feature = "async_std", feature = "async_compio"))] +compile_error!("Cannot enable both async_std and async_compio features at the same time"); + +#[cfg(feature = "async_compio")] use std::os::fd::AsRawFd as _; use std::{ borrow::{Borrow, BorrowMut}, @@ -13,14 +19,10 @@ use std::{ // // We should eventually split async functionality out into separate crates "aya-async-tokio" and // "async-async-std". Presently we arbitrarily choose tokio over async-std when both are requested. -#[cfg(all(not(feature = "async_tokio"), feature = "async_std"))] +#[cfg(feature = "async_std")] use async_io::Async; use bytes::BytesMut; -#[cfg(all( - not(feature = "async_tokio"), - not(feature = "async_std"), - feature = "async_compio" -))] +#[cfg(feature = "async_compio")] use compio::{ driver::{ SharedFd, @@ -121,7 +123,7 @@ impl> AsyncPerfEventArray { let buf = perf_map.open(index, page_count)?; #[cfg(feature = "async_tokio")] let buf = AsyncFd::new(buf)?; - #[cfg(all(not(feature = "async_tokio"), feature = "async_std"))] + #[cfg(feature = "async_std")] let buf = Async::new(buf)?; Ok(AsyncPerfEventArrayBuffer { buf }) } @@ -157,7 +159,7 @@ pub struct AsyncPerfEventArrayBuffer> { #[cfg(feature = "async_tokio")] buf: AsyncFd>, - #[cfg(all(not(feature = "async_tokio"), feature = "async_std"))] + #[cfg(feature = "async_std")] buf: Async>, } @@ -182,7 +184,7 @@ impl> AsyncPerfEventArrayBuffer { #[cfg(feature = "async_tokio")] let buf = guard.get_inner_mut(); - #[cfg(all(not(feature = "async_tokio"), feature = "async_std"))] + #[cfg(feature = "async_std")] let buf = { if !buf.get_ref().readable() { buf.readable().await?; @@ -190,11 +192,7 @@ impl> AsyncPerfEventArrayBuffer { unsafe { buf.get_mut() } }; - #[cfg(all( - not(feature = "async_tokio"), - not(feature = "async_std"), - feature = "async_compio" - ))] + #[cfg(feature = "async_compio")] { let poll_once = PollOnce::new(SharedFd::new(buf.as_raw_fd()), Interest::Readable); runtime::submit(poll_once).await.0?;