use alloc::{collections::BTreeMap, vec::Vec};
use ax_kspin::SpinNoIrq as Mutex;
use super::queue::VcpuInterruptQueue;
use crate::{AxTaskRef, AxVmResult, ax_err_type, irq::model::PendingVcpuInterrupt};
#[allow(dead_code)]
pub struct VcpuIrqDispatcher {
queue: VcpuInterruptQueue,
vcpu_tasks: Mutex<BTreeMap<usize, AxTaskRef>>,
}
impl VcpuIrqDispatcher {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
queue: VcpuInterruptQueue::new(),
vcpu_tasks: Mutex::new(BTreeMap::new()),
}
}
#[allow(dead_code)]
pub fn register_vcpu_task(&self, vcpu_id: usize, task: AxTaskRef) {
self.vcpu_tasks.lock().insert(vcpu_id, task);
}
#[allow(dead_code)]
pub fn enqueue(&self, vcpu_id: usize, interrupt: PendingVcpuInterrupt) -> AxVmResult<usize> {
let cpu_id = {
let tasks = self.vcpu_tasks.lock();
tasks
.get(&vcpu_id)
.map(|t| t.cpu_id() as usize)
.ok_or_else(|| {
ax_err_type!(NotFound, format_args!("vCPU {vcpu_id} task not found"))
})?
};
self.queue.push(vcpu_id, interrupt);
Ok(cpu_id)
}
#[allow(dead_code)]
pub fn drain(&self, vcpu_id: usize) -> Vec<PendingVcpuInterrupt> {
self.queue.drain(vcpu_id)
}
}
#[cfg(all(test, feature = "host-test"))]
mod tests {
use super::*;
use crate::irq::model::VirtualInterruptId;
#[test]
fn enqueue_unregistered_vcpu_returns_error() {
let d = VcpuIrqDispatcher::new();
let result = d.enqueue(
0,
PendingVcpuInterrupt {
id: VirtualInterruptId(1),
trigger: crate::InterruptTriggerMode::EdgeTriggered,
},
);
assert!(result.is_err());
}
}