Skip to content

Commit

Permalink
Use Linux's irqfd support to raise interrupt to the guest
Browse files Browse the repository at this point in the history
The Linux kernel can now raise the interrupt to the guest domain, on
sensing an event on the eventfd. Use that instead of raising the
interrupt from here.

This saves us an unnecessary context switch.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
  • Loading branch information
vireshk committed Jul 20, 2023
1 parent 355bd13 commit d79c419
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 138 deletions.
3 changes: 0 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Expand Up @@ -23,6 +23,6 @@ vhost-user-frontend = { git = "https://github.com/vireshk/vhost", branch = "fron
virtio-queue = "0.9"
vm-memory = { version = "0.12", features = ["backend-mmap", "backend-atomic", "backend-bitmap", "xen"] }
vmm-sys-util = "0.11"
xen-ioctls = { git = "https://github.com/mathieupoirier/xen-sys" }
xen-store = { git = "https://github.com/mathieupoirier/xen-sys" }
xen-bindings = { git = "https://github.com/mathieupoirier/xen-sys" }
xen-ioctls = { path = "/home/vireshk/work/repos/virtio/rust/reference/xen-sys/crates/xen-ioctls" }
xen-store = { path = "/home/vireshk/work/repos/virtio/rust/reference/xen-sys/crates/xen-store" }
xen-bindings = { path = "/home/vireshk/work/repos/virtio/rust/reference/xen-sys/crates/xen-bindings" }
55 changes: 7 additions & 48 deletions src/device.rs
Expand Up @@ -11,12 +11,9 @@ use std::{
};

use lazy_static::lazy_static;
use vhost_user_frontend::{Generic, VhostUserConfig, VirtioDeviceType};
use vhost_user_frontend::{Generic, VhostUserConfig, VirtioDevice, VirtioDeviceType};
use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK};
use xen_bindings::bindings::{
ioreq, xenbus_state_XenbusStateInitialising, xenbus_state_XenbusStateUnknown,
xs_watch_type_XS_WATCH_TOKEN,
};
use xen_bindings::bindings::ioreq;

