mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-21 16:33:24 +00:00
Avoiding busy loop in sending packet and optimize device caps
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
e0106f1f18
commit
f793259512
@ -1,14 +1,13 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use alloc::{collections::LinkedList, sync::Arc};
|
use alloc::{collections::linked_list::LinkedList, sync::Arc};
|
||||||
|
|
||||||
use align_ext::AlignExt;
|
|
||||||
use ostd::{
|
use ostd::{
|
||||||
mm::{
|
mm::{
|
||||||
Daddr, DmaDirection, DmaStream, FrameAllocOptions, HasDaddr, Infallible, VmReader,
|
Daddr, DmaDirection, DmaStream, FrameAllocOptions, HasDaddr, Infallible, VmReader,
|
||||||
VmWriter, PAGE_SIZE,
|
VmWriter, PAGE_SIZE,
|
||||||
},
|
},
|
||||||
sync::SpinLock,
|
sync::{LocalIrqDisabled, SpinLock},
|
||||||
Pod,
|
Pod,
|
||||||
};
|
};
|
||||||
use spin::Once;
|
use spin::Once;
|
||||||
@ -18,37 +17,40 @@ use crate::dma_pool::{DmaPool, DmaSegment};
|
|||||||
pub struct TxBuffer {
|
pub struct TxBuffer {
|
||||||
dma_stream: DmaStream,
|
dma_stream: DmaStream,
|
||||||
nbytes: usize,
|
nbytes: usize,
|
||||||
pool: &'static SpinLock<LinkedList<DmaStream>>,
|
pool: &'static SpinLock<LinkedList<DmaStream>, LocalIrqDisabled>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TxBuffer {
|
impl TxBuffer {
|
||||||
pub fn new<H: Pod>(
|
pub fn new<H: Pod>(
|
||||||
header: &H,
|
header: &H,
|
||||||
packet: &[u8],
|
packet: &[u8],
|
||||||
pool: &'static SpinLock<LinkedList<DmaStream>>,
|
pool: &'static SpinLock<LinkedList<DmaStream>, LocalIrqDisabled>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let header = header.as_bytes();
|
let header = header.as_bytes();
|
||||||
let nbytes = header.len() + packet.len();
|
let nbytes = header.len() + packet.len();
|
||||||
|
|
||||||
let dma_stream = if let Some(stream) = get_tx_stream_from_pool(nbytes, pool) {
|
assert!(nbytes <= TX_BUFFER_LEN);
|
||||||
|
|
||||||
|
let dma_stream = if let Some(stream) = pool.lock().pop_front() {
|
||||||
stream
|
stream
|
||||||
} else {
|
} else {
|
||||||
let segment = {
|
let segment = FrameAllocOptions::new(TX_BUFFER_LEN / PAGE_SIZE)
|
||||||
let nframes = (nbytes.align_up(PAGE_SIZE)) / PAGE_SIZE;
|
.alloc_contiguous()
|
||||||
FrameAllocOptions::new(nframes).alloc_contiguous().unwrap()
|
.unwrap();
|
||||||
};
|
|
||||||
DmaStream::map(segment, DmaDirection::ToDevice, false).unwrap()
|
DmaStream::map(segment, DmaDirection::ToDevice, false).unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let tx_buffer = {
|
||||||
let mut writer = dma_stream.writer().unwrap();
|
let mut writer = dma_stream.writer().unwrap();
|
||||||
writer.write(&mut VmReader::from(header));
|
writer.write(&mut VmReader::from(header));
|
||||||
writer.write(&mut VmReader::from(packet));
|
writer.write(&mut VmReader::from(packet));
|
||||||
|
Self {
|
||||||
let tx_buffer = Self {
|
|
||||||
dma_stream,
|
dma_stream,
|
||||||
nbytes,
|
nbytes,
|
||||||
pool,
|
pool,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tx_buffer.sync();
|
tx_buffer.sync();
|
||||||
tx_buffer
|
tx_buffer
|
||||||
}
|
}
|
||||||
@ -74,10 +76,7 @@ impl HasDaddr for TxBuffer {
|
|||||||
|
|
||||||
impl Drop for TxBuffer {
|
impl Drop for TxBuffer {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.pool
|
self.pool.lock().push_back(self.dma_stream.clone());
|
||||||
.disable_irq()
|
|
||||||
.lock()
|
|
||||||
.push_back(self.dma_stream.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,29 +138,13 @@ impl HasDaddr for RxBuffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const RX_BUFFER_LEN: usize = 4096;
|
pub const RX_BUFFER_LEN: usize = 4096;
|
||||||
|
pub const TX_BUFFER_LEN: usize = 4096;
|
||||||
pub static RX_BUFFER_POOL: Once<Arc<DmaPool>> = Once::new();
|
pub static RX_BUFFER_POOL: Once<Arc<DmaPool>> = Once::new();
|
||||||
pub static TX_BUFFER_POOL: Once<SpinLock<LinkedList<DmaStream>>> = Once::new();
|
|
||||||
|
|
||||||
fn get_tx_stream_from_pool(
|
|
||||||
nbytes: usize,
|
|
||||||
tx_buffer_pool: &'static SpinLock<LinkedList<DmaStream>>,
|
|
||||||
) -> Option<DmaStream> {
|
|
||||||
let mut pool = tx_buffer_pool.disable_irq().lock();
|
|
||||||
let mut cursor = pool.cursor_front_mut();
|
|
||||||
while let Some(current) = cursor.current() {
|
|
||||||
if current.nbytes() >= nbytes {
|
|
||||||
return cursor.remove_current();
|
|
||||||
}
|
|
||||||
cursor.move_next();
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
const POOL_INIT_SIZE: usize = 32;
|
const POOL_INIT_SIZE: usize = 64;
|
||||||
const POOL_HIGH_WATERMARK: usize = 64;
|
const POOL_HIGH_WATERMARK: usize = 128;
|
||||||
RX_BUFFER_POOL.call_once(|| {
|
RX_BUFFER_POOL.call_once(|| {
|
||||||
DmaPool::new(
|
DmaPool::new(
|
||||||
RX_BUFFER_LEN,
|
RX_BUFFER_LEN,
|
||||||
@ -171,5 +154,4 @@ pub fn init() {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
TX_BUFFER_POOL.call_once(|| SpinLock::new(LinkedList::new()));
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ impl device::Device for dyn AnyNetworkDevice {
|
|||||||
type TxToken<'a> = TxToken<'a>;
|
type TxToken<'a> = TxToken<'a>;
|
||||||
|
|
||||||
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||||
if self.can_receive() {
|
if self.can_receive() && self.can_send() {
|
||||||
let rx_buffer = self.receive().unwrap();
|
let rx_buffer = self.receive().unwrap();
|
||||||
Some((RxToken(rx_buffer), TxToken(self)))
|
Some((RxToken(rx_buffer), TxToken(self)))
|
||||||
} else {
|
} else {
|
||||||
|
@ -16,7 +16,7 @@ use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec};
|
|||||||
use core::{any::Any, fmt::Debug};
|
use core::{any::Any, fmt::Debug};
|
||||||
|
|
||||||
use aster_bigtcp::device::DeviceCapabilities;
|
use aster_bigtcp::device::DeviceCapabilities;
|
||||||
pub use buffer::{RxBuffer, TxBuffer, RX_BUFFER_POOL, TX_BUFFER_POOL};
|
pub use buffer::{RxBuffer, TxBuffer, RX_BUFFER_POOL, TX_BUFFER_LEN};
|
||||||
use component::{init_component, ComponentInitError};
|
use component::{init_component, ComponentInitError};
|
||||||
pub use dma_pool::DmaSegment;
|
pub use dma_pool::DmaSegment;
|
||||||
use ostd::{
|
use ostd::{
|
||||||
@ -33,6 +33,7 @@ pub struct EthernetAddr(pub [u8; 6]);
|
|||||||
pub enum VirtioNetError {
|
pub enum VirtioNetError {
|
||||||
NotReady,
|
NotReady,
|
||||||
WrongToken,
|
WrongToken,
|
||||||
|
Busy,
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +52,7 @@ pub trait AnyNetworkDevice: Send + Sync + Any + Debug {
|
|||||||
fn receive(&mut self) -> Result<RxBuffer, VirtioNetError>;
|
fn receive(&mut self) -> Result<RxBuffer, VirtioNetError>;
|
||||||
/// Send a packet to network. Return until the request completes.
|
/// Send a packet to network. Return until the request completes.
|
||||||
fn send(&mut self, packet: &[u8]) -> Result<(), VirtioNetError>;
|
fn send(&mut self, packet: &[u8]) -> Result<(), VirtioNetError>;
|
||||||
|
fn free_processed_tx_buffers(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait NetDeviceIrqHandler = Fn() + Send + Sync + 'static;
|
pub trait NetDeviceIrqHandler = Fn() + Send + Sync + 'static;
|
||||||
@ -64,13 +66,13 @@ pub fn register_device(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.network_device_table
|
.network_device_table
|
||||||
.lock()
|
.lock()
|
||||||
.insert(name, (Arc::new(SpinLock::new(Vec::new())), device));
|
.insert(name, NetworkDeviceIrqCallbackSet::new(device));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_device(str: &str) -> Option<Arc<SpinLock<dyn AnyNetworkDevice, LocalIrqDisabled>>> {
|
pub fn get_device(str: &str) -> Option<Arc<SpinLock<dyn AnyNetworkDevice, LocalIrqDisabled>>> {
|
||||||
let table = COMPONENT.get().unwrap().network_device_table.lock();
|
let table = COMPONENT.get().unwrap().network_device_table.lock();
|
||||||
let (_, device) = table.get(str)?;
|
let callbacks = table.get(str)?;
|
||||||
Some(device.clone())
|
Some(callbacks.device.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers callback which will be called when receiving message.
|
/// Registers callback which will be called when receiving message.
|
||||||
@ -79,18 +81,48 @@ pub fn get_device(str: &str) -> Option<Arc<SpinLock<dyn AnyNetworkDevice, LocalI
|
|||||||
/// the callback function should NOT sleep.
|
/// the callback function should NOT sleep.
|
||||||
pub fn register_recv_callback(name: &str, callback: impl NetDeviceIrqHandler) {
|
pub fn register_recv_callback(name: &str, callback: impl NetDeviceIrqHandler) {
|
||||||
let device_table = COMPONENT.get().unwrap().network_device_table.lock();
|
let device_table = COMPONENT.get().unwrap().network_device_table.lock();
|
||||||
let Some((callbacks, _)) = device_table.get(name) else {
|
let Some(callbacks) = device_table.get(name) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
callbacks.lock().push(Arc::new(callback));
|
callbacks.recv_callbacks.lock().push(Arc::new(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_send_callback(name: &str, callback: impl NetDeviceIrqHandler) {
|
||||||
|
let device_table = COMPONENT.get().unwrap().network_device_table.lock();
|
||||||
|
let Some(callbacks) = device_table.get(name) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
callbacks.send_callbacks.lock().push(Arc::new(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_recv_irq(name: &str) {
|
pub fn handle_recv_irq(name: &str) {
|
||||||
let device_table = COMPONENT.get().unwrap().network_device_table.lock();
|
let device_table = COMPONENT.get().unwrap().network_device_table.lock();
|
||||||
let Some((callbacks, _)) = device_table.get(name) else {
|
let Some(callbacks) = device_table.get(name) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let callbacks = callbacks.lock();
|
|
||||||
|
let callbacks = callbacks.recv_callbacks.lock();
|
||||||
|
for callback in callbacks.iter() {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_send_irq(name: &str) {
|
||||||
|
let device_table = COMPONENT.get().unwrap().network_device_table.lock();
|
||||||
|
let Some(callbacks) = device_table.get(name) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let can_send = {
|
||||||
|
let mut device = callbacks.device.lock();
|
||||||
|
device.free_processed_tx_buffers();
|
||||||
|
device.can_send()
|
||||||
|
};
|
||||||
|
if !can_send {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let callbacks = callbacks.send_callbacks.lock();
|
||||||
for callback in callbacks.iter() {
|
for callback in callbacks.iter() {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
@ -100,7 +132,7 @@ pub fn all_devices() -> Vec<(String, NetworkDeviceRef)> {
|
|||||||
let network_devs = COMPONENT.get().unwrap().network_device_table.lock();
|
let network_devs = COMPONENT.get().unwrap().network_device_table.lock();
|
||||||
network_devs
|
network_devs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, (_, device))| (name.clone(), device.clone()))
|
.map(|(name, callbacks)| (name.clone(), callbacks.device.clone()))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,10 +156,24 @@ type NetworkDeviceRef = Arc<SpinLock<dyn AnyNetworkDevice, LocalIrqDisabled>>;
|
|||||||
|
|
||||||
struct Component {
|
struct Component {
|
||||||
/// Device list, the key is device name, value is (callbacks, device);
|
/// Device list, the key is device name, value is (callbacks, device);
|
||||||
network_device_table: SpinLock<
|
network_device_table: SpinLock<BTreeMap<String, NetworkDeviceIrqCallbackSet>, LocalIrqDisabled>,
|
||||||
BTreeMap<String, (NetDeviceIrqHandlerListRef, NetworkDeviceRef)>,
|
}
|
||||||
LocalIrqDisabled,
|
|
||||||
>,
|
/// The send callbacks and recv callbacks for a network device
|
||||||
|
struct NetworkDeviceIrqCallbackSet {
|
||||||
|
device: NetworkDeviceRef,
|
||||||
|
recv_callbacks: NetDeviceIrqHandlerListRef,
|
||||||
|
send_callbacks: NetDeviceIrqHandlerListRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkDeviceIrqCallbackSet {
|
||||||
|
fn new(device: NetworkDeviceRef) -> Self {
|
||||||
|
Self {
|
||||||
|
device,
|
||||||
|
recv_callbacks: Arc::new(SpinLock::new(Vec::new())),
|
||||||
|
send_callbacks: Arc::new(SpinLock::new(Vec::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component {
|
impl Component {
|
||||||
|
@ -63,7 +63,7 @@ pub struct VirtioNetConfig {
|
|||||||
pub mac: EthernetAddr,
|
pub mac: EthernetAddr,
|
||||||
pub status: Status,
|
pub status: Status,
|
||||||
max_virtqueue_pairs: u16,
|
max_virtqueue_pairs: u16,
|
||||||
mtu: u16,
|
pub mtu: u16,
|
||||||
speed: u32,
|
speed: u32,
|
||||||
duplex: u8,
|
duplex: u8,
|
||||||
rss_max_key_size: u8,
|
rss_max_key_size: u8,
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use alloc::{boxed::Box, string::ToString, sync::Arc};
|
use alloc::{
|
||||||
use core::{fmt::Debug, hint::spin_loop, mem::size_of};
|
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::{
|
use aster_network::{
|
||||||
AnyNetworkDevice, EthernetAddr, RxBuffer, TxBuffer, VirtioNetError, RX_BUFFER_POOL,
|
AnyNetworkDevice, EthernetAddr, RxBuffer, TxBuffer, VirtioNetError, RX_BUFFER_POOL,
|
||||||
TX_BUFFER_POOL,
|
|
||||||
};
|
};
|
||||||
use aster_util::slot_vec::SlotVec;
|
use aster_util::slot_vec::SlotVec;
|
||||||
use log::debug;
|
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 super::{config::VirtioNetConfig, header::VirtioNetHdr};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -21,9 +26,15 @@ use crate::{
|
|||||||
|
|
||||||
pub struct NetworkDevice {
|
pub struct NetworkDevice {
|
||||||
config: VirtioNetConfig,
|
config: VirtioNetConfig,
|
||||||
|
// For smoltcp use
|
||||||
|
caps: DeviceCapabilities,
|
||||||
mac_addr: EthernetAddr,
|
mac_addr: EthernetAddr,
|
||||||
send_queue: VirtQueue,
|
send_queue: VirtQueue,
|
||||||
recv_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>,
|
rx_buffers: SlotVec<RxBuffer>,
|
||||||
transport: Box<dyn VirtioTransport>,
|
transport: Box<dyn VirtioTransport>,
|
||||||
}
|
}
|
||||||
@ -48,11 +59,15 @@ impl NetworkDevice {
|
|||||||
let config = VirtioNetConfig::read(&virtio_net_config).unwrap();
|
let config = VirtioNetConfig::read(&virtio_net_config).unwrap();
|
||||||
let mac_addr = config.mac;
|
let mac_addr = config.mac;
|
||||||
debug!("mac addr = {:x?}, status = {:?}", mac_addr, config.status);
|
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())
|
let mut recv_queue = VirtQueue::new(QUEUE_RECV, QUEUE_SIZE, transport.as_mut())
|
||||||
.expect("creating recv queue fails");
|
.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();
|
let mut rx_buffers = SlotVec::new();
|
||||||
for i in 0..QUEUE_SIZE {
|
for i in 0..QUEUE_SIZE {
|
||||||
@ -68,11 +83,15 @@ impl NetworkDevice {
|
|||||||
debug!("notify receive queue");
|
debug!("notify receive queue");
|
||||||
recv_queue.notify();
|
recv_queue.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut device = Self {
|
let mut device = Self {
|
||||||
config,
|
config,
|
||||||
|
caps,
|
||||||
mac_addr,
|
mac_addr,
|
||||||
send_queue,
|
send_queue,
|
||||||
recv_queue,
|
recv_queue,
|
||||||
|
header: VirtioNetHdr::default(),
|
||||||
|
tx_buffers,
|
||||||
rx_buffers,
|
rx_buffers,
|
||||||
transport,
|
transport,
|
||||||
};
|
};
|
||||||
@ -82,8 +101,11 @@ impl NetworkDevice {
|
|||||||
debug!("network device config space change");
|
debug!("network device config space change");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interrupt handler if network device receives some packet
|
/// Interrupt handlers if network device receives/sends some packet
|
||||||
fn handle_network_event(_: &TrapFrame) {
|
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);
|
aster_network::handle_recv_irq(super::DEVICE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,8 +115,13 @@ impl NetworkDevice {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
device
|
device
|
||||||
.transport
|
.transport
|
||||||
.register_queue_callback(QUEUE_RECV, Box::new(handle_network_event), false)
|
.register_queue_callback(QUEUE_SEND, Box::new(handle_send_event), false)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
device
|
||||||
|
.transport
|
||||||
|
.register_queue_callback(QUEUE_RECV, Box::new(handle_recv_event), false)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
device.transport.finish_init();
|
device.transport.finish_init();
|
||||||
|
|
||||||
aster_network::register_device(
|
aster_network::register_device(
|
||||||
@ -139,29 +166,23 @@ impl NetworkDevice {
|
|||||||
/// Send a packet to network. Return until the request completes.
|
/// Send a packet to network. Return until the request completes.
|
||||||
/// FIEME: Replace tx_buffer with VM segment-based data structure to use dma mapping.
|
/// FIEME: Replace tx_buffer with VM segment-based data structure to use dma mapping.
|
||||||
fn send(&mut self, packet: &[u8]) -> Result<(), VirtioNetError> {
|
fn send(&mut self, packet: &[u8]) -> Result<(), VirtioNetError> {
|
||||||
let header = VirtioNetHdr::default();
|
if !self.can_send() {
|
||||||
let tx_pool = TX_BUFFER_POOL.get().unwrap();
|
return Err(VirtioNetError::Busy);
|
||||||
let tx_buffer = TxBuffer::new(&header, packet, tx_pool);
|
}
|
||||||
|
|
||||||
|
let tx_buffer = TxBuffer::new(&self.header, packet, &TX_BUFFER_POOL);
|
||||||
|
|
||||||
let token = self
|
let token = self
|
||||||
.send_queue
|
.send_queue
|
||||||
.add_dma_buf(&[&tx_buffer], &[])
|
.add_dma_buf(&[&tx_buffer], &[])
|
||||||
.map_err(queue_to_network_error)?;
|
.map_err(queue_to_network_error)?;
|
||||||
|
|
||||||
if self.send_queue.should_notify() {
|
if self.send_queue.should_notify() {
|
||||||
self.send_queue.notify();
|
self.send_queue.notify();
|
||||||
}
|
}
|
||||||
// Wait until the buffer is used
|
|
||||||
while !self.send_queue.can_pop() {
|
debug_assert!(self.tx_buffers[token as usize].is_none());
|
||||||
spin_loop();
|
self.tx_buffers[token as usize] = Some(tx_buffer);
|
||||||
}
|
|
||||||
// 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");
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,21 +191,57 @@ fn queue_to_network_error(err: QueueError) -> VirtioNetError {
|
|||||||
match err {
|
match err {
|
||||||
QueueError::NotReady => VirtioNetError::NotReady,
|
QueueError::NotReady => VirtioNetError::NotReady,
|
||||||
QueueError::WrongToken => VirtioNetError::WrongToken,
|
QueueError::WrongToken => VirtioNetError::WrongToken,
|
||||||
|
QueueError::BufferTooSmall => VirtioNetError::Busy,
|
||||||
_ => VirtioNetError::Unknown,
|
_ => 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 {
|
impl AnyNetworkDevice for NetworkDevice {
|
||||||
fn mac_addr(&self) -> EthernetAddr {
|
fn mac_addr(&self) -> EthernetAddr {
|
||||||
self.mac_addr
|
self.mac_addr
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capabilities(&self) -> DeviceCapabilities {
|
fn capabilities(&self) -> DeviceCapabilities {
|
||||||
let mut caps = DeviceCapabilities::default();
|
self.caps.clone()
|
||||||
caps.max_transmission_unit = 1536;
|
|
||||||
caps.max_burst_size = Some(1);
|
|
||||||
caps.medium = Medium::Ethernet;
|
|
||||||
caps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_receive(&self) -> bool {
|
fn can_receive(&self) -> bool {
|
||||||
@ -192,7 +249,7 @@ impl AnyNetworkDevice for NetworkDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn can_send(&self) -> bool {
|
fn can_send(&self) -> bool {
|
||||||
self.send_queue.available_desc() >= 2
|
self.send_queue.available_desc() >= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn receive(&mut self) -> Result<RxBuffer, VirtioNetError> {
|
fn receive(&mut self) -> Result<RxBuffer, VirtioNetError> {
|
||||||
@ -202,6 +259,12 @@ impl AnyNetworkDevice for NetworkDevice {
|
|||||||
fn send(&mut self, packet: &[u8]) -> Result<(), VirtioNetError> {
|
fn send(&mut self, packet: &[u8]) -> Result<(), VirtioNetError> {
|
||||||
self.send(packet)
|
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 {
|
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_RECV: u16 = 0;
|
||||||
const QUEUE_SEND: u16 = 1;
|
const QUEUE_SEND: u16 = 1;
|
||||||
|
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// 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 aster_network::dma_pool::DmaPool;
|
||||||
use ostd::{
|
use ostd::{
|
||||||
mm::{DmaDirection, DmaStream},
|
mm::{DmaDirection, DmaStream},
|
||||||
sync::SpinLock,
|
sync::{LocalIrqDisabled, SpinLock},
|
||||||
};
|
};
|
||||||
use spin::Once;
|
use spin::Once;
|
||||||
|
|
||||||
const RX_BUFFER_LEN: usize = 4096;
|
const RX_BUFFER_LEN: usize = 4096;
|
||||||
|
const TX_BUFFER_LEN: usize = 4096;
|
||||||
pub static RX_BUFFER_POOL: Once<Arc<DmaPool>> = Once::new();
|
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() {
|
pub fn init() {
|
||||||
const POOL_INIT_SIZE: usize = 32;
|
const POOL_INIT_SIZE: usize = 32;
|
||||||
|
@ -190,8 +190,10 @@ impl SocketDevice {
|
|||||||
) -> Result<(), SocketError> {
|
) -> Result<(), SocketError> {
|
||||||
debug!("Sent packet {:?}. Op {:?}", header, header.op());
|
debug!("Sent packet {:?}. Op {:?}", header, header.op());
|
||||||
debug!("buffer in send_packet_to_tx_queue: {:?}", buffer);
|
debug!("buffer in send_packet_to_tx_queue: {:?}", buffer);
|
||||||
let tx_pool = TX_BUFFER_POOL.get().unwrap();
|
let tx_buffer = {
|
||||||
let tx_buffer = TxBuffer::new(header, buffer, tx_pool);
|
let pool = TX_BUFFER_POOL.get().unwrap();
|
||||||
|
TxBuffer::new(header, buffer, pool)
|
||||||
|
};
|
||||||
|
|
||||||
let token = self.send_queue.add_dma_buf(&[&tx_buffer], &[])?;
|
let token = self.send_queue.add_dma_buf(&[&tx_buffer], &[])?;
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
pub use smoltcp::phy::{Device, DeviceCapabilities, Loopback, Medium, RxToken, TxToken};
|
pub use smoltcp::phy::{
|
||||||
|
Checksum, ChecksumCapabilities, Device, DeviceCapabilities, Loopback, Medium, RxToken, TxToken,
|
||||||
|
};
|
||||||
|
|
||||||
/// A trait that allows to obtain a mutable reference of [`Device`].
|
/// A trait that allows to obtain a mutable reference of [`Device`].
|
||||||
///
|
///
|
||||||
|
@ -22,11 +22,13 @@ pub fn init() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
for (name, _) in aster_network::all_devices() {
|
for (name, _) in aster_network::all_devices() {
|
||||||
aster_network::register_recv_callback(&name, || {
|
let callback = || {
|
||||||
// TODO: further check that the irq num is the same as iface's irq num
|
// TODO: further check that the irq num is the same as iface's irq num
|
||||||
let iface_virtio = &IFACES.get().unwrap()[0];
|
let iface_virtio = &IFACES.get().unwrap()[0];
|
||||||
iface_virtio.poll();
|
iface_virtio.poll();
|
||||||
})
|
};
|
||||||
|
aster_network::register_recv_callback(&name, callback);
|
||||||
|
aster_network::register_send_callback(&name, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
poll_ifaces();
|
poll_ifaces();
|
||||||
|
Reference in New Issue
Block a user