Fix :iperf Throughput And Musl Test Discovery - #264
Conversation
There was a problem hiding this comment.
Code Review
This pull request significantly improves network performance and stability, particularly for iperf TCP and UDP tests. Key changes include refactoring the test staging script to stage the entire musl test tree, chunking socket I/O buffers to 64 KiB, implementing a parallel TCP listener pool, dynamically sizing the network adapter RX buffer based on MTU, and introducing lazy growth with a fallback drop-oldest strategy for UDP receive queues. The feedback highlights two critical robustness issues: first, the UDP queue-full check using VecDeque capacity is unreliable and could lead to excessive memory usage or panics on allocation failure; second, the TCP listener replenishment logic should not fail the entire accept call if a new listener cannot be allocated but existing spare listeners are still available.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| if q.len() == q.capacity() { | ||
| return false; | ||
| let old_capacity = q.capacity(); | ||
| if old_capacity < UDP_RXQ_MAX_CAP { | ||
| let new_capacity = old_capacity | ||
| .saturating_mul(2) | ||
| .clamp(UDP_RXQ_INITIAL_CAP, UDP_RXQ_MAX_CAP); | ||
| let _ = q.try_reserve(new_capacity.saturating_sub(old_capacity)); | ||
| } | ||
| if q.len() == q.capacity() { | ||
| let _ = q.pop_front(); | ||
| } | ||
| } |
There was a problem hiding this comment.
在 udp_push 中,使用 q.len() == q.capacity() 来判断队列是否已满并限制最大容量存在严重缺陷。因为 VecDeque 的实际 capacity() 是由底层分配器决定的,通常会大于请求的容量(例如向上取整到 2 的幂)。如果 q.capacity() 超过了 UDP_RXQ_MAX_CAP(512),队列将无法在达到 512 时触发丢包逻辑,从而导致内存占用超出预期。
此外,如果 try_reserve 失败(内存不足),由于没有成功扩容, q.push_back 可能会触发 panic。
建议显式使用 UDP_RXQ_MAX_CAP 限制长度,并在扩容失败时安全地丢弃旧数据包。
if q.len() >= UDP_RXQ_MAX_CAP {
let _ = q.pop_front();
} else if q.len() == q.capacity() {
let old_capacity = q.capacity();
let new_capacity = old_capacity
.saturating_mul(2)
.clamp(UDP_RXQ_INITIAL_CAP, UDP_RXQ_MAX_CAP);
let reserved = if new_capacity > old_capacity {
q.try_reserve(new_capacity - old_capacity).is_ok()
} else {
false
};
if !reserved && q.len() == q.capacity() {
let _ = q.pop_front();
}
}| fn replenish_tcp_listeners( | ||
| socket_file: &SocketFile, | ||
| listen_endpoint: smoltcp::wire::IpListenEndpoint, | ||
| backlog: usize, | ||
| ) -> Result<(), isize> { | ||
| let target = backlog.clamp(1, TCP_LISTENER_POOL_LIMIT); | ||
| while network_stack().tcp_spare_listener_count(socket_file, listen_endpoint) < target { | ||
| let new_listen_handle = match create_tcp_socket() { | ||
| Ok(SocketHandle::Tcp(h)) => h, | ||
| Err(e) => return Err(e.to_errno()), | ||
| Ok(SocketHandle::Udp(_)) => return Err(-(crate::uapi::errno::EINVAL as isize)), | ||
| }; | ||
|
|
||
| if let Err(e) = network_stack().tcp_listen(new_listen_handle, listen_endpoint) { | ||
| network_stack().remove_tcp_socket(new_listen_handle); | ||
| return Err(e.to_errno()); | ||
| } | ||
|
|
||
| socket_file.add_listen_socket(SocketHandle::Tcp(new_listen_handle)); | ||
| } | ||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
在 replenish_tcp_listeners 中,如果创建或监听新的 TCP socket 失败(例如由于内存不足 NoMemory),函数会立即返回错误。这会导致 accept 系统调用直接失败。然而,如果此时 tcp_spare_listener_count 已经大于 0,说明内核中仍有可用的 spare listener,我们应该允许 accept 继续处理已有的连接,而不是因为无法补充新的 listener 而让整个调用失败。建议在已有可用 listener 时仅中断补充循环,而不返回错误。
fn replenish_tcp_listeners(
socket_file: &SocketFile,
listen_endpoint: smoltcp::wire::IpListenEndpoint,
backlog: usize,
) -> Result<(), isize> {
let target = backlog.clamp(1, TCP_LISTENER_POOL_LIMIT);
while network_stack().tcp_spare_listener_count(socket_file, listen_endpoint) < target {
let new_listen_handle = match create_tcp_socket() {
Ok(SocketHandle::Tcp(h)) => h,
Err(e) => {
if network_stack().tcp_spare_listener_count(socket_file, listen_endpoint) > 0 {
break;
}
return Err(e.to_errno());
}
Ok(SocketHandle::Udp(_)) => return Err(-(crate::uapi::errno::EINVAL as isize)),
};
if let Err(e) = network_stack().tcp_listen(new_listen_handle, listen_endpoint) {
network_stack().remove_tcp_socket(new_listen_handle);
if network_stack().tcp_spare_listener_count(socket_file, listen_endpoint) > 0 {
break;
}
return Err(e.to_errno());
}
socket_file.add_listen_socket(SocketHandle::Tcp(new_listen_handle));
}
Ok(())
}
Fix iperf Throughput And Musl Test Discovery
描述
本次拉取请求基于
fix/iperf-throughput,目标是修复 RISC-V musl 下 iperf 分数极低的问题,并把 musl 测试入口从少量白名单调整为全量测试脚本扫描。主要改动如下:
iperf_testcode.sh运行时需要 BusyBox 工具,RISC-V 和 LoongArch musl 的 iperf staging 都补入busybox。WouldBlock。send/recv每轮最多处理 64KiB,避免 iperf 大 buffer 造成一次性内核分配和拷贝压力。-P 5多 stream 下后续连接Connection refused的问题。info降到debug,避免串口输出拖慢 iperf。device.mtu() + Ethernet header分配,避免 64KiB 级 TCP frame 被 2048B 缓冲丢弃。128KiB - 1,保留较高吞吐并稳定通过完整 iperf 组。/tests/musl全量 staging 到/tmp/musl,再扫描/tmp/musl/*_testcode.sh执行所有 musl 测试,避免直接在官方测试盘上运行写入型测试。关键原因
原始 iperf 分数低不是单点问题:
BASIC_TCP和REVERSE_TCP只有 4KiB 传输,receiver 甚至为 0,主要受小 TCP buffer、loopback poll 不及时和 adapter RX buffer 过小影响。PARALLEL_TCP报Connection refused,原因是 smoltcp 没有 Linux 风格的单 socket backlog 队列,需要在本内核层补 listener 池。SocketFile的 per-fd 队列,原队列 64 项无法吸收 iperf 的 burst。/tests/musl跑全量测试会遇到官方测试盘写入/权限/性能问题,因此全量 musl 测试需要先 staging 到 tmpfs。取舍
128KiB - 1是稳定性优先的折中。验证
构建验证:
ARCH=riscv cargo build --release --target riscv64gc-unknown-none-elf通过。iperf-only QEMU 验证:
BASIC_UDP: receiver 25.9MiB / 108Mbit/s,0% loss。PARALLEL_UDP: receiver 36.5MiB / 153Mbit/s,0% loss。REVERSE_UDP: receiver 19.8MiB / 82.8Mbit/s,0% loss。BASIC_TCP: receiver 71.4MiB / 299Mbit/s。PARALLEL_TCP: 5 stream 全部连接成功,SUM receiver 102MiB / 418Mbit/s。REVERSE_TCP: receiver 72.0MiB / 302Mbit/s。脚本语法验证:
sh -n data/risc-v_musl/etc/init.d/rcS通过。风险
/tests/musl会增加 tmpfs 内存占用。如果测试镜像继续变大,需要评估 tmpfs 容量和复制时间。本记录基于以下提交
da0d56c tests: stage busybox for iperf script56c01e8 net: enlarge socket buffers for iperfde092e8 net: chunk socket syscall bufferse63c8a5 net: drain loopback after socket writeseb9d8ce net: support parallel tcp listeners491634c net: reduce iperf socket log noiseaae6897 net: size adapter rx buffer for mtu692b19c docs: record iperf tcp tuning474b6ea net: cap tcp buffers below unstable windowf4905d9 net: grow udp receive queues lazilye75f172 docs: record iperf udp tuning671f1c4 调成测试脚本探测范围关联 Issue
暂无关联 Issue。