mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-08-08 05:29:47 +00:00
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:
@@ -447,7 +447,7 @@ pub(super) async fn udp_session_layer_recv_task<S, R>(
|
||||
{
|
||||
let control_permits = Arc::new(Semaphore::new(UDP_SESSION_QUEUE_CAPACITY));
|
||||
loop {
|
||||
let datagram = match socket.recv_datagram().await {
|
||||
let datagram = match socket.recv_session_datagram().await {
|
||||
Ok(datagram) => datagram,
|
||||
Err(err) => {
|
||||
tracing::debug!(?err, "udp session recv loop stopped");
|
||||
|
||||
@@ -301,6 +301,7 @@ async fn udp_session_listener_reports_bound_local_addr_before_accept() {
|
||||
struct MockVirtualUdpSocket {
|
||||
local_addr: SocketAddr,
|
||||
incoming: Mutex<VecDeque<(Vec<u8>, SocketAddr)>>,
|
||||
recv_capacities: Mutex<Vec<usize>>,
|
||||
sent: Mutex<Vec<(Vec<u8>, SocketAddr)>>,
|
||||
send_attempts: Mutex<Vec<(Vec<u8>, SocketAddr, UdpSocketSendMeta)>>,
|
||||
reject_preferred_source: AtomicBool,
|
||||
@@ -311,6 +312,7 @@ impl MockVirtualUdpSocket {
|
||||
Self {
|
||||
local_addr,
|
||||
incoming: Mutex::new(incoming.into()),
|
||||
recv_capacities: Mutex::new(Vec::new()),
|
||||
sent: Mutex::new(Vec::new()),
|
||||
send_attempts: Mutex::new(Vec::new()),
|
||||
reject_preferred_source: AtomicBool::new(false),
|
||||
@@ -357,6 +359,7 @@ impl VirtualUdpSocket for MockVirtualUdpSocket {
|
||||
}
|
||||
|
||||
async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
|
||||
self.recv_capacities.lock().unwrap().push(buf.len());
|
||||
let (data, remote_addr) =
|
||||
self.incoming.lock().unwrap().pop_front().ok_or_else(|| {
|
||||
io::Error::new(io::ErrorKind::UnexpectedEof, "no incoming datagram")
|
||||
@@ -367,6 +370,32 @@ impl VirtualUdpSocket for MockVirtualUdpSocket {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn portable_udp_receive_keeps_general_and_session_capacities_separate() {
|
||||
let local_addr = SocketAddr::from(([127, 0, 0, 1], 12000));
|
||||
let peer_addr = SocketAddr::from(([127, 0, 0, 1], 12001));
|
||||
let socket = MockVirtualUdpSocket::new(
|
||||
local_addr,
|
||||
vec![
|
||||
(b"session".to_vec(), peer_addr),
|
||||
(b"general".to_vec(), peer_addr),
|
||||
],
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
socket.recv_session_datagram().await.unwrap().payload,
|
||||
b"session".as_slice()
|
||||
);
|
||||
assert_eq!(
|
||||
socket.recv_datagram().await.unwrap().payload,
|
||||
b"general".as_slice()
|
||||
);
|
||||
assert_eq!(
|
||||
*socket.recv_capacities.lock().unwrap(),
|
||||
[MAX_UDP_SESSION_DATAGRAM_SIZE + 1, MAX_UDP_DATAGRAM_SIZE]
|
||||
);
|
||||
}
|
||||
|
||||
fn easytier_stun_request(change_ip: bool, change_port: bool) -> Vec<u8> {
|
||||
let mut request = Message::<Attribute>::new(MessageClass::Request, BINDING, u32_to_tid(7));
|
||||
if change_ip || change_port {
|
||||
|
||||
@@ -76,16 +76,36 @@ pub trait VirtualUdpSocket: Send + Sync + 'static {
|
||||
/// override it when their socket API can write directly into owned storage,
|
||||
/// avoiding a second allocation and copy at the Host boundary.
|
||||
async fn recv_datagram(&self) -> std::io::Result<UdpSocketDatagram> {
|
||||
let mut payload = BytesMut::new();
|
||||
payload.resize(MAX_UDP_DATAGRAM_SIZE, 0);
|
||||
let (len, remote_addr, meta) = self.recv_from_with_meta(&mut payload).await?;
|
||||
payload.truncate(len);
|
||||
Ok(UdpSocketDatagram {
|
||||
payload,
|
||||
remote_addr,
|
||||
meta,
|
||||
})
|
||||
recv_portable_datagram(self, MAX_UDP_DATAGRAM_SIZE).await
|
||||
}
|
||||
|
||||
/// Receives one datagram for the UDP session/multiplexer data plane.
|
||||
///
|
||||
/// Portable hosts receive one byte past the session limit so a truncated
|
||||
/// oversized datagram remains distinguishable from a valid maximum-sized
|
||||
/// datagram. Native hosts may override this when they can detect truncation
|
||||
/// without the extra byte.
|
||||
async fn recv_session_datagram(&self) -> std::io::Result<UdpSocketDatagram> {
|
||||
recv_portable_datagram(self, MAX_UDP_SESSION_DATAGRAM_SIZE + 1).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn recv_portable_datagram<S>(
|
||||
socket: &S,
|
||||
capacity: usize,
|
||||
) -> std::io::Result<UdpSocketDatagram>
|
||||
where
|
||||
S: VirtualUdpSocket + ?Sized,
|
||||
{
|
||||
let mut payload = BytesMut::new();
|
||||
payload.resize(capacity, 0);
|
||||
let (len, remote_addr, meta) = socket.recv_from_with_meta(&mut payload).await?;
|
||||
payload.truncate(len);
|
||||
Ok(UdpSocketDatagram {
|
||||
payload,
|
||||
remote_addr,
|
||||
meta,
|
||||
})
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
Reference in New Issue
Block a user