Add tcp latency test over virtio-net
This commit is contained in:
parent
e78303b01a
commit
090149eed7
|
@ -58,6 +58,7 @@ jobs:
|
|||
- lmbench/tcp_loopback_bw_4k
|
||||
- lmbench/tcp_loopback_bw_64k
|
||||
- lmbench/tcp_loopback_lat
|
||||
- lmbench/tcp_virtio_lat
|
||||
- lmbench/tcp_loopback_connect_lat
|
||||
- lmbench/tcp_loopback_select_lat
|
||||
- lmbench/tcp_loopback_http_bw
|
||||
|
|
|
@ -154,7 +154,7 @@ impl NetworkDevice {
|
|||
.rx_buffers
|
||||
.remove(token as usize)
|
||||
.ok_or(VirtioNetError::WrongToken)?;
|
||||
rx_buffer.set_packet_len(len as usize);
|
||||
rx_buffer.set_packet_len(len as usize - size_of::<VirtioNetHdr>());
|
||||
// FIXME: Ideally, we can reuse the returned buffer without creating new buffer.
|
||||
// But this requires locking device to be compatible with smoltcp interface.
|
||||
let rx_pool = RX_BUFFER_POOL.get().unwrap();
|
||||
|
@ -180,6 +180,8 @@ impl NetworkDevice {
|
|||
self.send_queue.notify();
|
||||
}
|
||||
|
||||
debug!("send packet, token = {}, len = {}", token, packet.len());
|
||||
|
||||
debug_assert!(self.tx_buffers[token as usize].is_none());
|
||||
self.tx_buffers[token as usize] = Some(tx_buffer);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ mod unbound;
|
|||
pub use bound::{BoundTcpSocket, BoundUdpSocket};
|
||||
pub(crate) use bound::{BoundTcpSocketInner, BoundUdpSocketInner, TcpProcessResult};
|
||||
pub use event::SocketEventObserver;
|
||||
pub use smoltcp::socket::tcp::State as TcpState;
|
||||
pub use unbound::{
|
||||
UnboundTcpSocket, UnboundUdpSocket, TCP_RECV_BUF_LEN, TCP_SEND_BUF_LEN, UDP_RECV_PAYLOAD_LEN,
|
||||
UDP_SEND_PAYLOAD_LEN,
|
||||
|
|
|
@ -4,7 +4,7 @@ use alloc::sync::Weak;
|
|||
|
||||
use aster_bigtcp::{
|
||||
errors::tcp::{RecvError, SendError},
|
||||
socket::SocketEventObserver,
|
||||
socket::{SocketEventObserver, TcpState},
|
||||
wire::IpEndpoint,
|
||||
};
|
||||
|
||||
|
@ -68,6 +68,9 @@ impl ConnectedStream {
|
|||
Ok(Err(e)) => Err(e),
|
||||
Err(RecvError::Finished) => Ok(0),
|
||||
Err(RecvError::InvalidState) => {
|
||||
if self.before_established() {
|
||||
return_errno_with_message!(Errno::EAGAIN, "the connection is not established");
|
||||
}
|
||||
return_errno_with_message!(Errno::ECONNRESET, "the connection is reset")
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +89,9 @@ impl ConnectedStream {
|
|||
Ok(Ok(sent_bytes)) => Ok(sent_bytes),
|
||||
Ok(Err(e)) => Err(e),
|
||||
Err(SendError::InvalidState) => {
|
||||
if self.before_established() {
|
||||
return_errno_with_message!(Errno::EAGAIN, "the connection is not established");
|
||||
}
|
||||
// FIXME: `EPIPE` is another possibility, which means that the socket is shut down
|
||||
// for writing. In that case, we should also trigger a `SIGPIPE` if `MSG_NOSIGNAL`
|
||||
// is not specified.
|
||||
|
@ -135,4 +141,20 @@ impl ConnectedStream {
|
|||
pub(super) fn set_observer(&self, observer: Weak<dyn SocketEventObserver>) {
|
||||
self.bound_socket.set_observer(observer)
|
||||
}
|
||||
|
||||
/// Returns whether the connection is before established.
|
||||
///
|
||||
/// Note that a newly accepted socket may not yet be in the [`TcpState::Established`] state.
|
||||
/// The accept syscall only verifies that a connection request is incoming by ensuring
|
||||
/// that the backlog socket is not in the [`TcpState::Listen`] state.
|
||||
/// However, the socket might still be waiting for further ACKs to complete the establishment process.
|
||||
/// Therefore, it could be in either the [`TcpState::SynSent`] or [`TcpState::SynReceived`] states.
|
||||
/// We must wait until the socket reaches the established state before it can send and receive data.
|
||||
///
|
||||
/// FIXME: Should we check established state in accept or here?
|
||||
fn before_established(&self) -> bool {
|
||||
self.bound_socket.raw_with(|socket| {
|
||||
socket.state() == TcpState::SynSent || socket.state() == TcpState::SynReceived
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -265,13 +265,18 @@ impl StreamSocket {
|
|||
return_errno_with_message!(Errno::EINVAL, "the socket is not listening");
|
||||
};
|
||||
|
||||
listen_stream.try_accept().map(|connected_stream| {
|
||||
let accepted = listen_stream.try_accept().map(|connected_stream| {
|
||||
listen_stream.update_io_events(&self.pollee);
|
||||
|
||||
let remote_endpoint = connected_stream.remote_endpoint();
|
||||
let accepted_socket = Self::new_connected(connected_stream);
|
||||
(accepted_socket as _, remote_endpoint.into())
|
||||
})
|
||||
});
|
||||
|
||||
drop(state);
|
||||
poll_ifaces();
|
||||
|
||||
accepted
|
||||
}
|
||||
|
||||
fn try_recv(
|
||||
|
|
|
@ -70,7 +70,7 @@ run_benchmark() {
|
|||
-drive if=none,format=raw,id=x0,file=${BENCHMARK_DIR}/../build/ext2.img \
|
||||
-device virtio-blk-pci,bus=pcie.0,addr=0x6,drive=x0,serial=vext2,disable-legacy=on,disable-modern=off,queue-size=64,num-queues=1,config-wce=off,request-merging=off,write-cache=off,backend_defaults=off,discard=off,event_idx=off,indirect_desc=off,ioeventfd=off,queue_reset=off \
|
||||
-append 'console=ttyS0 rdinit=/benchmark/common/bench_runner.sh ${benchmark} linux mitigations=off hugepages=0 transparent_hugepage=never quiet' \
|
||||
-netdev user,id=net01,hostfwd=tcp::5201-:5201,hostfwd=tcp::8080-:8080 \
|
||||
-netdev user,id=net01,hostfwd=tcp::5201-:5201,hostfwd=tcp::8080-:8080,hostfwd=tcp::31234-:31234 \
|
||||
-device virtio-net-pci,netdev=net01,disable-legacy=on,disable-modern=off \
|
||||
-nographic \
|
||||
2>&1"
|
||||
|
|
|
@ -26,6 +26,9 @@ elif [[ "$BENCHMARK_PATH" =~ "nginx" ]]; then
|
|||
elif [[ "$BENCHMARK_PATH" =~ "redis" ]]; then
|
||||
# Persist Redis port
|
||||
export REDIS_PORT=6379
|
||||
elif [[ "$BENCHMARK_PATH" =~ "tcp_virtio_lat" ]]; then
|
||||
# Persist lmbench/tcp_lat port
|
||||
export LMBENCH_TCP_LAT_PORT=31234
|
||||
fi
|
||||
|
||||
# Function to run the benchmark
|
||||
|
@ -57,7 +60,7 @@ run_benchmark() {
|
|||
sleep 1
|
||||
|
||||
# Run the host command and save the output to the specified file.
|
||||
bash "${BENCHMARK_PATH}/host.sh" | tee "${output_file}"
|
||||
bash "${BENCHMARK_PATH}/host.sh" 2>&1 | tee "${output_file}"
|
||||
|
||||
# Clean up the log file
|
||||
rm -f "${guest_log_file}"
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"tcp_loopback_bw_4k",
|
||||
"tcp_loopback_bw_64k",
|
||||
"tcp_loopback_lat",
|
||||
"tcp_virtio_lat",
|
||||
"tcp_loopback_connect_lat",
|
||||
"tcp_loopback_select_lat",
|
||||
"tcp_loopback_http_bw",
|
||||
|
|
|
@ -6,6 +6,6 @@ set -e
|
|||
|
||||
echo "*** Running lmbench TCP latency test ***"
|
||||
|
||||
/benchmark/bin/lmbench/lat_tcp -s
|
||||
/benchmark/bin/lmbench/lat_tcp -s 127.0.0.1
|
||||
/benchmark/bin/lmbench/lat_tcp -P 1 127.0.0.1
|
||||
/benchmark/bin/lmbench/lat_tcp -S 127.0.0.1
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"alert_threshold": "125%",
|
||||
"alert_tool": "customSmallerIsBetter",
|
||||
"search_pattern": "TCP latency using 127.0.0.1:",
|
||||
"result_index": "5",
|
||||
"description": "lat_tcp_virtio",
|
||||
"title": "[TCP sockets] The latency over virtio-net",
|
||||
"benchmark_type": "host_guest"
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/sh
|
||||
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
set -e
|
||||
|
||||
# Function to stop the guest VM
|
||||
stop_guest() {
|
||||
echo "Stopping guest VM..."
|
||||
# `-r` means if there's no qemu, the kill won't be executed
|
||||
pgrep qemu | xargs -r kill
|
||||
}
|
||||
|
||||
# Trap EXIT signal to ensure guest VM is stopped on script exit
|
||||
trap stop_guest EXIT
|
||||
|
||||
# Run lmbench tcp client
|
||||
echo "Running lmbench tcp client"
|
||||
/usr/local/benchmark/lmbench/lat_tcp -P 1 127.0.0.1
|
||||
|
||||
# The trap will automatically stop the guest VM when the script exits
|
|
@ -0,0 +1,14 @@
|
|||
[
|
||||
{
|
||||
"name": "Average TCP latency over virtio-net on Linux",
|
||||
"unit": "µs",
|
||||
"value": 0,
|
||||
"extra": "linux_result"
|
||||
},
|
||||
{
|
||||
"name": "Average TCP latency over virtio-net on Asterinas",
|
||||
"unit": "µs",
|
||||
"value": 0,
|
||||
"extra": "aster_result"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,13 @@
|
|||
#!/bin/sh
|
||||
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
set -e
|
||||
|
||||
echo "Running lmbench TCP latency over virtio-net..."
|
||||
|
||||
# Start the server
|
||||
/benchmark/bin/lmbench/lat_tcp -s 10.0.2.15
|
||||
|
||||
# Sleep for a long time to ensure VM won't exit
|
||||
sleep 200
|
|
@ -11,11 +11,12 @@ SSH_RAND_PORT=${SSH_PORT:-$(shuf -i 1024-65535 -n 1)}
|
|||
NGINX_RAND_PORT=${NGINX_PORT:-$(shuf -i 1024-65535 -n 1)}
|
||||
REDIS_RAND_PORT=${REDIS_PORT:-$(shuf -i 1024-65535 -n 1)}
|
||||
IPERF_RAND_PORT=${IPERF_PORT:-$(shuf -i 1024-65535 -n 1)}
|
||||
LMBENCH_TCP_LAT_RAND_PORT=${LMBENCH_TCP_LAT_PORT:-$(shuf -i 1024-65535 -n 1)}
|
||||
|
||||
# Optional QEMU arguments. Opt in them manually if needed.
|
||||
# QEMU_OPT_ARG_DUMP_PACKETS="-object filter-dump,id=filter0,netdev=net01,file=virtio-net.pcap"
|
||||
|
||||
echo "[$1] Forwarded QEMU guest port: $SSH_RAND_PORT->22; $NGINX_RAND_PORT->8080 $REDIS_RAND_PORT->6379 $IPERF_RAND_PORT->5201" 1>&2
|
||||
echo "[$1] Forwarded QEMU guest port: $SSH_RAND_PORT->22; $NGINX_RAND_PORT->8080 $REDIS_RAND_PORT->6379 $IPERF_RAND_PORT->5201 $LMBENCH_TCP_LAT_RAND_PORT->31234" 1>&2
|
||||
|
||||
COMMON_QEMU_ARGS="\
|
||||
-cpu Icelake-Server,+x2apic \
|
||||
|
@ -27,7 +28,7 @@ COMMON_QEMU_ARGS="\
|
|||
-serial chardev:mux \
|
||||
-monitor chardev:mux \
|
||||
-chardev stdio,id=mux,mux=on,signal=off,logfile=qemu.log \
|
||||
-netdev user,id=net01,hostfwd=tcp::$SSH_RAND_PORT-:22,hostfwd=tcp::$NGINX_RAND_PORT-:8080,hostfwd=tcp::$REDIS_RAND_PORT-:6379,hostfwd=tcp::$IPERF_RAND_PORT-:5201 \
|
||||
-netdev user,id=net01,hostfwd=tcp::$SSH_RAND_PORT-:22,hostfwd=tcp::$NGINX_RAND_PORT-:8080,hostfwd=tcp::$REDIS_RAND_PORT-:6379,hostfwd=tcp::$IPERF_RAND_PORT-:5201,hostfwd=tcp::$LMBENCH_TCP_LAT_RAND_PORT-:31234 \
|
||||
$QEMU_OPT_ARG_DUMP_PACKETS \
|
||||
-device isa-debug-exit,iobase=0xf4,iosize=0x04 \
|
||||
-drive if=none,format=raw,id=x0,file=./test/build/ext2.img \
|
||||
|
|
Loading…
Reference in New Issue