pH/src/virtio/virtqueue.rs
2017-10-16 02:36:00 +00:00

167 lines
4.1 KiB
Rust

use std::sync::atomic::{Ordering, AtomicUsize, AtomicBool};
use std::sync::Arc;
use memory::GuestRam;
use kvm::Kvm;
use vm::Result;
use super::eventfd::{EventFd,IoEventFd};
use super::consts::*;
use super::vring::{Vring,Descriptor};
use super::bus::VirtioDeviceConfig;
use super::chain::Chain;
#[derive(Clone)]
pub struct VirtQueue {
memory: GuestRam,
vring: Vring,
features: u64,
ioeventfd: Arc<IoEventFd>,
interrupt: Arc<InterruptLine>,
closed: Arc<AtomicBool>,
}
impl VirtQueue {
pub fn new(memory: GuestRam, vring: Vring, interrupt: Arc<InterruptLine>, ioeventfd: Arc<IoEventFd>) -> VirtQueue {
VirtQueue {
memory,
vring,
features: 0,
ioeventfd,
interrupt,
closed: Arc::new(AtomicBool::new(false)),
}
}
#[allow(dead_code)]
pub fn set_closed(&self) {
self.closed.store(true, Ordering::SeqCst);
self.ioeventfd.write(1).unwrap();
}
#[allow(dead_code)]
pub fn is_closed(&self) -> bool {
self.closed.load(Ordering::SeqCst)
}
fn use_event_idx(&self) -> bool {
self.features & VIRTIO_F_EVENT_IDX != 0
}
pub fn wait_ready(&self) -> Result<()> {
if self.vring.is_empty() {
let _ = self.ioeventfd.read()?;
}
Ok(())
}
pub fn wait_next_chain(&self) -> Result<Chain> {
loop {
self.wait_ready()?;
if let Some(idx) = self.pop_avail_entry() {
return Ok(Chain::new(self.memory.clone(), self.clone(), idx, self.vring.size()));
}
}
}
pub fn on_each_chain<F>(&self, mut f: F)
where F: FnMut(Chain) {
loop {
self.wait_ready().unwrap();
for chain in self.iter() {
f(chain);
}
}
}
pub fn iter(&self) -> QueueIter {
QueueIter { vq: self.clone() }
}
fn need_interrupt(&self, first_used: u16, used_count: usize) -> bool {
if used_count == 0 {
return false;
}
if self.use_event_idx() {
let event = self.vring.read_used_event();
// Minimum count needed to traverse event idx
let span = ((event - first_used) + 1) as usize;
return used_count >= span;
}
!self.vring.read_avail_no_interrupt()
}
pub fn put_used(&self, idx: u16, len: u32) {
let used = self.vring.next_used();
self.vring.put_used(idx, len);
if self.need_interrupt(used, 1) {
self.interrupt.notify_queue();
}
}
fn pop_avail_entry(&self) -> Option<u16> {
if let Some(idx) = self.vring.pop_avail_entry() {
if self.use_event_idx() {
self.vring.write_avail_event(self.vring.next_avail());
}
return Some(idx)
}
None
}
pub fn load_descriptor(&self, idx: u16) -> Option<Descriptor> {
self.vring.load_descriptor(idx)
}
}
pub struct QueueIter {
vq: VirtQueue,
}
impl Iterator for QueueIter {
type Item = Chain;
fn next(&mut self) -> Option<Self::Item> {
self.vq.pop_avail_entry().map(|idx| {
Chain::new(self.vq.memory.clone(),self.vq.clone(),idx, self.vq.vring.size())
})
}
}
pub struct InterruptLine {
irqfd: EventFd,
isr: AtomicUsize,
}
impl InterruptLine {
pub fn from_config(conf: &VirtioDeviceConfig) -> Result<Arc<InterruptLine>> {
InterruptLine::new(conf.kvm(), conf.irq())
}
fn new(kvm: &Kvm, irq: u8) -> Result<Arc<InterruptLine>> {
let irqfd = EventFd::new()?;
kvm.irqfd(irqfd.raw_fd() as u32, irq as u32)?;
Ok(Arc::new(InterruptLine{
irqfd,
isr: AtomicUsize::new(0)
}))
}
pub fn isr_read(&self) -> u64 {
self.isr.swap(0, Ordering::SeqCst) as u64
}
pub fn notify_queue(&self) {
self.isr.fetch_or(0x1, Ordering::SeqCst);
self.irqfd.write(1).unwrap();
}
pub fn notify_config(&self) {
self.isr.fetch_or(0x2, Ordering::SeqCst);
self.irqfd.write(1).unwrap();
}
}