Avoiding busy loop in sending packet and optimize device caps

This commit is contained in:
jiangjianfeng
2024-09-25 11:15:58 +00:00
committed by Tate, Hongliang Tian
parent e0106f1f18
commit f793259512
9 changed files with 198 additions and 97 deletions

View File

@ -63,7 +63,7 @@ pub struct VirtioNetConfig {
pub mac: EthernetAddr,
pub status: Status,
max_virtqueue_pairs: u16,
mtu: u16,
pub mtu: u16,
speed: u32,
duplex: u8,
rss_max_key_size: u8,

View File

@ -1,16 +1,21 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::{boxed::Box, string::ToString, sync::Arc};
use core::{fmt::Debug, hint::spin_loop, mem::size_of};
use alloc::{
boxed::Box, collections::linked_list::LinkedList, string::ToString, sync::Arc, vec::Vec,
};
use core::{fmt::Debug, mem::size_of};
use aster_bigtcp::device::{DeviceCapabilities, Medium};
use aster_bigtcp::device::{Checksum, DeviceCapabilities, Medium};
use aster_network::{
AnyNetworkDevice, EthernetAddr, RxBuffer, TxBuffer, VirtioNetError, RX_BUFFER_POOL,
TX_BUFFER_POOL,
};
use aster_util::slot_vec::SlotVec;
use log::debug;
use ostd::{sync::SpinLock, trap::TrapFrame};
use ostd::{
mm::DmaStream,
sync::{LocalIrqDisabled, SpinLock},
trap::TrapFrame,
};
use super::{config::VirtioNetConfig, header::VirtioNetHdr};
use crate::{
@ -21,9 +26,15 @@ use crate::{
pub struct NetworkDevice {
config: VirtioNetConfig,
// For smoltcp use
caps: DeviceCapabilities,
mac_addr: EthernetAddr,
send_queue: VirtQueue,
recv_queue: VirtQueue,
// Since the virtio net header remains consistent for each sending packet,
// we store it to avoid recreating the header repeatedly.
header: VirtioNetHdr,
tx_buffers: Vec<Option<TxBuffer>>,
rx_buffers: SlotVec<RxBuffer>,
transport: Box<dyn VirtioTransport>,
}
@ -48,11 +59,15 @@ impl NetworkDevice {
let config = VirtioNetConfig::read(&virtio_net_config).unwrap();
let mac_addr = config.mac;
debug!("mac addr = {:x?}, status = {:?}", mac_addr, config.status);
let caps = init_caps(&features, &config);
let send_queue = VirtQueue::new(QUEUE_SEND, QUEUE_SIZE, transport.as_mut())
.expect("create send queue fails");
let mut recv_queue = VirtQueue::new(QUEUE_RECV, QUEUE_SIZE, transport.as_mut())
.expect("creating recv queue fails");
let send_queue = VirtQueue::new(QUEUE_SEND, QUEUE_SIZE, transport.as_mut())
.expect("create send queue fails");
let tx_buffers = (0..QUEUE_SIZE).map(|_| None).collect();
let mut rx_buffers = SlotVec::new();
for i in 0..QUEUE_SIZE {
@ -68,11 +83,15 @@ impl NetworkDevice {
debug!("notify receive queue");
recv_queue.notify();
}
let mut device = Self {
config,
caps,
mac_addr,
send_queue,
recv_queue,
header: VirtioNetHdr::default(),
tx_buffers,
rx_buffers,
transport,
};
@ -82,8 +101,11 @@ impl NetworkDevice {
debug!("network device config space change");
}
/// Interrupt handler if network device receives some packet
fn handle_network_event(_: &TrapFrame) {
/// Interrupt handlers if network device receives/sends some packet
fn handle_send_event(_: &TrapFrame) {
aster_network::handle_send_irq(super::DEVICE_NAME);
}
fn handle_recv_event(_: &TrapFrame) {
aster_network::handle_recv_irq(super::DEVICE_NAME);
}
@ -93,8 +115,13 @@ impl NetworkDevice {
.unwrap();
device
.transport
.register_queue_callback(QUEUE_RECV, Box::new(handle_network_event), false)
.register_queue_callback(QUEUE_SEND, Box::new(handle_send_event), false)
.unwrap();
device
.transport
.register_queue_callback(QUEUE_RECV, Box::new(handle_recv_event), false)
.unwrap();
device.transport.finish_init();
aster_network::register_device(
@ -139,29 +166,23 @@ impl NetworkDevice {
/// Send a packet to network. Return until the request completes.
/// FIEME: Replace tx_buffer with VM segment-based data structure to use dma mapping.
fn send(&mut self, packet: &[u8]) -> Result<(), VirtioNetError> {
let header = VirtioNetHdr::default();
let tx_pool = TX_BUFFER_POOL.get().unwrap();
let tx_buffer = TxBuffer::new(&header, packet, tx_pool);
if !self.can_send() {
return Err(VirtioNetError::Busy);
}
let tx_buffer = TxBuffer::new(&self.header, packet, &TX_BUFFER_POOL);
let token = self
.send_queue
.add_dma_buf(&[&tx_buffer], &[])
.map_err(queue_to_network_error)?;
if self.send_queue.should_notify() {
self.send_queue.notify();
}
// Wait until the buffer is used
while !self.send_queue.can_pop() {
spin_loop();
}
// Pop out the buffer, so we can reuse the send queue further
let (pop_token, _) = self.send_queue.pop_used().map_err(queue_to_network_error)?;
debug_assert!(pop_token == token);
if pop_token != token {
return Err(VirtioNetError::WrongToken);
}
debug!("send packet succeeds");
debug_assert!(self.tx_buffers[token as usize].is_none());
self.tx_buffers[token as usize] = Some(tx_buffer);
Ok(())
}
}
@ -170,21 +191,57 @@ fn queue_to_network_error(err: QueueError) -> VirtioNetError {
match err {
QueueError::NotReady => VirtioNetError::NotReady,
QueueError::WrongToken => VirtioNetError::WrongToken,
QueueError::BufferTooSmall => VirtioNetError::Busy,
_ => VirtioNetError::Unknown,
}
}
fn init_caps(features: &NetworkFeatures, config: &VirtioNetConfig) -> DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.max_burst_size = None;
caps.medium = Medium::Ethernet;
if features.contains(NetworkFeatures::VIRTIO_NET_F_MTU) {
// If `VIRTIO_NET_F_MTU` is negotiated, the MTU is decided by the device.
caps.max_transmission_unit = config.mtu as usize;
} else {
// We do not support these features,
// so this asserts that they are _not_ negotiated.
//
// Without these features, the MTU is 1514 bytes per the virtio-net specification
// (see "5.1.6.3 Setting Up Receive Buffers" and "5.1.6.2 Packet Transmission").
assert!(
!features.contains(NetworkFeatures::VIRTIO_NET_F_GUEST_TSO4)
&& !features.contains(NetworkFeatures::VIRTIO_NET_F_GUEST_TSO6)
&& !features.contains(NetworkFeatures::VIRTIO_NET_F_GUEST_UFO)
);
caps.max_transmission_unit = 1514;
}
// We do not support checksum offloading.
// So the features must not be negotiated,
// and we must deliver fully checksummed packets to the device
// and validate all checksums for packets from the device.
assert!(
!features.contains(NetworkFeatures::VIRTIO_NET_F_CSUM)
&& !features.contains(NetworkFeatures::VIRTIO_NET_F_GUEST_CSUM)
);
caps.checksum.tcp = Checksum::Both;
caps.checksum.udp = Checksum::Both;
caps.checksum.ipv4 = Checksum::Both;
caps.checksum.icmpv4 = Checksum::Both;
caps
}
impl AnyNetworkDevice for NetworkDevice {
fn mac_addr(&self) -> EthernetAddr {
self.mac_addr
}
fn capabilities(&self) -> DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1536;
caps.max_burst_size = Some(1);
caps.medium = Medium::Ethernet;
caps
self.caps.clone()
}
fn can_receive(&self) -> bool {
@ -192,7 +249,7 @@ impl AnyNetworkDevice for NetworkDevice {
}
fn can_send(&self) -> bool {
self.send_queue.available_desc() >= 2
self.send_queue.available_desc() >= 1
}
fn receive(&mut self) -> Result<RxBuffer, VirtioNetError> {
@ -202,6 +259,12 @@ impl AnyNetworkDevice for NetworkDevice {
fn send(&mut self, packet: &[u8]) -> Result<(), VirtioNetError> {
self.send(packet)
}
fn free_processed_tx_buffers(&mut self) {
while let Ok((token, _)) = self.send_queue.pop_used() {
self.tx_buffers[token as usize] = None;
}
}
}
impl Debug for NetworkDevice {
@ -216,6 +279,9 @@ impl Debug for NetworkDevice {
}
}
static TX_BUFFER_POOL: SpinLock<LinkedList<DmaStream>, LocalIrqDisabled> =
SpinLock::new(LinkedList::new());
const QUEUE_RECV: u16 = 0;
const QUEUE_SEND: u16 = 1;

View File

@ -1,17 +1,18 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::{collections::LinkedList, sync::Arc};
use alloc::{collections::linked_list::LinkedList, sync::Arc};
use aster_network::dma_pool::DmaPool;
use ostd::{
mm::{DmaDirection, DmaStream},
sync::SpinLock,
sync::{LocalIrqDisabled, SpinLock},
};
use spin::Once;
const RX_BUFFER_LEN: usize = 4096;
const TX_BUFFER_LEN: usize = 4096;
pub static RX_BUFFER_POOL: Once<Arc<DmaPool>> = Once::new();
pub static TX_BUFFER_POOL: Once<SpinLock<LinkedList<DmaStream>>> = Once::new();
pub static TX_BUFFER_POOL: Once<SpinLock<LinkedList<DmaStream>, LocalIrqDisabled>> = Once::new();
pub fn init() {
const POOL_INIT_SIZE: usize = 32;

View File

@ -190,8 +190,10 @@ impl SocketDevice {
) -> Result<(), SocketError> {
debug!("Sent packet {:?}. Op {:?}", header, header.op());
debug!("buffer in send_packet_to_tx_queue: {:?}", buffer);
let tx_pool = TX_BUFFER_POOL.get().unwrap();
let tx_buffer = TxBuffer::new(header, buffer, tx_pool);
let tx_buffer = {
let pool = TX_BUFFER_POOL.get().unwrap();
TxBuffer::new(header, buffer, pool)
};
let token = self.send_queue.add_dma_buf(&[&tx_buffer], &[])?;