mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 18:03:25 +00:00
Add virtio net device driver
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
2985cdced6
commit
7304e06c88
@ -11,6 +11,7 @@ align_ext = { path = "../../../framework/libs/align_ext" }
|
||||
pod = { git = "https://github.com/jinzhao-dev/pod", rev = "7fa2ed2" }
|
||||
jinux-input = { path = "../../comps/input" }
|
||||
jinux-block = { path = "../../comps/block" }
|
||||
jinux-network = { path = "../../comps/network" }
|
||||
jinux-time = { path = "../../comps/time" }
|
||||
jinux-rights = { path = "../jinux-rights" }
|
||||
controlled = { path = "../../libs/comp-sys/controlled" }
|
||||
|
@ -17,8 +17,8 @@ use super::{
|
||||
};
|
||||
|
||||
pub struct IfaceCommon {
|
||||
interface: Mutex<smoltcp::iface::Interface>,
|
||||
sockets: Mutex<SocketSet<'static>>,
|
||||
interface: SpinLock<smoltcp::iface::Interface>,
|
||||
sockets: SpinLock<SocketSet<'static>>,
|
||||
used_ports: RwLock<BTreeMap<u16, usize>>,
|
||||
/// The time should do next poll. We stores the total microseconds since system boots up.
|
||||
next_poll_at_ms: AtomicU64,
|
||||
@ -30,19 +30,19 @@ impl IfaceCommon {
|
||||
let socket_set = SocketSet::new(Vec::new());
|
||||
let used_ports = BTreeMap::new();
|
||||
Self {
|
||||
interface: Mutex::new(interface),
|
||||
sockets: Mutex::new(socket_set),
|
||||
interface: SpinLock::new(interface),
|
||||
sockets: SpinLock::new(socket_set),
|
||||
used_ports: RwLock::new(used_ports),
|
||||
next_poll_at_ms: AtomicU64::new(0),
|
||||
bound_sockets: RwLock::new(BTreeSet::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn interface(&self) -> MutexGuard<smoltcp::iface::Interface> {
|
||||
pub(super) fn interface(&self) -> SpinLockGuard<smoltcp::iface::Interface> {
|
||||
self.interface.lock()
|
||||
}
|
||||
|
||||
pub(super) fn sockets(&self) -> MutexGuard<smoltcp::iface::SocketSet<'static>> {
|
||||
pub(super) fn sockets(&self) -> SpinLockGuard<smoltcp::iface::SocketSet<'static>> {
|
||||
self.sockets.lock()
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,13 @@ mod common;
|
||||
mod loopback;
|
||||
mod time;
|
||||
mod util;
|
||||
mod virtio;
|
||||
|
||||
pub use any_socket::{AnyBoundSocket, AnyUnboundSocket, RawTcpSocket, RawUdpSocket};
|
||||
pub use loopback::IfaceLoopback;
|
||||
pub use smoltcp::wire::{EthernetAddress, IpAddress, IpEndpoint, IpListenEndpoint, Ipv4Address};
|
||||
pub use util::{spawn_background_poll_thread, BindPortConfig};
|
||||
pub use virtio::IfaceVirtio;
|
||||
|
||||
/// Network interface.
|
||||
///
|
||||
@ -63,11 +65,11 @@ mod internal {
|
||||
pub trait IfaceInternal {
|
||||
fn common(&self) -> &IfaceCommon;
|
||||
/// The inner socket set
|
||||
fn sockets(&self) -> MutexGuard<SocketSet<'static>> {
|
||||
fn sockets(&self) -> SpinLockGuard<SocketSet<'static>> {
|
||||
self.common().sockets()
|
||||
}
|
||||
/// The inner iface.
|
||||
fn iface_inner(&self) -> MutexGuard<smoltcp::iface::Interface> {
|
||||
fn iface_inner(&self) -> SpinLockGuard<smoltcp::iface::Interface> {
|
||||
self.common().interface()
|
||||
}
|
||||
/// The time we should do another poll.
|
||||
|
125
services/libs/jinux-std/src/net/iface/virtio.rs
Normal file
125
services/libs/jinux-std/src/net/iface/virtio.rs
Normal file
@ -0,0 +1,125 @@
|
||||
use crate::prelude::*;
|
||||
use jinux_frame::sync::SpinLock;
|
||||
use jinux_network::{probe_virtio_net, NetworkDevice, VirtioNet};
|
||||
use smoltcp::{
|
||||
iface::{Config, Routes, SocketHandle, SocketSet},
|
||||
socket::dhcpv4,
|
||||
wire::{self, IpCidr},
|
||||
};
|
||||
|
||||
use super::{common::IfaceCommon, internal::IfaceInternal, Iface};
|
||||
|
||||
pub struct IfaceVirtio {
|
||||
driver: SpinLock<VirtioNet>,
|
||||
common: IfaceCommon,
|
||||
dhcp_handle: SocketHandle,
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
||||
impl IfaceVirtio {
|
||||
pub fn new() -> Arc<Self> {
|
||||
let mut virtio_net = probe_virtio_net().unwrap();
|
||||
let interface = {
|
||||
let mac_addr = virtio_net.mac_addr();
|
||||
let ip_addr = IpCidr::new(wire::IpAddress::Ipv4(wire::Ipv4Address::UNSPECIFIED), 0);
|
||||
let routes = Routes::new();
|
||||
let config = {
|
||||
let mut config = Config::new();
|
||||
config.hardware_addr = Some(wire::HardwareAddress::Ethernet(
|
||||
wire::EthernetAddress(mac_addr.0),
|
||||
));
|
||||
config
|
||||
};
|
||||
let mut interface = smoltcp::iface::Interface::new(config, &mut virtio_net);
|
||||
interface.update_ip_addrs(|ip_addrs| {
|
||||
debug_assert!(ip_addrs.len() == 0);
|
||||
ip_addrs.push(ip_addr).unwrap();
|
||||
});
|
||||
interface
|
||||
};
|
||||
let common = IfaceCommon::new(interface);
|
||||
let mut socket_set = common.sockets();
|
||||
let dhcp_handle = init_dhcp_client(&mut socket_set);
|
||||
drop(socket_set);
|
||||
Arc::new_cyclic(|weak| Self {
|
||||
driver: SpinLock::new(virtio_net),
|
||||
common,
|
||||
dhcp_handle,
|
||||
weak_self: weak.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// FIXME: Once we have user program dhcp client, we may remove dhcp logic from kernel.
|
||||
pub fn process_dhcp(&self) {
|
||||
let mut socket_set = self.common.sockets();
|
||||
let dhcp_socket: &mut dhcpv4::Socket = socket_set.get_mut(self.dhcp_handle);
|
||||
let config = if let Some(event) = dhcp_socket.poll() {
|
||||
debug!("event = {:?}", event);
|
||||
if let dhcpv4::Event::Configured(config) = event {
|
||||
config
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let ip_addr = IpCidr::Ipv4(config.address);
|
||||
let mut interface = self.common.interface();
|
||||
interface.update_ip_addrs(|ipaddrs| {
|
||||
if let Some(addr) = ipaddrs.iter_mut().next() {
|
||||
// already has ipaddrs
|
||||
*addr = ip_addr
|
||||
} else {
|
||||
// does not has ip addr
|
||||
ipaddrs.push(ip_addr).unwrap();
|
||||
}
|
||||
});
|
||||
println!(
|
||||
"DHCP update IP address: {:?}",
|
||||
interface.ipv4_addr().unwrap()
|
||||
);
|
||||
if let Some(router) = config.router {
|
||||
println!("Default router address: {:?}", router);
|
||||
interface
|
||||
.routes_mut()
|
||||
.add_default_ipv4_route(router)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IfaceInternal for IfaceVirtio {
|
||||
fn common(&self) -> &IfaceCommon {
|
||||
&self.common
|
||||
}
|
||||
|
||||
fn arc_self(&self) -> Arc<dyn Iface> {
|
||||
self.weak_self.upgrade().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iface for IfaceVirtio {
|
||||
fn name(&self) -> &str {
|
||||
"virtio"
|
||||
}
|
||||
|
||||
fn mac_addr(&self) -> Option<smoltcp::wire::EthernetAddress> {
|
||||
let interface = self.common.interface();
|
||||
let hardware_addr = interface.hardware_addr();
|
||||
match hardware_addr {
|
||||
wire::HardwareAddress::Ethernet(ethe_address) => Some(ethe_address),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll(&self) {
|
||||
let mut driver = self.driver.lock();
|
||||
self.common.poll(&mut *driver);
|
||||
self.process_dhcp();
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a dhcp socket.
|
||||
fn init_dhcp_client(socket_set: &mut SocketSet) -> SocketHandle {
|
||||
let dhcp_socket = dhcpv4::Socket::new();
|
||||
socket_set.add(dhcp_socket)
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
use crate::{
|
||||
net::iface::{Iface, IfaceLoopback},
|
||||
net::iface::{Iface, IfaceLoopback, IfaceVirtio},
|
||||
prelude::*,
|
||||
};
|
||||
use jinux_network::register_net_device_irq_handler;
|
||||
use spin::Once;
|
||||
|
||||
use self::iface::spawn_background_poll_thread;
|
||||
@ -13,8 +14,15 @@ pub mod socket;
|
||||
|
||||
pub fn init() {
|
||||
IFACES.call_once(|| {
|
||||
let iface_virtio = IfaceVirtio::new();
|
||||
let iface_loopback = IfaceLoopback::new();
|
||||
vec![iface_loopback]
|
||||
vec![iface_virtio, iface_loopback]
|
||||
});
|
||||
register_net_device_irq_handler(|irq_num| {
|
||||
debug!("irq num = {}", irq_num);
|
||||
// TODO: further check that the irq num is the same as iface's irq num
|
||||
let iface_virtio = &IFACES.get().unwrap()[0];
|
||||
iface_virtio.poll();
|
||||
});
|
||||
poll_ifaces();
|
||||
}
|
||||
|
Reference in New Issue
Block a user