-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(virtio): Adding Virtio-net Support to Cloudlet (#22)
* feat(virtio): configure net device - MMIO Device Discovery by the kernel command line - irq allocator Signed-off-by: sylvain-pierrot <[email protected]> * feat: add simple handler Signed-off-by: sylvain-pierrot <[email protected]> * refactor code architecture about devices + add queue/handlers Signed-off-by: sylvain-pierrot <[email protected]> * chore: fix traits for generic + refactor code Signed-off-by: sylvain-pierrot <[email protected]> * feat: add working device Signed-off-by: sylvain-pierrot <[email protected]> * refactor: remove all unwrap Signed-off-by: sylvain-pierrot <[email protected]> * fix: cargo clippy Signed-off-by: sylvain-pierrot <[email protected]> * refactor: remove unnecessary comments Signed-off-by: sylvain-pierrot <[email protected]> --------- Signed-off-by: sylvain-pierrot <[email protected]>
- Loading branch information
1 parent
5b3ffbb
commit b547afa
Showing
23 changed files
with
1,011 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
[workspace] | ||
members = ["src/api", "src/vmm", "src/cli", "src/fs-gen", "src/agent"] | ||
members = ["src/agent", "src/api", "src/cli", "src/fs-gen", "src/vmm"] | ||
resolver = "2" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
use std::io; | ||
|
||
pub(crate) mod serial; | ||
pub(crate) mod virtio; | ||
|
||
#[derive(Debug)] | ||
/// Devices errors. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
pub mod net; | ||
mod register; | ||
|
||
use event_manager::{ | ||
Error as EvmgrError, MutEventSubscriber, RemoteEndpoint, Result as EvmgrResult, SubscriberId, | ||
}; | ||
use kvm_ioctls::{IoEventAddress, VmFd}; | ||
use libc::EFD_NONBLOCK; | ||
use std::{ | ||
io, | ||
sync::{ | ||
atomic::{AtomicU8, Ordering}, | ||
Arc, Mutex, | ||
}, | ||
}; | ||
use virtio_device::VirtioConfig; | ||
use virtio_queue::{Queue, QueueT}; | ||
use vm_device::bus::{self, MmioRange}; | ||
use vmm_sys_util::{errno, eventfd::EventFd}; | ||
|
||
// Device-independent virtio features. | ||
mod features { | ||
pub const VIRTIO_F_RING_EVENT_IDX: u64 = 29; | ||
pub const VIRTIO_F_VERSION_1: u64 = 32; | ||
} | ||
|
||
// This bit is set on the device interrupt status when notifying the driver about used | ||
// queue events. | ||
// TODO: There seem to be similar semantics when the PCI transport is used with MSI-X cap | ||
// disabled. Let's figure out at some point if having MMIO as part of the name is necessary. | ||
const VIRTIO_MMIO_INT_VRING: u8 = 0x01; | ||
|
||
// The driver will write to the register at this offset in the MMIO region to notify the device | ||
// about available queue events. | ||
const VIRTIO_MMIO_QUEUE_NOTIFY_OFFSET: u64 = 0x50; | ||
|
||
// TODO: Make configurable for each device maybe? | ||
const QUEUE_MAX_SIZE: u16 = 256; | ||
|
||
#[derive(Debug)] | ||
#[allow(dead_code)] | ||
pub enum Error { | ||
AlreadyActivated, | ||
BadFeatures(u64), | ||
Bus(bus::Error), | ||
Cmdline(linux_loader::cmdline::Error), | ||
Endpoint(EvmgrError), | ||
EventFd(io::Error), | ||
Overflow, | ||
IoEvent, | ||
QueuesNotValid, | ||
RegisterIoevent(errno::Error), | ||
RegisterIrqfd(errno::Error), | ||
RegisterMmioDevice(bus::Error), | ||
Conversion, | ||
Mutex, | ||
Net, | ||
} | ||
|
||
pub type Result<T> = std::result::Result<T, Error>; | ||
pub type Subscriber = Arc<Mutex<dyn MutEventSubscriber + Send>>; | ||
|
||
#[derive(Copy, Clone)] | ||
pub struct MmioConfig { | ||
pub range: MmioRange, | ||
// The interrupt assigned to the device. | ||
pub gsi: u32, | ||
} | ||
|
||
pub struct Config { | ||
virtio: VirtioConfig<Queue>, | ||
pub mmio: MmioConfig, | ||
endpoint: RemoteEndpoint<Subscriber>, | ||
vm_fd: Arc<VmFd>, | ||
pub irqfd: Arc<EventFd>, | ||
} | ||
|
||
impl Config { | ||
pub fn new( | ||
virtio: VirtioConfig<Queue>, | ||
mmio: MmioConfig, | ||
endpoint: RemoteEndpoint<Subscriber>, | ||
vm_fd: Arc<VmFd>, | ||
) -> Result<Self> { | ||
let irqfd = Arc::new(EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?); | ||
|
||
// vm_fd | ||
// .register_irqfd(&irqfd, mmio.gsi) | ||
// .map_err(Error::RegisterIrqfd)?; | ||
|
||
Ok(Self { | ||
virtio, | ||
mmio, | ||
endpoint, | ||
vm_fd, | ||
irqfd, | ||
}) | ||
} | ||
|
||
// Perform common initial steps for device activation based on the configuration, and return | ||
// a `Vec` that contains `EventFd`s registered as ioeventfds, which are used to convey queue | ||
// notifications coming from the driver. | ||
pub fn prepare_activate(&self) -> Result<Vec<EventFd>> { | ||
if self.virtio.queues.iter().all(|queue| !queue.ready()) { | ||
return Err(Error::QueuesNotValid); | ||
} | ||
|
||
if self.virtio.device_activated { | ||
return Err(Error::AlreadyActivated); | ||
} | ||
|
||
// We do not support legacy drivers. | ||
if self.virtio.driver_features & (1 << features::VIRTIO_F_VERSION_1) == 0 { | ||
return Err(Error::BadFeatures(self.virtio.driver_features)); | ||
} | ||
|
||
let mut ioevents = Vec::new(); | ||
|
||
// Right now, we operate under the assumption all queues are marked ready by the device | ||
// (which is true until we start supporting devices that can optionally make use of | ||
// additional queues on top of the defaults). | ||
for i in 0..self.virtio.queues.len() { | ||
let fd = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?; | ||
|
||
// Register the queue event fd. | ||
self.vm_fd | ||
.register_ioevent( | ||
&fd, | ||
&IoEventAddress::Mmio( | ||
self.mmio.range.base().0 + VIRTIO_MMIO_QUEUE_NOTIFY_OFFSET, | ||
), | ||
// The maximum number of queues should fit within an `u16` according to the | ||
// standard, so the conversion below is always expected to succeed. | ||
u32::try_from(i).map_err(|_| Error::Conversion)?, | ||
) | ||
.map_err(Error::RegisterIoevent)?; | ||
|
||
ioevents.push(fd); | ||
} | ||
|
||
Ok(ioevents) | ||
} | ||
|
||
// Perform the final steps of device activation based on the inner configuration and the | ||
// provided subscriber that's going to handle the device queues. We'll extend this when | ||
// we start support devices that make use of multiple handlers (i.e. for multiple queues). | ||
pub fn finalize_activate(&mut self, handler: Subscriber) -> Result<()> { | ||
// Register the queue handler with the `EventManager`. We could record the `sub_id` | ||
// (and/or keep a handler clone) for further interaction (i.e. to remove the subscriber at | ||
// a later time, retrieve state, etc). | ||
let _sub_id = self | ||
.endpoint | ||
.call_blocking(move |mgr| -> EvmgrResult<SubscriberId> { | ||
Ok(mgr.add_subscriber(handler)) | ||
}) | ||
.map_err(Error::Endpoint)?; | ||
|
||
self.virtio.device_activated = true; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
/// Simple trait to model the operation of signalling the driver about used events | ||
/// for the specified queue. | ||
// TODO: Does this need renaming to be relevant for packed queues as well? | ||
pub trait SignalUsedQueue { | ||
// TODO: Should this return an error? This failing is not really recoverable at the interface | ||
// level so the expectation is the implementation handles that transparently somehow. | ||
fn signal_used_queue(&self, index: u16); | ||
} | ||
|
||
/// Uses a single irqfd as the basis of signalling any queue (useful for the MMIO transport, | ||
/// where a single interrupt is shared for everything). | ||
pub struct SingleFdSignalQueue { | ||
pub irqfd: Arc<EventFd>, | ||
pub interrupt_status: Arc<AtomicU8>, | ||
} | ||
|
||
impl SignalUsedQueue for SingleFdSignalQueue { | ||
fn signal_used_queue(&self, _index: u16) { | ||
self.interrupt_status | ||
.fetch_or(VIRTIO_MMIO_INT_VRING, Ordering::SeqCst); | ||
|
||
self.irqfd | ||
.write(1) | ||
.expect("Failed write to eventfd when signalling queue"); | ||
} | ||
} |
Oops, something went wrong.