Refactor Virtio

This commit is contained in:
Yuke Peng
2023-08-28 15:03:28 +08:00
committed by Tate, Hongliang Tian
parent df42397cea
commit 7d5e67e368
37 changed files with 1471 additions and 1413 deletions

View File

@ -8,9 +8,13 @@ edition = "2021"
[dependencies]
component = { path = "../../libs/comp-sys/component" }
jinux-frame = { path = "../../../framework/jinux-frame" }
jinux-virtio = { path = "../virtio" }
jinux-util = { path = "../../libs/jinux-util" }
jinux-rights = { path = "../../libs/jinux-rights" }
align_ext = { path = "../../../framework/libs/align_ext" }
int-to-c-enum = { path = "../../libs/int-to-c-enum" }
bytes = { version = "1.4.0", default-features = false }
pod = { git = "https://github.com/jinzhao-dev/pod", rev = "71e59ec" }
bitflags = "1.3"
spin = "0.9.4"
ringbuf = { version = "0.3.2", default-features = false, features = ["alloc"] }
log = "0.4"

View File

@ -0,0 +1,89 @@
use core::mem::size_of;
use align_ext::AlignExt;
use bytes::BytesMut;
use pod::Pod;
/// Buffer for receive packet
#[derive(Debug)]
pub struct RxBuffer {
/// Packet Buffer, length align 8.
buf: BytesMut,
/// Header len
header_len: usize,
/// Packet len
packet_len: usize,
}
impl RxBuffer {
pub fn new(len: usize, header_len: usize) -> Self {
let len = len.align_up(8);
let buf = BytesMut::zeroed(len);
Self {
buf,
packet_len: 0,
header_len,
}
}
pub const fn packet_len(&self) -> usize {
self.packet_len
}
pub fn set_packet_len(&mut self, packet_len: usize) {
self.packet_len = packet_len;
}
pub fn buf(&self) -> &[u8] {
&self.buf
}
pub fn buf_mut(&mut self) -> &mut [u8] {
&mut self.buf
}
/// Packet payload slice, which is inner buffer excluding VirtioNetHdr.
pub fn packet(&self) -> &[u8] {
debug_assert!(self.header_len + self.packet_len <= self.buf.len());
&self.buf[self.header_len..self.header_len + self.packet_len]
}
/// Mutable packet payload slice.
pub fn packet_mut(&mut self) -> &mut [u8] {
debug_assert!(self.header_len + self.packet_len <= self.buf.len());
&mut self.buf[self.header_len..self.header_len + self.packet_len]
}
pub fn header<H: Pod>(&self) -> H {
debug_assert_eq!(size_of::<H>(), self.header_len);
H::from_bytes(&self.buf[..size_of::<H>()])
}
}
/// Buffer for transmit packet
#[derive(Debug)]
pub struct TxBuffer {
buf: BytesMut,
}
impl TxBuffer {
pub fn with_len(buf_len: usize) -> Self {
Self {
buf: BytesMut::zeroed(buf_len),
}
}
pub fn new(buf: &[u8]) -> Self {
Self {
buf: BytesMut::from(buf),
}
}
pub fn buf(&self) -> &[u8] {
&self.buf
}
pub fn buf_mut(&mut self) -> &mut [u8] {
&mut self.buf
}
}

View File