use super::{
guest::XenGuest, interrupt::XenInterrupt, mmio::XenMmio, supported_devices::SUPPORTED_DEVICES,
Expand Down Expand Up @@ -75,8 +72,6 @@ pub struct XenDevice {
pub mmio: Mutex<XenMmio>,
pub xsh: XsHandle,
pub dev_id: u32,
be: String,
fe: String,
pub addr: u64,
pub irq: u8,
pub guest: Arc<XenGuest>,
Expand All @@ -86,7 +81,7 @@ pub struct XenDevice {
impl XenDevice {
pub fn new(dev_id: u32, guest: Arc<XenGuest>) -> Result<Arc<Self>> {
let mut xsh = XsHandle::new()?;
let (be, fe) = xsh.connect_dom(dev_id, guest.fe_domid)?;
let be = xsh.connect_dom(dev_id, guest.fe_domid)?;

let dev_dir = format!("{}/{}/{}", BACKEND_PATH, guest.fe_domid, dev_id);
let compatible = xsh.read_str(&dev_dir, "type")?;
Expand Down Expand Up @@ -127,18 +122,13 @@ impl XenDevice {
mmio: Mutex::new(mmio),
xsh,
dev_id,
be,
fe,
addr,
irq,
guest,
interrupt: Mutex::new(None),
});

let interrupt = XenInterrupt::new(dev.clone());
interrupt.clone().setup()?;
*dev.interrupt.lock().unwrap() = Some(interrupt);

*dev.interrupt.lock().unwrap() = Some(XenInterrupt::new(dev.clone()));
Ok(dev)
}

Expand All @@ -164,40 +154,6 @@ impl XenDevice {
.ummap_io_range_from_ioreq_server(self.addr, VIRTIO_MMIO_IO_SIZE)
}

fn be_state_change(&self) -> Result<()> {
let state = self.xsh.read_int(&self.be, "state")?;

if state == xenbus_state_XenbusStateUnknown {
Err(Error::XBInvalidState)
} else {
Ok(())
}
}

fn fe_state_change(&self) -> Result<()> {
let state = self.xsh.read_int(&self.fe, "state")?;

if state == xenbus_state_XenbusStateInitialising {
Ok(())
} else if state == xenbus_state_XenbusStateUnknown {
Err(Error::XBInvalidState)
} else {
panic!();
}
}

pub fn xs_event(&self) -> Result<()> {
let name = self.xsh.read_watch(xs_watch_type_XS_WATCH_TOKEN)?;

if self.be.eq(&name) {
self.be_state_change()
} else if self.fe.eq(&name) {
self.fe_state_change()
} else {
Ok(())
}
}

pub fn io_event(&self, ioreq: &mut ioreq) -> Result<()> {
self.mmio.lock().unwrap().io_event(ioreq, self)
}
Expand All @@ -207,6 +163,9 @@ impl XenDevice {
interrupt.exit();
}

self.gdev.lock().unwrap().reset();
self.gdev.lock().unwrap().shutdown();

self.destroy_ioreq().ok();
}
}
2 changes: 1 addition & 1 deletion src/frontend.rs
Expand Up @@ -45,7 +45,7 @@ impl FrontendGuests {

fn remove_device(&mut self, fe_domid: u16, dev_id: u32) {
let guest = self.find_guest(fe_domid).unwrap();
guest.remove_device(dev_id).exit();
guest.remove_device(dev_id);

if guest.is_empty() {
self.remove_guest(fe_domid);
Expand Down
4 changes: 2 additions & 2 deletions src/guest.rs
Expand Up @@ -98,11 +98,11 @@ impl XenGuest {
Ok(dev)
}

pub fn remove_device(&self, dev_id: u32) -> Arc<XenDevice> {
pub fn remove_device(&self, dev_id: u32) {
let dev = self.devices.lock().unwrap().remove(dev_id);

println!("Removed device {} / {}", self.fe_domid, dev_id);
dev
dev.exit();
}

fn io_event(&self) -> Result<()> {
Expand Down
87 changes: 23 additions & 64 deletions src/interrupt.rs
Expand Up @@ -5,96 +5,55 @@

use std::{
io::Result as IoResult,
os::unix::io::AsRawFd,
sync::{Arc, Mutex},
thread::{Builder, JoinHandle},
sync::Arc,
};

use vhost_user_frontend::{VirtioDevice, VirtioInterrupt, VirtioInterruptType};
use virtio_bindings::virtio_mmio::VIRTIO_MMIO_INT_VRING;
use vhost_user_frontend::{VirtioInterrupt, VirtioInterruptType};
use vmm_sys_util::eventfd::EventFd;

use super::{device::XenDevice, epoll::XenEpoll, Result};
use super::device::XenDevice;

pub struct XenInterrupt {
dev: Arc<XenDevice>,
// Single EventFd is enough for any number of queues as there is a single underlying interrupt
// to guest anyway.
call: EventFd,
handle: Mutex<Option<JoinHandle<()>>>,
}

impl XenInterrupt {
pub fn new(dev: Arc<XenDevice>) -> Arc<Self> {
Arc::new(XenInterrupt {
dev,
call: EventFd::new(0).unwrap(),
handle: Mutex::new(None),
})
}

fn as_raw_fd(&self) -> i32 {
self.call.as_raw_fd()
}
let call = EventFd::new(0).unwrap();

fn clear_event(&self) {
self.call.read().unwrap();
}

pub fn setup(self: Arc<Self>) -> Result<()> {
let xfd = self.dev.xsh.fileno()?;
let ifd = self.as_raw_fd();
let epoll = XenEpoll::new(vec![xfd, ifd])?;
let dev = self.dev.clone();
let interrupt = self.clone();
let xen_int = Arc::new(XenInterrupt {
dev,
call: call.try_clone().unwrap(),
});

*self.handle.lock().unwrap() = Some(
Builder::new()
.name(format!("interrupt {}", dev.dev_id))
.spawn(move || {
while let Ok(fd) = epoll.wait() {
if fd == ifd {
interrupt.trigger(VirtioInterruptType::Queue(0)).unwrap();
} else if dev.xs_event().is_err() {
dev.gdev.lock().unwrap().reset();
dev.gdev.lock().unwrap().shutdown();
break;
}
}
})
.unwrap(),
);
xen_int
.dev
.guest
.xdm
.lock()
.unwrap()
.set_irqfd(call, xen_int.dev.irq as u32, true)
.unwrap();

Ok(())
xen_int
}

pub fn exit(&self) {
if let Some(handle) = self.handle.lock().unwrap().take() {
handle.join().unwrap();
}
}
}

impl VirtioInterrupt for XenInterrupt {
fn trigger(&self, _int_type: VirtioInterruptType) -> IoResult<()> {
// Clear the eventfd from backend
self.clear_event();

// Update interrupt state
self.dev
.mmio
.lock()
.unwrap()
.update_interrupt_state(VIRTIO_MMIO_INT_VRING);

// Raise interrupt to the guest
self.dev
.guest
.xdm
.lock()
.unwrap()
.set_irq(self.dev.irq as u32)
.set_irqfd(self.call.try_clone().unwrap(), self.dev.irq as u32, false)
.unwrap();
}
}

impl VirtioInterrupt for XenInterrupt {
fn trigger(&self, _int_type: VirtioInterruptType) -> IoResult<()> {
Ok(())
}

Expand Down
18 changes: 7 additions & 11 deletions src/mmio.rs
Expand Up @@ -12,12 +12,12 @@ use virtio_bindings::virtio_config::{VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1
use virtio_bindings::virtio_mmio::{
VIRTIO_MMIO_CONFIG_GENERATION, VIRTIO_MMIO_DEVICE_FEATURES, VIRTIO_MMIO_DEVICE_FEATURES_SEL,
VIRTIO_MMIO_DEVICE_ID, VIRTIO_MMIO_DRIVER_FEATURES, VIRTIO_MMIO_DRIVER_FEATURES_SEL,
VIRTIO_MMIO_INTERRUPT_ACK, VIRTIO_MMIO_INTERRUPT_STATUS, VIRTIO_MMIO_MAGIC_VALUE,
VIRTIO_MMIO_QUEUE_AVAIL_HIGH, VIRTIO_MMIO_QUEUE_AVAIL_LOW, VIRTIO_MMIO_QUEUE_DESC_HIGH,
VIRTIO_MMIO_QUEUE_DESC_LOW, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM,
VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_READY, VIRTIO_MMIO_QUEUE_SEL,
VIRTIO_MMIO_QUEUE_USED_HIGH, VIRTIO_MMIO_QUEUE_USED_LOW, VIRTIO_MMIO_STATUS,
VIRTIO_MMIO_VENDOR_ID, VIRTIO_MMIO_VERSION,
VIRTIO_MMIO_INTERRUPT_ACK, VIRTIO_MMIO_INTERRUPT_STATUS, VIRTIO_MMIO_INT_VRING,
VIRTIO_MMIO_MAGIC_VALUE, VIRTIO_MMIO_QUEUE_AVAIL_HIGH, VIRTIO_MMIO_QUEUE_AVAIL_LOW,
VIRTIO_MMIO_QUEUE_DESC_HIGH, VIRTIO_MMIO_QUEUE_DESC_LOW, VIRTIO_MMIO_QUEUE_NOTIFY,
VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_READY,
VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_QUEUE_USED_HIGH, VIRTIO_MMIO_QUEUE_USED_LOW,
VIRTIO_MMIO_STATUS, VIRTIO_MMIO_VENDOR_ID, VIRTIO_MMIO_VERSION,
};
use virtio_bindings::virtio_ring::{__virtio16, vring_avail, vring_used, vring_used_elem};
use virtio_queue::{Descriptor, Queue, QueueT};
Expand Down Expand Up @@ -142,10 +142,6 @@ impl XenMmio {
.map_err(Error::EventFdWriteFailed)
}

pub fn update_interrupt_state(&mut self, mask: u32) {
self.interrupt_state |= mask;
}

fn config_read(&self, ioreq: &mut ioreq, gdev: &Generic, offset: u64) -> Result<()> {
let mut data: u64 = 0;
gdev.read_config(offset, &mut data.as_mut_slice()[0..ioreq.size as usize]);
Expand All @@ -169,7 +165,7 @@ impl XenMmio {
VIRTIO_MMIO_DEVICE_ID => gdev.device_type(),
VIRTIO_MMIO_VENDOR_ID => self.vendor_id,
VIRTIO_MMIO_STATUS => self.status,
VIRTIO_MMIO_INTERRUPT_STATUS => self.interrupt_state,
VIRTIO_MMIO_INTERRUPT_STATUS => self.interrupt_state | VIRTIO_MMIO_INT_VRING,
VIRTIO_MMIO_QUEUE_NUM_MAX => vq.size_max,
VIRTIO_MMIO_DEVICE_FEATURES => {
if self.device_features_sel > 1 {
Expand Down
15 changes: 11 additions & 4 deletions src/xdm.rs
Expand Up @@ -3,6 +3,8 @@
//
// SPDX-License-Identifier: Apache-2.0

use vmm_sys_util::eventfd::EventFd;

use super::{Error, Result};
use xen_ioctls::{XenDeviceModelHandle, HVM_IOREQSRV_BUFIOREQ_OFF};

Expand Down Expand Up @@ -82,10 +84,15 @@ impl XenDeviceModel {
.map_err(Error::XenIoctlError)
}

pub fn set_irq(&self, irq: u32) -> Result<()> {
self.xdmh
.set_irq_level(self.domid, irq, VIRTIO_IRQ_HIGH)
.map_err(Error::XenIoctlError)
pub fn set_irqfd(&self, fd: EventFd, irq: u32, set: bool) -> Result<()> {
if set {
self.xdmh
.set_irqfd(fd, self.domid, irq, VIRTIO_IRQ_HIGH as u8)
} else {
self.xdmh
.clear_irqfd(fd, self.domid, irq, VIRTIO_IRQ_HIGH as u8)
}
.map_err(Error::XenIoctlError)
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/xs.rs
Expand Up @@ -95,7 +95,7 @@ impl XsHandle {
self.read_watch(xs_watch_type_XS_WATCH_PATH)
}

pub fn connect_dom(&mut self, dev_id: u32, fe_domid: u16) -> Result<(String, String)> {
pub fn connect_dom(&mut self, dev_id: u32, fe_domid: u16) -> Result<String> {
let be = format!("{}/{}/{}", BACKEND_PATH, fe_domid, dev_id);

let state = self.read_int(&be, "state")?;
Expand All @@ -118,7 +118,7 @@ impl XsHandle {
return Err(Error::XBInvalidState);
}

Ok((be, fe))
Ok(be)
}

pub fn wait_for_device(&mut self) -> Result<(u16, u32, bool)> {
Expand Down

0 comments on commit d79c419

Please sign in to comment.