mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-03-07 17:26:14 +00:00
vm-virtio: Implement reset() for virtio-console
The virtio specification defines a device can be reset, which was not supported by this virtio-console implementation. The reason it is needed is to support unbinding this device from the guest driver, and rebind it to vfio-pci driver. Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
parent
dac7737919
commit
8225d4cd6e
@ -8,6 +8,7 @@ use std::cmp;
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::ops::DerefMut;
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
@ -57,7 +58,7 @@ struct ConsoleEpollHandler {
|
|||||||
mem: Arc<RwLock<GuestMemoryMmap>>,
|
mem: Arc<RwLock<GuestMemoryMmap>>,
|
||||||
interrupt_cb: Arc<VirtioInterrupt>,
|
interrupt_cb: Arc<VirtioInterrupt>,
|
||||||
in_buffer: Arc<Mutex<VecDeque<u8>>>,
|
in_buffer: Arc<Mutex<VecDeque<u8>>>,
|
||||||
out: Box<dyn io::Write + Send>,
|
out: Arc<Mutex<Box<dyn io::Write + Send + Sync + 'static>>>,
|
||||||
input_queue_evt: EventFd,
|
input_queue_evt: EventFd,
|
||||||
output_queue_evt: EventFd,
|
output_queue_evt: EventFd,
|
||||||
input_evt: EventFd,
|
input_evt: EventFd,
|
||||||
@ -130,8 +131,13 @@ impl ConsoleEpollHandler {
|
|||||||
let mem = self.mem.read().unwrap();
|
let mem = self.mem.read().unwrap();
|
||||||
for avail_desc in trans_queue.iter(&mem) {
|
for avail_desc in trans_queue.iter(&mem) {
|
||||||
let len;
|
let len;
|
||||||
let _ = mem.write_to(avail_desc.addr, &mut self.out, avail_desc.len as usize);
|
let mut out = self.out.lock().unwrap();
|
||||||
let _ = self.out.flush();
|
let _ = mem.write_to(
|
||||||
|
avail_desc.addr,
|
||||||
|
&mut out.deref_mut(),
|
||||||
|
avail_desc.len as usize,
|
||||||
|
);
|
||||||
|
let _ = out.flush();
|
||||||
|
|
||||||
len = avail_desc.len;
|
len = avail_desc.len;
|
||||||
used_desc_heads[used_count] = (avail_desc.index, len);
|
used_desc_heads[used_count] = (avail_desc.index, len);
|
||||||
@ -321,13 +327,15 @@ pub struct Console {
|
|||||||
acked_features: u64,
|
acked_features: u64,
|
||||||
config: Arc<Mutex<VirtioConsoleConfig>>,
|
config: Arc<Mutex<VirtioConsoleConfig>>,
|
||||||
input: Arc<ConsoleInput>,
|
input: Arc<ConsoleInput>,
|
||||||
out: Option<Box<dyn io::Write + Send>>,
|
out: Arc<Mutex<Box<dyn io::Write + Send + Sync + 'static>>>,
|
||||||
|
queue_evts: Option<Vec<EventFd>>,
|
||||||
|
interrupt_cb: Option<Arc<VirtioInterrupt>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Console {
|
impl Console {
|
||||||
/// Create a new virtio console device that gets random data from /dev/urandom.
|
/// Create a new virtio console device that gets random data from /dev/urandom.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
out: Option<Box<dyn io::Write + Send>>,
|
out: Box<dyn io::Write + Send + Sync + 'static>,
|
||||||
cols: u16,
|
cols: u16,
|
||||||
rows: u16,
|
rows: u16,
|
||||||
) -> io::Result<(Console, Arc<ConsoleInput>)> {
|
) -> io::Result<(Console, Arc<ConsoleInput>)> {
|
||||||
@ -351,7 +359,9 @@ impl Console {
|
|||||||
acked_features: 0u64,
|
acked_features: 0u64,
|
||||||
config: console_config,
|
config: console_config,
|
||||||
input: console_input.clone(),
|
input: console_input.clone(),
|
||||||
out,
|
out: Arc::new(Mutex::new(out)),
|
||||||
|
queue_evts: None,
|
||||||
|
interrupt_cb: None,
|
||||||
},
|
},
|
||||||
console_input,
|
console_input,
|
||||||
))
|
))
|
||||||
@ -456,6 +466,21 @@ impl VirtioDevice for Console {
|
|||||||
};
|
};
|
||||||
self.kill_evt = Some(self_kill_evt);
|
self.kill_evt = Some(self_kill_evt);
|
||||||
|
|
||||||
|
// Save the interrupt EventFD as we need to return it on reset
|
||||||
|
// but clone it to pass into the thread.
|
||||||
|
self.interrupt_cb = Some(interrupt_cb.clone());
|
||||||
|
|
||||||
|
let mut tmp_queue_evts: Vec<EventFd> = Vec::new();
|
||||||
|
for queue_evt in queue_evts.iter() {
|
||||||
|
// Save the queue EventFD as we need to return it on reset
|
||||||
|
// but clone it to pass into the thread.
|
||||||
|
tmp_queue_evts.push(queue_evt.try_clone().map_err(|e| {
|
||||||
|
error!("failed to clone queue EventFd: {}", e);
|
||||||
|
ActivateError::BadActivate
|
||||||
|
})?);
|
||||||
|
}
|
||||||
|
self.queue_evts = Some(tmp_queue_evts);
|
||||||
|
|
||||||
self.input
|
self.input
|
||||||
.acked_features
|
.acked_features
|
||||||
.store(self.acked_features, Ordering::Relaxed);
|
.store(self.acked_features, Ordering::Relaxed);
|
||||||
@ -466,31 +491,41 @@ impl VirtioDevice for Console {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(out) = self.out.take() {
|
let mut handler = ConsoleEpollHandler {
|
||||||
let mut handler = ConsoleEpollHandler {
|
queues,
|
||||||
queues,
|
mem,
|
||||||
mem,
|
interrupt_cb,
|
||||||
interrupt_cb,
|
in_buffer: self.input.in_buffer.clone(),
|
||||||
in_buffer: self.input.in_buffer.clone(),
|
out: self.out.clone(),
|
||||||
out,
|
input_queue_evt: queue_evts.remove(0),
|
||||||
input_queue_evt: queue_evts.remove(0),
|
output_queue_evt: queue_evts.remove(0),
|
||||||
output_queue_evt: queue_evts.remove(0),
|
input_evt: self.input.input_evt.try_clone().unwrap(),
|
||||||
input_evt: self.input.input_evt.try_clone().unwrap(),
|
config_evt: self.input.config_evt.try_clone().unwrap(),
|
||||||
config_evt: self.input.config_evt.try_clone().unwrap(),
|
kill_evt,
|
||||||
kill_evt,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let worker_result = thread::Builder::new()
|
let worker_result = thread::Builder::new()
|
||||||
.name("virtio_console".to_string())
|
.name("virtio_console".to_string())
|
||||||
.spawn(move || handler.run());
|
.spawn(move || handler.run());
|
||||||
|
|
||||||
if let Err(e) = worker_result {
|
if let Err(e) = worker_result {
|
||||||
error!("failed to spawn virtio_console worker: {}", e);
|
error!("failed to spawn virtio_console worker: {}", e);
|
||||||
return Err(ActivateError::BadActivate);
|
return Err(ActivateError::BadActivate);
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
Err(ActivateError::BadActivate)
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Option<(Arc<VirtioInterrupt>, Vec<EventFd>)> {
|
||||||
|
if let Some(kill_evt) = self.kill_evt.take() {
|
||||||
|
// Ignore the result because there is nothing we can do about it.
|
||||||
|
let _ = kill_evt.write(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the interrupt and queue EventFDs
|
||||||
|
Some((
|
||||||
|
self.interrupt_cb.take().unwrap(),
|
||||||
|
self.queue_evts.take().unwrap(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -401,20 +401,21 @@ impl DeviceManager {
|
|||||||
|
|
||||||
let mut virtio_devices: Vec<Box<dyn vm_virtio::VirtioDevice>> = Vec::new();
|
let mut virtio_devices: Vec<Box<dyn vm_virtio::VirtioDevice>> = Vec::new();
|
||||||
|
|
||||||
let console_writer: Option<Box<dyn io::Write + Send>> = match vm_info.vm_cfg.console.mode {
|
// Create serial and virtio-console
|
||||||
ConsoleOutputMode::File => Some(Box::new(
|
let console_writer: Option<Box<dyn io::Write + Send + Sync>> =
|
||||||
File::create(vm_info.vm_cfg.console.file.as_ref().unwrap())
|
match vm_info.vm_cfg.console.mode {
|
||||||
.map_err(DeviceManagerError::ConsoleOutputFileOpen)?,
|
ConsoleOutputMode::File => Some(Box::new(
|
||||||
)),
|
File::create(vm_info.vm_cfg.console.file.as_ref().unwrap())
|
||||||
ConsoleOutputMode::Tty => Some(Box::new(stdout())),
|
.map_err(DeviceManagerError::ConsoleOutputFileOpen)?,
|
||||||
ConsoleOutputMode::Null => Some(Box::new(sink())),
|
)),
|
||||||
ConsoleOutputMode::Off => None,
|
ConsoleOutputMode::Tty => Some(Box::new(stdout())),
|
||||||
};
|
ConsoleOutputMode::Null => Some(Box::new(sink())),
|
||||||
|
ConsoleOutputMode::Off => None,
|
||||||
|
};
|
||||||
let (col, row) = get_win_size();
|
let (col, row) = get_win_size();
|
||||||
let console_input = if console_writer.is_some() {
|
let console_input = if let Some(writer) = console_writer {
|
||||||
let (virtio_console_device, console_input) =
|
let (virtio_console_device, console_input) = vm_virtio::Console::new(writer, col, row)
|
||||||
vm_virtio::Console::new(console_writer, col, row)
|
.map_err(DeviceManagerError::CreateVirtioConsole)?;
|
||||||
.map_err(DeviceManagerError::CreateVirtioConsole)?;
|
|
||||||
virtio_devices
|
virtio_devices
|
||||||
.push(Box::new(virtio_console_device) as Box<dyn vm_virtio::VirtioDevice>);
|
.push(Box::new(virtio_console_device) as Box<dyn vm_virtio::VirtioDevice>);
|
||||||
Some(console_input)
|
Some(console_input)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user