@ -1,47 +1,36 @@
use alloc::vec;
use smoltcp::phy::{self, Medium};
use smoltcp::{phy, time::Instant};
use crate::VirtioNet;
use jinux_virtio::device::network::{
use crate::{
buffer::{RxBuffer, TxBuffer},
device::NetworkDevice,
NetworkDevice,
};
impl phy::Device for VirtioNet {
impl phy::Device for dyn NetworkDevice {
type RxToken<'a> = RxToken;
type TxToken<'a> = TxToken<'a>;
fn receive(
&mut self,
_timestamp: smoltcp::time::Instant,
) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
if self.can_receive() {
let device = self.device_mut();
let rx_buffer = device.receive().unwrap();
Some((RxToken(rx_buffer), TxToken(device)))
let rx_buffer = self.receive().unwrap();
Some((RxToken(rx_buffer), TxToken(self)))
} else {
None
}
}
fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
if self.can_send() {
let device = self.device_mut();
Some(TxToken(device))
Some(TxToken(self))
} else {
None
}
}
fn capabilities(&self) -> phy::DeviceCapabilities {
let mut caps = phy::DeviceCapabilities::default();
caps.max_transmission_unit = 1536;
caps.max_burst_size = Some(1);
caps.medium = Medium::Ethernet;
caps
self.capabilities()
}
}
pub struct RxToken(RxBuffer);
impl phy::RxToken for RxToken {
@ -55,7 +44,7 @@ impl phy::RxToken for RxToken {
}
}
pub struct TxToken<'a>(&'a mut NetworkDevice);
pub struct TxToken<'a>(&'a mut dyn NetworkDevice);
impl<'a> phy::TxToken for TxToken<'a> {
fn consume<R, F>(self, len: usize, f: F) -> R

View File

@ -1,59 +1,134 @@
#![no_std]
#![forbid(unsafe_code)]
#![feature(trait_alias)]
#![feature(fn_traits)]
pub mod buffer;
pub mod driver;
extern crate alloc;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use buffer::RxBuffer;
use buffer::TxBuffer;
use component::init_component;
use component::ComponentInitError;
use core::any::Any;
use core::fmt::Debug;
use jinux_frame::sync::SpinLock;
use jinux_virtio::device::network::device::EthernetAddr;
use jinux_virtio::VirtioDeviceType;
use jinux_util::safe_ptr::Pod;
use smoltcp::phy;
use spin::Once;
mod driver;
mod virtio;
#[derive(Debug, Clone, Copy, Pod)]
#[repr(C)]
pub struct EthernetAddr(pub [u8; 6]);
pub use virtio::VirtioNet;
pub trait NetworkDevice: Send + Sync + Any {
fn irq_number(&self) -> u8;
fn name(&self) -> &'static str;
fn mac_addr(&self) -> EthernetAddr;
#[derive(Debug, Clone, Copy)]
pub enum VirtioNetError {
NotReady,
WrongToken,
Unknown,
}
pub trait NetDeviceIrqHandler = Fn(u8) + Send + Sync + 'static;
pub trait NetworkDevice: Send + Sync + Any + Debug {
// ================Device Information=================
fn mac_addr(&self) -> EthernetAddr;
fn capabilities(&self) -> phy::DeviceCapabilities;
// ================Device Operation===================
fn can_receive(&self) -> bool;
fn can_send(&self) -> bool;
/// Receive a packet from network. If packet is ready, returns a RxBuffer containing the packet.
/// Otherwise, return NotReady error.
fn receive(&mut self) -> Result<RxBuffer, VirtioNetError>;
/// Send a packet to network. Return until the request completes.
fn send(&mut self, tx_buffer: TxBuffer) -> Result<(), VirtioNetError>;
}
pub trait NetDeviceIrqHandler = Fn() + Send + Sync + 'static;
pub fn register_device(name: String, device: Arc<SpinLock<Box<dyn NetworkDevice>>>) {
COMPONENT
.get()
.unwrap()
.devices
.lock()
.insert(name, (Arc::new(SpinLock::new(Vec::new())), device));
}
pub fn get_device(str: &String) -> Option<Arc<SpinLock<Box<dyn NetworkDevice>>>> {
let lock = COMPONENT.get().unwrap().devices.lock();
let Some((_, device)) = lock.get(str) else {
return None;
};
Some(device.clone())
}
pub fn register_recv_callback(name: &String, callback: impl NetDeviceIrqHandler) {
let lock = COMPONENT.get().unwrap().devices.lock();
let Some((callbacks, _)) = lock.get(name) else {
return;
};
callbacks.lock().push(Arc::new(callback));
}
pub fn handle_recv_irq(name: &String) {
let lock = COMPONENT.get().unwrap().devices.lock();
let Some((callbacks, _)) = lock.get(name) else {
return;
};
let callbacks = callbacks.clone();
let lock = callbacks.lock();
for callback in lock.iter() {
callback.call(())
}
}
pub fn all_devices() -> Vec<(String, Arc<SpinLock<Box<dyn NetworkDevice>>>)> {
let lock = COMPONENT.get().unwrap().devices.lock();
let mut vec = Vec::new();
for (name, (_, device)) in lock.iter() {
vec.push((name.clone(), device.clone()));
}
vec
}
static COMPONENT: Once<Component> = Once::new();
pub(crate) static NETWORK_IRQ_HANDLERS: Once<SpinLock<Vec<Arc<dyn NetDeviceIrqHandler>>>> =
Once::new();
#[init_component]
fn init() -> Result<(), ComponentInitError> {
let a = Component::init()?;
COMPONENT.call_once(|| a);
NETWORK_IRQ_HANDLERS.call_once(|| SpinLock::new(Vec::new()));
Ok(())
}
pub fn probe_virtio_net() -> Result<VirtioNet, ComponentInitError> {
let network_devices = {
let virtio = jinux_virtio::VIRTIO_COMPONENT.get().unwrap();
virtio.get_device(VirtioDeviceType::Network)
};
struct Component {
/// Device list, the key is device name, value is (callbacks, device);
devices: SpinLock<
BTreeMap<
String,
(
Arc<SpinLock<Vec<Arc<dyn NetDeviceIrqHandler>>>>,
Arc<SpinLock<Box<dyn NetworkDevice>>>,
),
>,
>,
}
// FIXME: deal with multiple net devices
if let Some(device) = network_devices.into_iter().next() {
let virtio_net = VirtioNet::new(device);
return Ok(virtio_net);
impl Component {
pub fn init() -> Result<Self, ComponentInitError> {
Ok(Self {
devices: SpinLock::new(BTreeMap::new()),
})
}
Err(ComponentInitError::Unknown)
}
pub fn register_net_device_irq_handler(callback: impl NetDeviceIrqHandler) {
NETWORK_IRQ_HANDLERS
.get()
.unwrap()
.lock_irq_disabled()
.push(Arc::new(callback))
}

View File

@ -1,111 +0,0 @@
use jinux_frame::io_mem::IoMem;
use jinux_frame::offset_of;
use jinux_frame::sync::SpinLock;
use jinux_frame::trap::TrapFrame;
use jinux_pci::msix::MSIX;
use jinux_util::field_ptr;
use jinux_util::safe_ptr::SafePtr;
use jinux_virtio::device::network::device::{self, EthernetAddr};
use jinux_virtio::PCIVirtioDevice;
use jinux_virtio::VirtioPciCommonCfg;
use log::debug;
use crate::{NetworkDevice, NETWORK_IRQ_HANDLERS};
pub struct VirtioNet {
/// Network Device
device: device::NetworkDevice,
/// Own common cfg to avoid other devices access this frame
_common_cfg: SafePtr<VirtioPciCommonCfg, IoMem>,
_msix: SpinLock<MSIX>,
irq_number: u8,
}
impl NetworkDevice for VirtioNet {
fn irq_number(&self) -> u8 {
self.irq_number
}
fn name(&self) -> &'static str {
"virtio net"
}
fn mac_addr(&self) -> EthernetAddr {
self.device.mac_addr()
}
}
impl VirtioNet {
pub(crate) fn new(virtio_device: PCIVirtioDevice) -> Self {
let device = if let jinux_virtio::device::VirtioDevice::Network(network_device) =
virtio_device.device
{
network_device
} else {
panic!("Invalid device type")
};
let common_cfg = virtio_device.common_cfg;
let mut msix = virtio_device.msix;
let config_msix_vector = field_ptr!(&common_cfg, VirtioPciCommonCfg, config_msix_vector)
.read()
.unwrap() as usize;
let mut network_irq_num = 0;
for i in 0..msix.table_size as usize {
let msix_entry = msix.table.get_mut(i).unwrap();
if !msix_entry.irq_handle.is_empty() {
panic!("msix already have irq functions");
}
if config_msix_vector == i {
debug!(
"network config space change irq number = {}",
msix_entry.irq_handle.num()
);
msix_entry.irq_handle.on_active(config_space_change);
} else {
network_irq_num = msix_entry.irq_handle.num();
msix_entry.irq_handle.on_active(handle_network_event);
}
}
debug_assert!(network_irq_num != 0);
debug!("Network device irq num = {}", network_irq_num);
let device = VirtioNet {
device,
_common_cfg: common_cfg,
irq_number: network_irq_num,
_msix: SpinLock::new(msix),
};
device
}
pub(crate) fn can_receive(&self) -> bool {
self.device.can_receive()
}
pub(crate) fn can_send(&self) -> bool {
self.device.can_send()
}
pub(crate) fn device_mut(&mut self) -> &mut device::NetworkDevice {
&mut self.device
}
}
/// Interrupt handler if network device config space changes
fn config_space_change(_: &TrapFrame) {
debug!("network device config space change");
}
/// Interrupt handler if network device receives some packet
fn handle_network_event(trap_frame: &TrapFrame) {
let irq_num = trap_frame.trap_num as u8;
for callback in NETWORK_IRQ_HANDLERS
.get()
.unwrap()
.lock_irq_disabled()
.iter()
{
callback(irq_num);
}
}