mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
mshv: Use in-kernel irqfd mechanism
Convert MshvIrqRoutingEntry functions to methods as well. Signed-off-by: Vineeth Pillai <viremana@linux.microsoft.com>
This commit is contained in:
committed by
Rob Bradford
parent
c2acea5156
commit
5209bd6120
@@ -4,6 +4,9 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
use crate::arch::emulator::{PlatformEmulator, PlatformError};
|
use crate::arch::emulator::{PlatformEmulator, PlatformError};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::arch::x86::emulator::{Emulator, EmulatorCpuState};
|
use crate::arch::x86::emulator::{Emulator, EmulatorCpuState};
|
||||||
use crate::cpu;
|
use crate::cpu;
|
||||||
@@ -24,13 +27,11 @@ use vmm_sys_util::eventfd::EventFd;
|
|||||||
pub use x86_64::VcpuMshvState as CpuState;
|
pub use x86_64::VcpuMshvState as CpuState;
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub use x86_64::*;
|
pub use x86_64::*;
|
||||||
// Wei: for emulating irqfd and ioeventfd
|
|
||||||
|
// Wei: for emulating ioeventfd
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::io;
|
use std::sync::RwLock;
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
|
||||||
use std::sync::{Mutex, RwLock};
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
pub const PAGE_SHIFT: usize = 12;
|
pub const PAGE_SHIFT: usize = 12;
|
||||||
|
|
||||||
@@ -41,195 +42,6 @@ pub struct HvState {
|
|||||||
|
|
||||||
pub use HvState as VmState;
|
pub use HvState as VmState;
|
||||||
|
|
||||||
struct IrqfdCtrlEpollHandler {
|
|
||||||
vm_fd: Arc<VmFd>, /* For issuing hypercall */
|
|
||||||
irqfd: EventFd, /* Registered by caller */
|
|
||||||
kill: EventFd, /* Created by us, signal thread exit */
|
|
||||||
epoll_fd: RawFd, /* epoll fd */
|
|
||||||
gsi: u32,
|
|
||||||
gsi_routes: Arc<RwLock<HashMap<u32, MshvIrqRoutingEntry>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_listener(
|
|
||||||
epoll_fd: RawFd,
|
|
||||||
fd: RawFd,
|
|
||||||
ev_type: epoll::Events,
|
|
||||||
data: u64,
|
|
||||||
) -> std::result::Result<(), io::Error> {
|
|
||||||
epoll::ctl(
|
|
||||||
epoll_fd,
|
|
||||||
epoll::ControlOptions::EPOLL_CTL_ADD,
|
|
||||||
fd,
|
|
||||||
epoll::Event::new(ev_type, data),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const KILL_EVENT: u16 = 1;
|
|
||||||
const IRQFD_EVENT: u16 = 2;
|
|
||||||
|
|
||||||
impl IrqfdCtrlEpollHandler {
|
|
||||||
fn assert_virtual_interrupt(&self, e: &MshvIrqRoutingEntry) -> vm::Result<()> {
|
|
||||||
// GSI routing contains MSI information.
|
|
||||||
// We still need to translate that to APIC ID etc
|
|
||||||
|
|
||||||
debug!("Inject {:x?}", e);
|
|
||||||
|
|
||||||
let MshvIrqRouting::Msi(msi) = e.route;
|
|
||||||
|
|
||||||
/* Make an assumption here ... */
|
|
||||||
if msi.address_hi != 0 {
|
|
||||||
panic!("MSI high address part is not zero");
|
|
||||||
}
|
|
||||||
|
|
||||||
let typ = self
|
|
||||||
.get_interrupt_type(self.get_delivery_mode(msi.data))
|
|
||||||
.unwrap();
|
|
||||||
let apic_id = self.get_destination(msi.address_lo);
|
|
||||||
let vector = self.get_vector(msi.data);
|
|
||||||
let level_triggered = self.get_trigger_mode(msi.data);
|
|
||||||
let logical_destination_mode = self.get_destination_mode(msi.address_lo);
|
|
||||||
|
|
||||||
debug!(
|
|
||||||
"{:x} {:x} {:x} {} {}",
|
|
||||||
typ, apic_id, vector, level_triggered, logical_destination_mode
|
|
||||||
);
|
|
||||||
|
|
||||||
let request: InterruptRequest = InterruptRequest {
|
|
||||||
interrupt_type: typ,
|
|
||||||
apic_id,
|
|
||||||
vector: vector.into(),
|
|
||||||
level_triggered,
|
|
||||||
logical_destination_mode,
|
|
||||||
long_mode: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.vm_fd
|
|
||||||
.request_virtual_interrupt(&request)
|
|
||||||
.map_err(|e| vm::HypervisorVmError::AsserttVirtualInterrupt(e.into()))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn run_ctrl(&mut self) {
|
|
||||||
self.epoll_fd = epoll::create(true).unwrap();
|
|
||||||
let epoll_file = unsafe { File::from_raw_fd(self.epoll_fd) };
|
|
||||||
|
|
||||||
register_listener(
|
|
||||||
epoll_file.as_raw_fd(),
|
|
||||||
self.kill.as_raw_fd(),
|
|
||||||
epoll::Events::EPOLLIN,
|
|
||||||
u64::from(KILL_EVENT),
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
info!(
|
|
||||||
"IrqfdCtrlEpollHandler: failed to register listener: {:?}",
|
|
||||||
err
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
register_listener(
|
|
||||||
epoll_file.as_raw_fd(),
|
|
||||||
self.irqfd.as_raw_fd(),
|
|
||||||
epoll::Events::EPOLLIN,
|
|
||||||
u64::from(IRQFD_EVENT),
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
info!(
|
|
||||||
"IrqfdCtrlEpollHandler: failed to register listener: {:?}",
|
|
||||||
err
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut events = vec![epoll::Event::new(epoll::Events::empty(), 0); 2];
|
|
||||||
|
|
||||||
'epoll: loop {
|
|
||||||
let num_events = match epoll::wait(epoll_file.as_raw_fd(), -1, &mut events[..]) {
|
|
||||||
Ok(res) => res,
|
|
||||||
Err(e) => {
|
|
||||||
if e.kind() == std::io::ErrorKind::Interrupted {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
panic!("irqfd epoll ???");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for event in events.iter().take(num_events) {
|
|
||||||
let ev_type = event.data as u16;
|
|
||||||
|
|
||||||
match ev_type {
|
|
||||||
KILL_EVENT => {
|
|
||||||
break 'epoll;
|
|
||||||
}
|
|
||||||
IRQFD_EVENT => {
|
|
||||||
debug!("IRQFD_EVENT received, inject to guest");
|
|
||||||
let _ = self.irqfd.read().unwrap();
|
|
||||||
let gsi_routes = self.gsi_routes.read().unwrap();
|
|
||||||
|
|
||||||
if let Some(e) = gsi_routes.get(&self.gsi) {
|
|
||||||
self.assert_virtual_interrupt(&e).unwrap();
|
|
||||||
} else {
|
|
||||||
debug!("No routing info found for GSI {}", self.gsi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
error!("Unknown event");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// See Intel SDM vol3 10.11.1
|
|
||||||
/// We assume APIC ID and Hyper-V Vcpu ID are the same value
|
|
||||||
///
|
|
||||||
|
|
||||||
fn get_destination(&self, message_address: u32) -> u64 {
|
|
||||||
((message_address >> 12) & 0xff).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_destination_mode(&self, message_address: u32) -> bool {
|
|
||||||
if (message_address >> 2) & 0x1 == 0x1 {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_vector(&self, message_data: u32) -> u8 {
|
|
||||||
(message_data & 0xff) as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// True means level triggered
|
|
||||||
///
|
|
||||||
fn get_trigger_mode(&self, message_data: u32) -> bool {
|
|
||||||
if (message_data >> 15) & 0x1 == 0x1 {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_delivery_mode(&self, message_data: u32) -> u8 {
|
|
||||||
((message_data & 0x700) >> 8) as u8
|
|
||||||
}
|
|
||||||
///
|
|
||||||
/// Translate from architectural defined delivery mode to Hyper-V type
|
|
||||||
/// See Intel SDM vol3 10.11.2
|
|
||||||
///
|
|
||||||
fn get_interrupt_type(&self, delivery_mode: u8) -> Option<hv_interrupt_type> {
|
|
||||||
match delivery_mode {
|
|
||||||
0 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_FIXED),
|
|
||||||
1 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_LOWESTPRIORITY),
|
|
||||||
2 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_SMI),
|
|
||||||
4 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_NMI),
|
|
||||||
5 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_INIT),
|
|
||||||
7 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_EXTINT),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapper over mshv system ioctls.
|
/// Wrapper over mshv system ioctls.
|
||||||
pub struct MshvHypervisor {
|
pub struct MshvHypervisor {
|
||||||
mshv: Mshv,
|
mshv: Mshv,
|
||||||
@@ -289,14 +101,12 @@ impl hypervisor::Hypervisor for MshvHypervisor {
|
|||||||
}
|
}
|
||||||
let vm_fd = Arc::new(fd);
|
let vm_fd = Arc::new(fd);
|
||||||
|
|
||||||
let irqfds = Mutex::new(HashMap::new());
|
|
||||||
let ioeventfds = Arc::new(RwLock::new(HashMap::new()));
|
let ioeventfds = Arc::new(RwLock::new(HashMap::new()));
|
||||||
let gsi_routes = Arc::new(RwLock::new(HashMap::new()));
|
let gsi_routes = Arc::new(RwLock::new(HashMap::new()));
|
||||||
|
|
||||||
Ok(Arc::new(MshvVm {
|
Ok(Arc::new(MshvVm {
|
||||||
fd: vm_fd,
|
fd: vm_fd,
|
||||||
msrs,
|
msrs,
|
||||||
irqfds,
|
|
||||||
ioeventfds,
|
ioeventfds,
|
||||||
gsi_routes,
|
gsi_routes,
|
||||||
hv_state: hv_state_init(),
|
hv_state: hv_state_init(),
|
||||||
@@ -844,8 +654,6 @@ impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
|
|||||||
pub struct MshvVm {
|
pub struct MshvVm {
|
||||||
fd: Arc<VmFd>,
|
fd: Arc<VmFd>,
|
||||||
msrs: MsrEntries,
|
msrs: MsrEntries,
|
||||||
// Emulate irqfd
|
|
||||||
irqfds: Mutex<HashMap<u32, (EventFd, EventFd)>>,
|
|
||||||
// Emulate ioeventfd
|
// Emulate ioeventfd
|
||||||
ioeventfds: Arc<RwLock<HashMap<IoEventAddress, (Option<DataMatch>, EventFd)>>>,
|
ioeventfds: Arc<RwLock<HashMap<IoEventAddress, (Option<DataMatch>, EventFd)>>>,
|
||||||
// GSI routing information
|
// GSI routing information
|
||||||
@@ -888,36 +696,34 @@ impl vm::Vm for MshvVm {
|
|||||||
/// Registers an event that will, when signaled, trigger the `gsi` IRQ.
|
/// Registers an event that will, when signaled, trigger the `gsi` IRQ.
|
||||||
///
|
///
|
||||||
fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> vm::Result<()> {
|
fn register_irqfd(&self, fd: &EventFd, gsi: u32) -> vm::Result<()> {
|
||||||
let dup_fd = fd.try_clone().unwrap();
|
|
||||||
let kill_fd = EventFd::new(libc::EFD_NONBLOCK).unwrap();
|
|
||||||
|
|
||||||
let mut ctrl_handler = IrqfdCtrlEpollHandler {
|
|
||||||
vm_fd: self.fd.clone(),
|
|
||||||
kill: kill_fd.try_clone().unwrap(),
|
|
||||||
irqfd: fd.try_clone().unwrap(),
|
|
||||||
epoll_fd: 0,
|
|
||||||
gsi,
|
|
||||||
gsi_routes: self.gsi_routes.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("register_irqfd fd {} gsi {}", fd.as_raw_fd(), gsi);
|
debug!("register_irqfd fd {} gsi {}", fd.as_raw_fd(), gsi);
|
||||||
|
|
||||||
thread::Builder::new()
|
let gsi_routes = self.gsi_routes.read().unwrap();
|
||||||
.name(format!("irqfd_{}", gsi))
|
|
||||||
.spawn(move || ctrl_handler.run_ctrl())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
self.irqfds.lock().unwrap().insert(gsi, (dup_fd, kill_fd));
|
if let Some(e) = gsi_routes.get(&gsi) {
|
||||||
|
let msi = e
|
||||||
|
.get_msi_routing()
|
||||||
|
.map_err(|e| vm::HypervisorVmError::RegisterIrqFd(e.into()))?;
|
||||||
|
let request = msi.to_interrupt_request();
|
||||||
|
self.fd
|
||||||
|
.register_irqfd(&fd, gsi, &request)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::RegisterIrqFd(e.into()))?;
|
||||||
|
} else {
|
||||||
|
error!("No routing info found for GSI {}", gsi)
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
/// Unregisters an event that will, when signaled, trigger the `gsi` IRQ.
|
/// Unregisters an event that will, when signaled, trigger the `gsi` IRQ.
|
||||||
///
|
///
|
||||||
fn unregister_irqfd(&self, _fd: &EventFd, gsi: u32) -> vm::Result<()> {
|
fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> vm::Result<()> {
|
||||||
debug!("unregister_irqfd fd {} gsi {}", _fd.as_raw_fd(), gsi);
|
debug!("unregister_irqfd fd {} gsi {}", fd.as_raw_fd(), gsi);
|
||||||
let (_, kill_fd) = self.irqfds.lock().unwrap().remove(&gsi).unwrap();
|
|
||||||
kill_fd.write(1).unwrap();
|
self.fd
|
||||||
|
.unregister_irqfd(&fd, gsi)
|
||||||
|
.map_err(|e| vm::HypervisorVmError::UnregisterIrqFd(e.into()))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
@@ -1060,6 +866,12 @@ pub enum MshvIrqRouting {
|
|||||||
Msi(MshvIrqRoutingMsi),
|
Msi(MshvIrqRoutingMsi),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum MshvIrqRoutingEntryError {
|
||||||
|
#[error("Invalid MSI address: {0}")]
|
||||||
|
InvalidMsiAddress(#[source] anyhow::Error),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct MshvIrqRoutingEntry {
|
pub struct MshvIrqRoutingEntry {
|
||||||
pub gsi: u32,
|
pub gsi: u32,
|
||||||
@@ -1067,4 +879,79 @@ pub struct MshvIrqRoutingEntry {
|
|||||||
}
|
}
|
||||||
pub type IrqRoutingEntry = MshvIrqRoutingEntry;
|
pub type IrqRoutingEntry = MshvIrqRoutingEntry;
|
||||||
|
|
||||||
|
impl MshvIrqRoutingEntry {
|
||||||
|
fn get_msi_routing(&self) -> Result<MshvIrqRoutingMsi, MshvIrqRoutingEntryError> {
|
||||||
|
let MshvIrqRouting::Msi(msi) = self.route;
|
||||||
|
if msi.address_hi != 0 {
|
||||||
|
return Err(MshvIrqRoutingEntryError::InvalidMsiAddress(anyhow!(
|
||||||
|
"MSI high address part is not zero"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Ok(msi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MshvIrqRoutingMsi {
|
||||||
|
///
|
||||||
|
/// See Intel SDM vol3 10.11.1
|
||||||
|
/// We assume APIC ID and Hyper-V Vcpu ID are the same value
|
||||||
|
///
|
||||||
|
|
||||||
|
fn get_destination(&self) -> u64 {
|
||||||
|
((self.address_lo >> 12) & 0xff).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_destination_mode(&self) -> bool {
|
||||||
|
if (self.address_lo >> 2) & 0x1 == 0x1 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_vector(&self) -> u8 {
|
||||||
|
(self.data & 0xff) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// True means level triggered
|
||||||
|
///
|
||||||
|
fn get_trigger_mode(&self) -> bool {
|
||||||
|
if (self.data >> 15) & 0x1 == 0x1 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_delivery_mode(&self) -> u8 {
|
||||||
|
((self.data & 0x700) >> 8) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Translate from architectural defined delivery mode to Hyper-V type
|
||||||
|
/// See Intel SDM vol3 10.11.2
|
||||||
|
///
|
||||||
|
fn get_interrupt_type(&self) -> Option<hv_interrupt_type> {
|
||||||
|
match self.get_delivery_mode() {
|
||||||
|
0 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_FIXED),
|
||||||
|
1 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_LOWESTPRIORITY),
|
||||||
|
2 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_SMI),
|
||||||
|
4 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_NMI),
|
||||||
|
5 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_INIT),
|
||||||
|
7 => Some(hv_interrupt_type_HV_X64_INTERRUPT_TYPE_EXTINT),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_interrupt_request(&self) -> InterruptRequest {
|
||||||
|
InterruptRequest {
|
||||||
|
interrupt_type: self.get_interrupt_type().unwrap(),
|
||||||
|
apic_id: self.get_destination(),
|
||||||
|
vector: self.get_vector() as u32,
|
||||||
|
level_triggered: self.get_trigger_mode(),
|
||||||
|
logical_destination_mode: self.get_destination_mode(),
|
||||||
|
long_mode: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;
|
pub const CPUID_FLAG_VALID_INDEX: u32 = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user