perf(wasi): optimize data plane and extend host ABI to v3 (#2455)

Overhaul the WASI guest data plane for throughput and add the host
capabilities it relies on. The externally driven Tokio runtime now
runs its timer pre-turn only when a tracked deadline has expired,
and all WASI-reachable timers (STUN, port mapping, WebClient, UDP
flow cleanup) go through the portable time facade so conditional
timer driving cannot starve them.

Data plane:

- Move read/write deadlines onto TCP and UDP resources with one ABI
  setter per direction, reuse a single expiration timer per
  resource, and drop timeout arguments from the four hot data-plane
  submissions (ABI v3). Checked absolute instants treat
  unrepresentable finite timeouts as unbounded instead of panicking.
- Batch host traffic: vectored TCP frame writes combine queued
  slices into one host operation, and reads request a bounded 64
  KiB while retaining excess bytes in the stream buffer.
- Complete TCP writes inside the guest with cancellation-safe
  writes, reporting the completed prefix before honoring
  cancellation or timeout so hosts never replay bytes.
- Repoll smoltcp egress immediately on zero poll delay, enlarge
  virtual UDP receive queues to 128 KiB payload with 128 metadata
  slots, and bound UDP session receive buffers to 8 KiB plus one
  byte while keeping oversized-datagram detection.

Host integration:

- Add optional algorithm-neutral AEAD seal/open imports with the
  ring backend as fallback, and pin the ring AES-128-GCM wire vector
  so the Go host stays interoperable.
- Forward instance events to hosts through one best-effort,
  synchronous, non-blocking import.
- Add a repository-owned build entry point for the Go host artifact:
  Binaryen 131 at -O4 with cached, SHA-256-verified official
  archives.
This commit is contained in:
KKRainbow
2026-07-28 00:29:08 +08:00
committed by GitHub
parent 7b506e25a7
commit d55e63b88e
40 changed files with 1342 additions and 220 deletions
@@ -1,4 +1,4 @@
# Native data-plane ABI v2
# Native data-plane ABI v3
The native data-plane ABI is a thin adapter over the instance-owned
`DataPlaneSession`. It does not own sockets, operation state, completion
@@ -13,12 +13,18 @@ queues, routing policy, or timeouts.
- `data_plane_completion_drain` returns a non-negative descriptor count or a
negative error value.
- Handle zero is invalid.
- `timeout_ms == UINT64_MAX` means no deadline. Every other timeout starts when
submission is accepted, including time spent waiting for an I/O direction
lock.
- `timeout_ms == UINT64_MAX` means no deadline.
- TCP connect/bind/accept and UDP bind timeouts start when submission is
accepted.
- TCP streams and UDP sockets have persistent read and write deadlines.
`data_plane_resource_deadline_set` replaces the selected directions'
deadlines immediately, including for active operations. An expired deadline
remains expired until it is replaced or cleared with `UINT64_MAX`.
- Deadline direction `1` selects reads, `2` selects writes, and `3` selects
both.
- Request and write bytes are copied before a submit call returns.
- Socket-address fields use native-endian integers. Address bytes are in
network order. ABI v2 accepts IPv4 only.
network order. ABI v3 accepts IPv4 only.
`DataPlaneSocketAddr` is:
@@ -46,6 +52,7 @@ One native session may be open for an EasyTier instance at a time:
```text
data_plane_session_open
-> set resource deadlines
-> submit operations
-> completion_wait
-> completion_drain
@@ -92,6 +99,7 @@ The exported function families are:
- `data_plane_tcp_*_submit`
- `data_plane_udp_*_submit`
- `data_plane_resource_deadline_set`
- `data_plane_completion_wait`
- `data_plane_completion_drain`
- `data_plane_*_result_take`
@@ -13,6 +13,9 @@ use crate::{
types::{DataPlaneCompletion, DataPlaneSocketAddr},
};
pub const DATA_PLANE_DEADLINE_READ: u32 = 1 << 0;
pub const DATA_PLANE_DEADLINE_WRITE: u32 = 1 << 1;
fn failure(error: NativeDataPlaneError) -> c_int {
set_error_msg(&error.message);
-(error.kind as c_int)
@@ -43,7 +46,7 @@ fn socket_addr(address: DataPlaneSocketAddr) -> NativeDataPlaneResult<SocketAddr
6 => {
return Err(NativeDataPlaneError {
kind: DataPlaneErrorKind::AddressFamilyUnsupported,
message: "IPv6 is not supported by data-plane ABI v2".to_string(),
message: "IPv6 is not supported by data-plane ABI v3".to_string(),
});
}
family => {
@@ -210,11 +213,10 @@ pub unsafe extern "C" fn data_plane_tcp_read_submit(
session: u64,
stream: u64,
max_len: u32,
timeout_ms: u64,
out_operation: *mut u64,
) -> c_int {
write_operation(out_operation, || {
super::session::submit_tcp_read(session, stream, max_len, timeout_ms)
super::session::submit_tcp_read(session, stream, max_len)
})
}
@@ -229,7 +231,6 @@ pub unsafe extern "C" fn data_plane_tcp_write_submit(
stream: u64,
data: *const c_uchar,
len: u32,
timeout_ms: u64,
out_operation: *mut u64,
) -> c_int {
let data = match unsafe { copy_input(data, len) } {
@@ -237,7 +238,7 @@ pub unsafe extern "C" fn data_plane_tcp_write_submit(
Err(error) => return failure(error),
};
write_operation(out_operation, || {
super::session::submit_tcp_write(session, stream, data, timeout_ms)
super::session::submit_tcp_write(session, stream, data)
})
}
@@ -266,11 +267,10 @@ pub unsafe extern "C" fn data_plane_udp_receive_submit(
session: u64,
socket: u64,
max_len: u32,
timeout_ms: u64,
out_operation: *mut u64,
) -> c_int {
write_operation(out_operation, || {
super::session::submit_udp_receive(session, socket, max_len, timeout_ms)
super::session::submit_udp_receive(session, socket, max_len)
})
}
@@ -286,7 +286,6 @@ pub unsafe extern "C" fn data_plane_udp_send_submit(
peer_addr: DataPlaneSocketAddr,
data: *const c_uchar,
len: u32,
timeout_ms: u64,
out_operation: *mut u64,
) -> c_int {
let peer_addr = match socket_addr(peer_addr) {
@@ -298,10 +297,27 @@ pub unsafe extern "C" fn data_plane_udp_send_submit(
Err(error) => return failure(error),
};
write_operation(out_operation, || {
super::session::submit_udp_send(session, socket, peer_addr, data, timeout_ms)
super::session::submit_udp_send(session, socket, peer_addr, data)
})
}
#[cfg_attr(feature = "c-abi", unsafe(no_mangle))]
pub extern "C" fn data_plane_resource_deadline_set(
session: u64,
resource: u64,
direction: u32,
timeout_ms: u64,
) -> c_int {
let read = direction & DATA_PLANE_DEADLINE_READ != 0;
let write = direction & DATA_PLANE_DEADLINE_WRITE != 0;
if direction == 0 || direction & !(DATA_PLANE_DEADLINE_READ | DATA_PLANE_DEADLINE_WRITE) != 0 {
return failure(invalid(format!("invalid deadline direction {direction}")));
}
status(super::session::set_resource_deadline(
session, resource, read, write, timeout_ms,
))
}
#[cfg_attr(feature = "c-abi", unsafe(no_mangle))]
pub extern "C" fn data_plane_operation_cancel(session: u64, operation: u64) -> c_int {
status(super::session::cancel_operation(session, operation))
@@ -628,7 +644,7 @@ mod tests {
}
#[test]
fn ipv6_is_rejected_by_v2() {
fn ipv6_is_rejected_by_v3() {
let error = socket_addr(ffi_socket_addr(
"[2001:db8::1]:4321".parse::<SocketAddr>().unwrap(),
))
@@ -646,6 +662,13 @@ mod tests {
assert_eq!(error.kind, DataPlaneErrorKind::AddressFamilyUnsupported);
}
#[test]
fn invalid_deadline_direction_is_rejected_before_session_lookup() {
let invalid = -(DataPlaneErrorKind::Io as c_int);
assert_eq!(data_plane_resource_deadline_set(u64::MAX, 1, 0, 0), invalid);
assert_eq!(data_plane_resource_deadline_set(u64::MAX, 1, 4, 0), invalid);
}
#[test]
fn null_operation_output_does_not_submit() {
let submitted = std::cell::Cell::new(false);
@@ -112,10 +112,10 @@ impl NativeDataPlaneSession {
self.core.discard_all();
}
fn submit(
fn call<T>(
&self,
submit: impl FnOnce(&Arc<CoreDataPlaneSession>) -> Result<DataPlaneOperationId, DataPlaneError>,
) -> NativeDataPlaneResult<u64> {
call: impl FnOnce(&Arc<CoreDataPlaneSession>) -> Result<T, DataPlaneError>,
) -> NativeDataPlaneResult<T> {
let _gate = self
.submit_gate
.lock()
@@ -126,9 +126,14 @@ impl NativeDataPlaneSession {
));
}
let _runtime = self.runtime.enter();
submit(&self.core)
.map(DataPlaneOperationId::get)
.map_err(Into::into)
call(&self.core).map_err(Into::into)
}
fn submit(
&self,
submit: impl FnOnce(&Arc<CoreDataPlaneSession>) -> Result<DataPlaneOperationId, DataPlaneError>,
) -> NativeDataPlaneResult<u64> {
self.call(submit).map(DataPlaneOperationId::get)
}
}
@@ -287,21 +292,18 @@ pub(super) fn submit_tcp_read(
session: u64,
stream: u64,
max_len: u32,
timeout_ms: u64,
) -> NativeDataPlaneResult<u64> {
let stream = resource_id(stream)?;
get_session(session)?
.submit(|core| core.submit_tcp_read(stream, max_len as usize, timeout(timeout_ms)))
get_session(session)?.submit(|core| core.submit_tcp_read(stream, max_len as usize))
}
pub(super) fn submit_tcp_write(
session: u64,
stream: u64,
data: Vec<u8>,
timeout_ms: u64,
) -> NativeDataPlaneResult<u64> {
let stream = resource_id(stream)?;
get_session(session)?.submit(|core| core.submit_tcp_write(stream, data, timeout(timeout_ms)))
get_session(session)?.submit(|core| core.submit_tcp_write(stream, data))
}
pub(super) fn submit_udp_bind(
@@ -316,11 +318,9 @@ pub(super) fn submit_udp_receive(
session: u64,
socket: u64,
max_len: u32,
timeout_ms: u64,
) -> NativeDataPlaneResult<u64> {
let socket = resource_id(socket)?;
get_session(session)?
.submit(|core| core.submit_udp_receive(socket, max_len as usize, timeout(timeout_ms)))
get_session(session)?.submit(|core| core.submit_udp_receive(socket, max_len as usize))
}
pub(super) fn submit_udp_send(
@@ -328,11 +328,21 @@ pub(super) fn submit_udp_send(
socket: u64,
peer_addr: SocketAddr,
data: Vec<u8>,
timeout_ms: u64,
) -> NativeDataPlaneResult<u64> {
let socket = resource_id(socket)?;
get_session(session)?.submit(|core| core.submit_udp_send(socket, peer_addr, data))
}
pub(super) fn set_resource_deadline(
session: u64,
resource: u64,
read: bool,
write: bool,
timeout_ms: u64,
) -> NativeDataPlaneResult<()> {
let resource = resource_id(resource)?;
get_session(session)?
.submit(|core| core.submit_udp_send(socket, peer_addr, data, timeout(timeout_ms)))
.call(|core| core.set_resource_deadline(resource, read, write, timeout(timeout_ms)))
}
pub(super) fn cancel_operation(session: u64, operation: u64) -> NativeDataPlaneResult<()> {
+3 -2
View File
@@ -312,8 +312,9 @@ pub extern "C" fn is_config_server_client_connected() -> c_int {
#[cfg(feature = "ffi-dataplane")]
pub use data_plane::{
data_plane_completion_drain, data_plane_completion_wait, data_plane_operation_cancel,
data_plane_operation_free, data_plane_resource_close, data_plane_result_size,
DATA_PLANE_DEADLINE_READ, DATA_PLANE_DEADLINE_WRITE, data_plane_completion_drain,
data_plane_completion_wait, data_plane_operation_cancel, data_plane_operation_free,
data_plane_resource_close, data_plane_resource_deadline_set, data_plane_result_size,
data_plane_session_close, data_plane_session_open, data_plane_tcp_accept_result_take,
data_plane_tcp_accept_submit, data_plane_tcp_bind_result_take, data_plane_tcp_bind_submit,
data_plane_tcp_connect_result_take, data_plane_tcp_connect_submit,
@@ -739,4 +739,8 @@ fn data_plane_invalid_handle_errors_are_stable() {
assert_eq!(data_plane_operation_cancel(u64::MAX, 1), closed);
assert_eq!(data_plane_operation_free(u64::MAX, 1), closed);
assert_eq!(data_plane_resource_close(u64::MAX, 1), closed);
assert_eq!(
data_plane_resource_deadline_set(u64::MAX, 1, DATA_PLANE_DEADLINE_READ, 0),
closed
);
}