bbr and buffers fixes
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use bytes::{Buf, Bytes, BytesMut};
|
||||
use bytes::{Buf, Bytes};
|
||||
use std::collections::VecDeque;
|
||||
use netrunner_core::{
|
||||
net::{NetworkConfig, UDP_IDLE_TIMEOUT},
|
||||
rawcast::{LocalProtocol, RawCastFrame},
|
||||
@@ -50,7 +51,8 @@ pub enum ConnectionState {
|
||||
pub struct TcpConnection {
|
||||
core: ConnectionCore<Bytes>,
|
||||
state: ConnectionState,
|
||||
pending_data: BytesMut,
|
||||
// 🔥 ФИКС: Используем очередь чанков, чтобы не копировать память
|
||||
pending_data: VecDeque<Bytes>,
|
||||
handshake_rx: Option<oneshot::Receiver<()>>,
|
||||
chunk_buf: Vec<u8>,
|
||||
server_eof: bool,
|
||||
@@ -72,7 +74,7 @@ impl TcpConnection {
|
||||
let conn = Self {
|
||||
core,
|
||||
state: ConnectionState::Handshaking,
|
||||
pending_data: BytesMut::new(),
|
||||
pending_data: VecDeque::new(),
|
||||
handshake_rx: Some(handshake_rx),
|
||||
chunk_buf: vec![0u8; NetworkConfig::global().tcp_chunk_size],
|
||||
server_eof: false,
|
||||
@@ -163,13 +165,14 @@ impl TcpConnection {
|
||||
|
||||
if !self.server_eof {
|
||||
loop {
|
||||
if self.pending_data.len() >= socket.send_capacity() {
|
||||
// Если буфер сокета заполнился, нет смысла выкачивать канал MPSC
|
||||
if !socket.can_send() {
|
||||
break;
|
||||
}
|
||||
|
||||
match self.core.rx.try_recv() {
|
||||
Ok(data) => {
|
||||
self.pending_data.extend_from_slice(&data);
|
||||
self.pending_data.push_back(data);
|
||||
}
|
||||
Err(mpsc::error::TryRecvError::Empty) => break,
|
||||
Err(mpsc::error::TryRecvError::Disconnected) => {
|
||||
@@ -181,14 +184,27 @@ impl TcpConnection {
|
||||
}
|
||||
}
|
||||
|
||||
if !self.pending_data.is_empty() && socket.can_send() {
|
||||
match socket.send_slice(&self.pending_data) {
|
||||
Ok(n) => {
|
||||
self.pending_data.advance(n);
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(%self.core.handle, "Smoltcp send error: {:?}", e);
|
||||
// 🔥 ФИКС: Отправляем данные кусками без копирования памяти
|
||||
while socket.can_send() {
|
||||
if let Some(mut chunk) = self.pending_data.pop_front() {
|
||||
match socket.send_slice(&chunk) {
|
||||
Ok(n) => {
|
||||
if n < chunk.len() {
|
||||
// Если сокет проглотил не всё, отрезаем отправленное,
|
||||
// а остаток возвращаем в начало очереди
|
||||
chunk.advance(n);
|
||||
self.pending_data.push_front(chunk);
|
||||
break; // Буфер сокета заполнен
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(%self.core.handle, "Smoltcp send error: {:?}", e);
|
||||
self.pending_data.push_front(chunk); // Возвращаем кусок обратно при ошибке
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break; // Нет больше данных для отправки
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,4 +412,4 @@ impl IcmpResponder {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
-15
@@ -15,7 +15,7 @@ use std::{
|
||||
time::Instant as StdInstant,
|
||||
};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||
use tokio::sync::mpsc::{self, Receiver, Sender};
|
||||
use tokio::time::{Duration, sleep};
|
||||
use tun::{DeviceReader, DeviceWriter};
|
||||
|
||||
@@ -35,8 +35,8 @@ pub struct Engine {
|
||||
socket_set: SocketSet<'static>,
|
||||
manager: ConnectionManager,
|
||||
device: VirtTunDevice,
|
||||
to_smoltcp_tx: UnboundedSender<TokenBuffer>,
|
||||
from_smoltcp_rx: Option<UnboundedReceiver<TokenBuffer>>,
|
||||
to_smoltcp_tx: Sender<TokenBuffer>,
|
||||
from_smoltcp_rx: Option<Receiver<TokenBuffer>>,
|
||||
avail: Arc<AtomicBool>,
|
||||
rx_from_tunnel: mpsc::Receiver<RawCastFrame>,
|
||||
}
|
||||
@@ -83,6 +83,7 @@ impl Engine {
|
||||
|
||||
let from_smoltcp_rx = self.from_smoltcp_rx.take().expect("Engine started twice");
|
||||
Self::spawn_tun_writer(writer, from_smoltcp_rx);
|
||||
|
||||
loop {
|
||||
// 1. Сначала обрабатываем всё, что накопилось в стеке
|
||||
let mut repeat_poll = true;
|
||||
@@ -122,16 +123,17 @@ impl Engine {
|
||||
if let Some(token) = msg {
|
||||
self.manager.try_create_socket_from_packet(&token, &mut self.socket_set);
|
||||
|
||||
// Если Muxer вернул "BUSY", smoltcp не запишет этот пакет,
|
||||
// и mark_rx_available не сработает. Это ПРАВИЛЬНО.
|
||||
if self.to_smoltcp_tx.send(token).is_ok() {
|
||||
// 🔥 ФИКС: try_send вместо send.
|
||||
// Если Muxer/smoltcp вернул "BUSY", smoltcp не запишет этот пакет.
|
||||
// Очередь дропнет пакет, и ОС поймет, что нужно сбавить скорость TCP.
|
||||
if self.to_smoltcp_tx.try_send(token).is_ok() {
|
||||
self.device.mark_rx_available();
|
||||
}
|
||||
|
||||
let mut count = 0;
|
||||
while let Ok(token) = tun_to_engine_rx.try_recv() {
|
||||
self.manager.try_create_socket_from_packet(&token, &mut self.socket_set);
|
||||
if self.to_smoltcp_tx.send(token).is_ok() {
|
||||
if self.to_smoltcp_tx.try_send(token).is_ok() {
|
||||
self.device.mark_rx_available();
|
||||
}
|
||||
count += 1;
|
||||
@@ -151,7 +153,7 @@ impl Engine {
|
||||
|
||||
fn spawn_tun_reader(
|
||||
mut reader: DeviceReader,
|
||||
to_engine: mpsc::Sender<TokenBuffer>, // 👈 ИЗМЕНЕНО ЗДЕСЬ
|
||||
to_engine: mpsc::Sender<TokenBuffer>,
|
||||
is_avail: Arc<AtomicBool>,
|
||||
) {
|
||||
tokio::spawn(async move {
|
||||
@@ -181,19 +183,24 @@ impl Engine {
|
||||
token.extend_from_slice(&buf[..n]);
|
||||
|
||||
// 🚨 ИСПОЛЬЗУЕМ .await. Это создаст идеальный Backpressure!
|
||||
// Если Engine занят, TUN просто перестанет читать из ОС телефона!
|
||||
if to_engine.send(token).await.is_ok() {
|
||||
is_avail.store(true, Ordering::Release);
|
||||
} else {
|
||||
break;
|
||||
// Если Engine занят, TUN просто перестанет читать из ОС.
|
||||
match to_engine.try_send(token) {
|
||||
Ok(_) => {
|
||||
is_avail.store(true, Ordering::Release);
|
||||
}
|
||||
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => {
|
||||
// Пакет отброшен (Drop). Идеальное поведение для борьбы с Bufferbloat.
|
||||
}
|
||||
Err(_) => break, // Канал закрыт
|
||||
}
|
||||
}
|
||||
warn!("TUN Reader task stopped");
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn_tun_writer(
|
||||
mut writer: DeviceWriter,
|
||||
mut from_smoltcp: UnboundedReceiver<TokenBuffer>,
|
||||
mut from_smoltcp: Receiver<TokenBuffer>,
|
||||
) {
|
||||
tokio::spawn(async move {
|
||||
debug!("TUN Writer task started");
|
||||
@@ -371,4 +378,4 @@ impl EngineBuilder {
|
||||
|
||||
Ok((engine, tun))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,21 +87,26 @@ impl SocketProvider for SmolSocketFactory {
|
||||
}
|
||||
|
||||
fn reconfigure_tcp(&self, socket: &mut tcp::Socket, profile: TrafficProfile) {
|
||||
// Выключаем Nagle везде, так как внутри Netrunner мы сами батчим данные в чанки (tcp_chunk_size),
|
||||
// а BBR сам управляет плавностью отправки (Pacing). Nagle здесь будет только мешать.
|
||||
socket.set_nagle_enabled(false);
|
||||
|
||||
match profile {
|
||||
TrafficProfile::Interactive => {
|
||||
socket.set_nagle_enabled(false);
|
||||
TrafficProfile::Interactive | TrafficProfile::Dns => {
|
||||
// Для интерактивного трафика (SSH, RDP, DNS) нам нужна минимальная задержка.
|
||||
// Отвечаем ACK'ами мгновенно.
|
||||
socket.set_ack_delay(None);
|
||||
}
|
||||
TrafficProfile::Bulk => {
|
||||
socket.set_nagle_enabled(false);
|
||||
socket.set_ack_delay(Some(Duration::from_millis(15)));
|
||||
}
|
||||
_ => {
|
||||
socket.set_nagle_enabled(false);
|
||||
socket.set_ack_delay(None);
|
||||
TrafficProfile::Bulk | TrafficProfile::Default => {
|
||||
// Для тяжелого трафика ставим крошечный delay (1 мс).
|
||||
// Это снизит нагрузку на CPU (будет меньше syscall-ов на отправку голых ACK),
|
||||
// но при этом 1 мс не испортит расчеты RTT и RateSample для BBR.
|
||||
socket.set_ack_delay(Some(Duration::from_millis(1)));
|
||||
}
|
||||
}
|
||||
socket.set_congestion_control(CongestionControl::Cubic);
|
||||
|
||||
// Включаем нашу ракету
|
||||
socket.set_congestion_control(CongestionControl::Bbr);
|
||||
}
|
||||
|
||||
fn create_udp(&self, profile: TrafficProfile) -> udp::Socket<'static> {
|
||||
|
||||
Reference in New Issue
Block a user