mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-01-22 04:25:21 +00:00
vmm: Support both reset and shutdown
Add a 2nd EventFd to the VM to control resetting (rebooting) the VM this supplements the EventFd used for managing shutdown of the VM. The default behaviour on i8042 or triple-fault based reset is currently unchanged i.e. it will trigger a shutdown. In order to support restarting the VM it was necessary to make start() function take a reference to the config. Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
parent
ebe8edd423
commit
ae66a44d26
@ -34,6 +34,7 @@ impl BusDevice for I8042Device {
|
|||||||
|
|
||||||
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
|
fn write(&mut self, _base: u64, offset: u64, data: &[u8]) {
|
||||||
if data.len() == 1 && data[0] == 0xfe && offset == 3 {
|
if data.len() == 1 && data[0] == 0xfe && offset == 3 {
|
||||||
|
debug!("i8042 reset signalled");
|
||||||
if let Err(e) = self.reset_evt.write(1) {
|
if let Err(e) = self.reset_evt.write(1) {
|
||||||
error!("Error triggering i8042 reset event: {}", e);
|
error!("Error triggering i8042 reset event: {}", e);
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ pub mod config;
|
|||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
||||||
use self::config::VmConfig;
|
use self::config::VmConfig;
|
||||||
use self::vm::Vm;
|
use self::vm::{ExitBehaviour, Vm};
|
||||||
|
|
||||||
/// Errors associated with VM management
|
/// Errors associated with VM management
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -55,11 +55,15 @@ impl Vmm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn boot_kernel(config: VmConfig) -> Result<()> {
|
pub fn boot_kernel(config: VmConfig) -> Result<()> {
|
||||||
|
loop {
|
||||||
let vmm = Vmm::new()?;
|
let vmm = Vmm::new()?;
|
||||||
let mut vm = Vm::new(&vmm.kvm, config).map_err(Error::VmNew)?;
|
let mut vm = Vm::new(&vmm.kvm, &config).map_err(Error::VmNew)?;
|
||||||
|
|
||||||
let entry = vm.load_kernel().map_err(Error::LoadKernel)?;
|
let entry = vm.load_kernel().map_err(Error::LoadKernel)?;
|
||||||
vm.start(entry).map_err(Error::VmStart)?;
|
if vm.start(entry).map_err(Error::VmStart)? == ExitBehaviour::Shutdown {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -605,9 +605,12 @@ struct DeviceManager {
|
|||||||
serial: Option<Arc<Mutex<devices::legacy::Serial>>>,
|
serial: Option<Arc<Mutex<devices::legacy::Serial>>>,
|
||||||
console_input: Option<Arc<vm_virtio::ConsoleInput>>,
|
console_input: Option<Arc<vm_virtio::ConsoleInput>>,
|
||||||
|
|
||||||
// i8042 device for exit
|
// i8042 device for i8042 reset
|
||||||
i8042: Arc<Mutex<devices::legacy::I8042Device>>,
|
i8042: Arc<Mutex<devices::legacy::I8042Device>>,
|
||||||
|
|
||||||
|
// Shutdown (exit) and reboot (reset) control
|
||||||
exit_evt: EventFd,
|
exit_evt: EventFd,
|
||||||
|
reset_evt: EventFd,
|
||||||
|
|
||||||
// IOAPIC
|
// IOAPIC
|
||||||
ioapic: Option<Arc<Mutex<ioapic::Ioapic>>>,
|
ioapic: Option<Arc<Mutex<ioapic::Ioapic>>>,
|
||||||
@ -679,6 +682,7 @@ impl DeviceManager {
|
|||||||
|
|
||||||
// Add a shutdown device (i8042)
|
// Add a shutdown device (i8042)
|
||||||
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(DeviceManagerError::EventFd)?;
|
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(DeviceManagerError::EventFd)?;
|
||||||
|
let reset_evt = EventFd::new(EFD_NONBLOCK).map_err(DeviceManagerError::EventFd)?;
|
||||||
let i8042 = Arc::new(Mutex::new(devices::legacy::I8042Device::new(
|
let i8042 = Arc::new(Mutex::new(devices::legacy::I8042Device::new(
|
||||||
exit_evt.try_clone().map_err(DeviceManagerError::EventFd)?,
|
exit_evt.try_clone().map_err(DeviceManagerError::EventFd)?,
|
||||||
)));
|
)));
|
||||||
@ -734,6 +738,7 @@ impl DeviceManager {
|
|||||||
console_input: console,
|
console_input: console,
|
||||||
i8042,
|
i8042,
|
||||||
exit_evt,
|
exit_evt,
|
||||||
|
reset_evt,
|
||||||
ioapic,
|
ioapic,
|
||||||
pci,
|
pci,
|
||||||
})
|
})
|
||||||
@ -1298,6 +1303,7 @@ impl DeviceManager {
|
|||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
enum EpollDispatch {
|
enum EpollDispatch {
|
||||||
Exit,
|
Exit,
|
||||||
|
Reset,
|
||||||
Stdin,
|
Stdin,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1313,7 +1319,7 @@ impl EpollContext {
|
|||||||
// Initial capacity needs to be large enough to hold:
|
// Initial capacity needs to be large enough to hold:
|
||||||
// * 1 exit event
|
// * 1 exit event
|
||||||
// * 1 stdin event
|
// * 1 stdin event
|
||||||
let mut dispatch_table = Vec::with_capacity(3);
|
let mut dispatch_table = Vec::with_capacity(4);
|
||||||
dispatch_table.push(None);
|
dispatch_table.push(None);
|
||||||
|
|
||||||
Ok(EpollContext {
|
Ok(EpollContext {
|
||||||
@ -1359,6 +1365,12 @@ impl AsRawFd for EpollContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum ExitBehaviour {
|
||||||
|
Shutdown = 1,
|
||||||
|
Reset = 2,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Vm<'a> {
|
pub struct Vm<'a> {
|
||||||
fd: Arc<VmFd>,
|
fd: Arc<VmFd>,
|
||||||
kernel: File,
|
kernel: File,
|
||||||
@ -1366,14 +1378,14 @@ pub struct Vm<'a> {
|
|||||||
vcpus: Vec<thread::JoinHandle<()>>,
|
vcpus: Vec<thread::JoinHandle<()>>,
|
||||||
devices: DeviceManager,
|
devices: DeviceManager,
|
||||||
cpuid: CpuId,
|
cpuid: CpuId,
|
||||||
config: VmConfig<'a>,
|
config: &'a VmConfig<'a>,
|
||||||
epoll: EpollContext,
|
epoll: EpollContext,
|
||||||
on_tty: bool,
|
on_tty: bool,
|
||||||
creation_ts: std::time::Instant,
|
creation_ts: std::time::Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Vm<'a> {
|
impl<'a> Vm<'a> {
|
||||||
pub fn new(kvm: &Kvm, config: VmConfig<'a>) -> Result<Self> {
|
pub fn new(kvm: &Kvm, config: &'a VmConfig<'a>) -> Result<Self> {
|
||||||
let kernel = File::open(&config.kernel.path).map_err(Error::KernelFile)?;
|
let kernel = File::open(&config.kernel.path).map_err(Error::KernelFile)?;
|
||||||
let fd = kvm.create_vm().map_err(Error::VmCreate)?;
|
let fd = kvm.create_vm().map_err(Error::VmCreate)?;
|
||||||
let fd = Arc::new(fd);
|
let fd = Arc::new(fd);
|
||||||
@ -1553,7 +1565,7 @@ impl<'a> Vm<'a> {
|
|||||||
let vm_info = VmInfo {
|
let vm_info = VmInfo {
|
||||||
memory: &guest_memory,
|
memory: &guest_memory,
|
||||||
vm_fd: &fd,
|
vm_fd: &fd,
|
||||||
vm_cfg: &config,
|
vm_cfg: config,
|
||||||
};
|
};
|
||||||
|
|
||||||
let device_manager = DeviceManager::new(
|
let device_manager = DeviceManager::new(
|
||||||
@ -1577,6 +1589,9 @@ impl<'a> Vm<'a> {
|
|||||||
epoll
|
epoll
|
||||||
.add_event(&device_manager.exit_evt, EpollDispatch::Exit)
|
.add_event(&device_manager.exit_evt, EpollDispatch::Exit)
|
||||||
.map_err(Error::EpollError)?;
|
.map_err(Error::EpollError)?;
|
||||||
|
epoll
|
||||||
|
.add_event(&device_manager.reset_evt, EpollDispatch::Reset)
|
||||||
|
.map_err(Error::EpollError)?;
|
||||||
|
|
||||||
let vcpus = Vec::with_capacity(u8::from(&config.cpus) as usize);
|
let vcpus = Vec::with_capacity(u8::from(&config.cpus) as usize);
|
||||||
|
|
||||||
@ -1662,7 +1677,7 @@ impl<'a> Vm<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn control_loop(&mut self) -> Result<()> {
|
pub fn control_loop(&mut self) -> Result<ExitBehaviour> {
|
||||||
// Let's start the STDIN polling thread.
|
// Let's start the STDIN polling thread.
|
||||||
const EPOLL_EVENTS_LEN: usize = 100;
|
const EPOLL_EVENTS_LEN: usize = 100;
|
||||||
|
|
||||||
@ -1676,6 +1691,8 @@ impl<'a> Vm<'a> {
|
|||||||
.map_err(Error::SetTerminalRaw)?;
|
.map_err(Error::SetTerminalRaw)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let exit_behaviour;
|
||||||
|
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) {
|
let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
@ -1702,6 +1719,14 @@ impl<'a> Vm<'a> {
|
|||||||
EpollDispatch::Exit => {
|
EpollDispatch::Exit => {
|
||||||
// Consume the event.
|
// Consume the event.
|
||||||
self.devices.exit_evt.read().map_err(Error::EventFd)?;
|
self.devices.exit_evt.read().map_err(Error::EventFd)?;
|
||||||
|
exit_behaviour = ExitBehaviour::Shutdown;
|
||||||
|
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
EpollDispatch::Reset => {
|
||||||
|
// Consume the event.
|
||||||
|
self.devices.reset_evt.read().map_err(Error::EventFd)?;
|
||||||
|
exit_behaviour = ExitBehaviour::Reset;
|
||||||
|
|
||||||
break 'outer;
|
break 'outer;
|
||||||
}
|
}
|
||||||
@ -1749,7 +1774,7 @@ impl<'a> Vm<'a> {
|
|||||||
.map_err(Error::SetTerminalCanon)?;
|
.map_err(Error::SetTerminalCanon)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(exit_behaviour)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn os_signal_handler(signals: Signals, console_input_clone: Arc<vm_virtio::ConsoleInput>) {
|
fn os_signal_handler(signals: Signals, console_input_clone: Arc<vm_virtio::ConsoleInput>) {
|
||||||
@ -1761,7 +1786,7 @@ impl<'a> Vm<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&mut self, entry_addr: GuestAddress) -> Result<()> {
|
pub fn start(&mut self, entry_addr: GuestAddress) -> Result<ExitBehaviour> {
|
||||||
self.devices.register_devices()?;
|
self.devices.register_devices()?;
|
||||||
|
|
||||||
let vcpu_count = u8::from(&self.config.cpus);
|
let vcpu_count = u8::from(&self.config.cpus);
|
||||||
@ -1824,9 +1849,7 @@ impl<'a> Vm<'a> {
|
|||||||
Err(e) => error!("Signal not found {}", e),
|
Err(e) => error!("Signal not found {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.control_loop()?;
|
self.control_loop()
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets an Arc to the guest memory owned by this VM.
|
/// Gets an Arc to the guest memory owned by this VM.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user