Skip to content

Commit c3f3063

Browse files
committed
refactor(drivers/net): Unify MTUs and fix buffer sizes for virtio-net
Signed-off-by: Jens Reidel <[email protected]>
1 parent 4a33adb commit c3f3063

File tree

6 files changed

+87
-62
lines changed

6 files changed

+87
-62
lines changed

src/drivers/net/gem.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::arch::kernel::interrupts::*;
2626
use crate::arch::kernel::mmio as hardware;
2727
use crate::arch::mm::paging::PageTableEntryFlags;
2828
use crate::drivers::error::DriverError;
29-
use crate::drivers::net::NetworkDriver;
29+
use crate::drivers::net::{NetworkDriver, mtu};
3030
#[cfg(all(any(feature = "tcp", feature = "udp"), feature = "pci"))]
3131
use crate::drivers::pci as hardware;
3232
use crate::drivers::{Driver, InterruptLine};
@@ -705,7 +705,7 @@ pub fn init_device(
705705

706706
Ok(GEMDriver {
707707
gem,
708-
mtu: 1500,
708+
mtu: mtu(),
709709
irq,
710710
mac,
711711
rx_counter: 0,

src/drivers/net/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ pub mod rtl8139;
55
#[cfg(not(all(target_arch = "x86_64", feature = "rtl8139")))]
66
pub mod virtio;
77

8+
use core::str::FromStr;
9+
810
use smoltcp::phy::ChecksumCapabilities;
911

1012
#[allow(unused_imports)]
@@ -36,3 +38,21 @@ pub(crate) trait NetworkDriver: Driver {
3638
/// Handle interrupt and check if a packet is available
3739
fn handle_interrupt(&mut self);
3840
}
41+
42+
// Default IP level MTU to use.
43+
const DEFAULT_IP_MTU: u16 = 1500;
44+
45+
/// Default MTU to use.
46+
///
47+
/// This is 1500 IP MTU and a 14-byte ethernet header.
48+
const DEFAULT_MTU: u16 = DEFAULT_IP_MTU + 14;
49+
50+
/// Determines the MTU that should be used as configured by crate features
51+
/// or environment variables.
52+
pub(crate) fn mtu() -> u16 {
53+
if let Some(my_mtu) = hermit_var!("HERMIT_MTU") {
54+
u16::from_str(&my_mtu).unwrap()
55+
} else {
56+
DEFAULT_MTU
57+
}
58+
}

src/drivers/net/rtl8139.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::arch::kernel::interrupts::*;
1313
use crate::arch::pci::PciConfigRegion;
1414
use crate::drivers::Driver;
1515
use crate::drivers::error::DriverError;
16-
use crate::drivers::net::NetworkDriver;
16+
use crate::drivers::net::{NetworkDriver, mtu};
1717
use crate::drivers::pci::PciDevice;
1818
use crate::executor::device::{RxToken, TxToken};
1919
use crate::mm::device_alloc::DeviceAlloc;
@@ -565,7 +565,7 @@ pub(crate) fn init_device(
565565

566566
Ok(RTL8139Driver {
567567
iobase,
568-
mtu: 1514,
568+
mtu: mtu(),
569569
irq,
570570
mac,
571571
tx_in_use: [false; NO_TX_BUFFERS],

src/drivers/net/virtio/mmio.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,13 @@ impl VirtioNetDriver<Uninit> {
3838
let isr_stat = IsrStatus::new(registers.borrow_mut());
3939
let notif_cfg = NotifCfg::new(registers.borrow_mut());
4040

41-
let mtu = if let Some(my_mtu) = hermit_var!("HERMIT_MTU") {
42-
u16::from_str(&my_mtu).unwrap()
43-
} else {
44-
// fallback to the default MTU
45-
1514
46-
};
47-
4841
Ok(VirtioNetDriver {
4942
dev_cfg,
5043
com_cfg: ComCfg::new(registers, 1),
5144
isr_stat,
5245
notif_cfg,
5346
inner: Uninit,
5447
num_vqs: 0,
55-
mtu,
5648
irq,
5749
checksums: ChecksumCapabilities::default(),
5850
})

src/drivers/net/virtio/mod.rs

Lines changed: 63 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use volatile::access::ReadOnly;
2525
use self::constants::MAX_NUM_VQ;
2626
use self::error::VirtioNetError;
2727
use crate::config::VIRTIO_MAX_QUEUE_SIZE;
28-
use crate::drivers::net::NetworkDriver;
28+
use crate::drivers::net::{NetworkDriver, mtu};
2929
#[cfg(not(feature = "pci"))]
3030
use crate::drivers::virtio::transport::mmio::{ComCfg, IsrStatus, NotifCfg};
3131
#[cfg(feature = "pci")]
@@ -48,22 +48,53 @@ pub(crate) struct NetDevCfg {
4848
pub features: virtio::net::F,
4949
}
5050

51+
fn determine_mtu(dev_cfg: &NetDevCfg) -> u16 {
52+
// If VIRTIO_NET_F_MTU is negotiated, "the driver uses mtu as the maximum MTU value"
53+
// (VirtIO specification, 5.1.3, "Feature bits")
54+
if dev_cfg.features.contains(virtio::net::F::MTU) {
55+
dev_cfg.raw.as_ptr().mtu().read().to_ne()
56+
} else {
57+
// Otherwise, we can just use the MTU we want to use
58+
mtu()
59+
}
60+
}
61+
62+
fn determine_buf_size(dev_cfg: &NetDevCfg) -> u32 {
63+
// See Virtio specification v1.1 - 5.1.6.3.1 and 5.1.4.2
64+
65+
// Our desired minimum buffer size - we want it to be at least the MTU generally
66+
let mut min_buf_size = determine_mtu(dev_cfg).into();
67+
68+
// If VIRTIO_NET_F_MRG_RXBUF is negotiated, each buffer MUST be at least the size of the struct virtio_net_hdr.
69+
if dev_cfg.features.contains(virtio::net::F::MRG_RXBUF) {
70+
min_buf_size = u32::max(min_buf_size, size_of::<Hdr>() as u32);
71+
} else {
72+
// If [...] are negotiated, the driver SHOULD populate the receive queue(s) with buffers of at least 65562 bytes.
73+
if dev_cfg.features.contains(virtio::net::F::GUEST_TSO4)
74+
|| dev_cfg.features.contains(virtio::net::F::GUEST_TSO6)
75+
|| dev_cfg.features.contains(virtio::net::F::GUEST_UFO)
76+
{
77+
min_buf_size = u32::max(min_buf_size, 65562 - size_of::<Hdr>() as u32);
78+
} else {
79+
// Otherwise, the driver SHOULD populate the receive queue(s) with buffers of at least 1526 bytes.
80+
min_buf_size = u32::max(min_buf_size, 1526 - size_of::<Hdr>() as u32);
81+
}
82+
}
83+
84+
min_buf_size
85+
}
86+
5187
pub struct RxQueues {
5288
vqs: Vec<VirtQueue>,
53-
packet_size: u32,
89+
buf_size: u32,
5490
}
5591

5692
impl RxQueues {
5793
pub fn new(vqs: Vec<VirtQueue>, dev_cfg: &NetDevCfg) -> Self {
58-
// See Virtio specification v1.1 - 5.1.6.3.1
59-
//
60-
let packet_size = if dev_cfg.features.contains(virtio::net::F::MRG_RXBUF) {
61-
1514
62-
} else {
63-
dev_cfg.raw.as_ptr().mtu().read().to_ne().into()
64-
};
65-
66-
Self { vqs, packet_size }
94+
Self {
95+
vqs,
96+
buf_size: determine_buf_size(dev_cfg),
97+
}
6798
}
6899

69100
/// Takes care of handling packets correctly which need some processing after being received.
@@ -79,8 +110,8 @@ impl RxQueues {
79110
/// Queues are all populated according to Virtio specification v1.1. - 5.1.6.3.1
80111
fn add(&mut self, mut vq: VirtQueue) {
81112
const BUFF_PER_PACKET: u16 = 2;
82-
let num_packets: u16 = u16::from(vq.size()) / BUFF_PER_PACKET;
83-
fill_queue(&mut vq, num_packets, self.packet_size);
113+
let num_bufs: u16 = u16::from(vq.size()) / BUFF_PER_PACKET;
114+
fill_queue(&mut vq, num_bufs, self.buf_size);
84115
self.vqs.push(vq);
85116
}
86117

@@ -107,24 +138,23 @@ impl RxQueues {
107138

108139
fn buffer_token_from_hdr(
109140
hdr: Box<MaybeUninit<Hdr>, DeviceAlloc>,
110-
packet_size: u32,
141+
buf_size: u32,
111142
) -> AvailBufferToken {
112143
AvailBufferToken::new(SmallVec::new(), {
113144
SmallVec::from_buf([
114145
BufferElem::Sized(hdr),
115146
BufferElem::Vector(Vec::with_capacity_in(
116-
packet_size.try_into().unwrap(),
147+
buf_size.try_into().unwrap(),
117148
DeviceAlloc,
118149
)),
119150
])
120151
})
121152
.unwrap()
122153
}
123154

124-
fn fill_queue(vq: &mut VirtQueue, num_packets: u16, packet_size: u32) {
125-
for _ in 0..num_packets {
126-
let buff_tkn =
127-
buffer_token_from_hdr(Box::<Hdr, _>::new_uninit_in(DeviceAlloc), packet_size);
155+
fn fill_queue(vq: &mut VirtQueue, num_bufs: u16, buf_size: u32) {
156+
for _ in 0..num_bufs {
157+
let buff_tkn = buffer_token_from_hdr(Box::<Hdr, _>::new_uninit_in(DeviceAlloc), buf_size);
128158

129159
// BufferTokens are directly provided to the queue
130160
// TransferTokens are directly dispatched
@@ -142,22 +172,17 @@ pub struct TxQueues {
142172
vqs: Vec<VirtQueue>,
143173
/// Indicates, whether the Driver/Device are using multiple
144174
/// queues for communication.
145-
packet_length: u32,
175+
buf_size: u32,
146176
}
147177

148178
impl TxQueues {
149179
pub fn new(vqs: Vec<VirtQueue>, dev_cfg: &NetDevCfg) -> Self {
150-
let packet_length = if dev_cfg.features.contains(virtio::net::F::GUEST_TSO4)
151-
| dev_cfg.features.contains(virtio::net::F::GUEST_TSO6)
152-
| dev_cfg.features.contains(virtio::net::F::GUEST_UFO)
153-
{
154-
0x0001_000e
155-
} else {
156-
dev_cfg.raw.as_ptr().mtu().read().to_ne().into()
157-
};
158-
159-
Self { vqs, packet_length }
180+
Self {
181+
vqs,
182+
buf_size: determine_buf_size(dev_cfg),
183+
}
160184
}
185+
161186
#[allow(dead_code)]
162187
fn enable_notifs(&mut self) {
163188
for vq in &mut self.vqs {
@@ -189,6 +214,7 @@ impl TxQueues {
189214

190215
pub(crate) struct Uninit;
191216
pub(crate) struct Init {
217+
pub(super) mtu: u16,
192218
pub(super) ctrl_vq: Option<VirtQueue>,
193219
pub(super) recv_vqs: RxQueues,
194220
pub(super) send_vqs: TxQueues,
@@ -207,7 +233,6 @@ pub(crate) struct VirtioNetDriver<T = Init> {
207233
pub(super) inner: T,
208234

209235
pub(super) num_vqs: u16,
210-
pub(super) mtu: u16,
211236
pub(super) irq: InterruptLine,
212237
pub(super) checksums: ChecksumCapabilities,
213238
}
@@ -227,7 +252,7 @@ impl NetworkDriver for VirtioNetDriver<Init> {
227252

228253
/// Returns the current MTU of the device.
229254
fn get_mtu(&self) -> u16 {
230-
self.mtu
255+
self.inner.mtu
231256
}
232257

233258
fn get_checksums(&self) -> ChecksumCapabilities {
@@ -249,7 +274,7 @@ impl NetworkDriver for VirtioNetDriver<Init> {
249274
// what we are about to add
250275
self.inner.send_vqs.poll();
251276

252-
assert!(len < usize::try_from(self.inner.send_vqs.packet_length).unwrap());
277+
assert!(len < usize::try_from(self.inner.send_vqs.buf_size).unwrap());
253278
let mut packet = Vec::with_capacity_in(len, DeviceAlloc);
254279
let result = unsafe {
255280
let result = f(packet.spare_capacity_mut().assume_init_mut());
@@ -323,7 +348,7 @@ impl NetworkDriver for VirtioNetDriver<Init> {
323348
unsafe {
324349
transmute::<Box<Hdr, DeviceAlloc>, Box<MaybeUninit<Hdr>, DeviceAlloc>>(first_header)
325350
},
326-
self.inner.recv_vqs.packet_size,
351+
self.inner.recv_vqs.buf_size,
327352
);
328353
self.inner.recv_vqs.vqs[0]
329354
.dispatch(first_tkn, false, BufferType::Direct)
@@ -338,7 +363,7 @@ impl NetworkDriver for VirtioNetDriver<Init> {
338363
unsafe {
339364
transmute::<Box<Hdr, DeviceAlloc>, Box<MaybeUninit<Hdr>, DeviceAlloc>>(header)
340365
},
341-
self.inner.recv_vqs.packet_size,
366+
self.inner.recv_vqs.buf_size,
342367
);
343368
self.inner.recv_vqs.vqs[0]
344369
.dispatch(tkn, false, BufferType::Direct)
@@ -609,11 +634,14 @@ impl VirtioNetDriver<Uninit> {
609634
}
610635

611636
let mut inner = Init {
637+
mtu: determine_mtu(&self.dev_cfg),
612638
ctrl_vq: None,
613639
recv_vqs: RxQueues::new(Vec::new(), &self.dev_cfg),
614640
send_vqs: TxQueues::new(Vec::new(), &self.dev_cfg),
615641
};
616642

643+
debug!("Using RX buffer size of {}", inner.recv_vqs.buf_size);
644+
617645
self.dev_spec_init(&mut inner)?;
618646
info!(
619647
"Device specific initialization for Virtio network device {:x} finished",
@@ -637,18 +665,13 @@ impl VirtioNetDriver<Uninit> {
637665
}
638666
debug!("{:?}", self.checksums);
639667

640-
if self.dev_cfg.features.contains(virtio::net::F::MTU) {
641-
self.mtu = self.dev_cfg.raw.as_ptr().mtu().read().to_ne();
642-
}
643-
644668
Ok(VirtioNetDriver {
645669
dev_cfg: self.dev_cfg,
646670
com_cfg: self.com_cfg,
647671
isr_stat: self.isr_stat,
648672
notif_cfg: self.notif_cfg,
649673
inner,
650674
num_vqs: self.num_vqs,
651-
mtu: self.mtu,
652675
irq: self.irq,
653676
checksums: self.checksums,
654677
})

src/drivers/net/virtio/pci.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
//!
33
//! The module contains ...
44
5-
use core::str::FromStr;
6-
75
use pci_types::CommandRegister;
86
use smoltcp::phy::ChecksumCapabilities;
97
use volatile::VolatileRef;
@@ -50,21 +48,13 @@ impl VirtioNetDriver<Uninit> {
5048
return Err(error::VirtioNetError::NoDevCfg(device_id));
5149
};
5250

53-
let mtu = if let Some(my_mtu) = hermit_var!("HERMIT_MTU") {
54-
u16::from_str(&my_mtu).unwrap()
55-
} else {
56-
// fallback to the default MTU
57-
1514
58-
};
59-
6051
Ok(VirtioNetDriver {
6152
dev_cfg,
6253
com_cfg,
6354
isr_stat: isr_cfg,
6455
notif_cfg,
6556
inner: Uninit,
6657
num_vqs: 0,
67-
mtu,
6858
irq: device.get_irq().unwrap(),
6959
checksums: ChecksumCapabilities::default(),
7060
})

0 commit comments

Comments
 (0)