2019-07-22 18:50:56 +00:00
|
|
|
// Copyright 2019 Intel Corporation. All Rights Reserved.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2019-12-31 10:49:11 +00:00
|
|
|
use super::Error as DeviceError;
|
|
|
|
use super::{
|
2021-10-21 10:41:16 +00:00
|
|
|
ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, VirtioCommon, VirtioDevice,
|
|
|
|
VirtioDeviceType, VirtioInterruptType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_IOMMU_PLATFORM,
|
|
|
|
VIRTIO_F_VERSION_1,
|
2019-12-31 10:49:11 +00:00
|
|
|
};
|
2021-09-03 10:43:30 +00:00
|
|
|
use crate::seccomp_filters::Thread;
|
|
|
|
use crate::thread_helper::spawn_virtio_thread;
|
2021-06-02 19:08:04 +00:00
|
|
|
use crate::GuestMemoryMmap;
|
2019-12-31 10:49:11 +00:00
|
|
|
use crate::VirtioInterrupt;
|
2022-08-12 01:30:13 +00:00
|
|
|
use anyhow::anyhow;
|
2021-09-10 11:27:08 +00:00
|
|
|
use libc::{EFD_NONBLOCK, TIOCGWINSZ};
|
2021-09-03 10:43:30 +00:00
|
|
|
use seccompiler::SeccompAction;
|
2022-08-25 08:38:42 +00:00
|
|
|
use serial_buffer::SerialBuffer;
|
2019-07-22 18:50:56 +00:00
|
|
|
use std::cmp;
|
|
|
|
use std::collections::VecDeque;
|
2021-09-02 15:56:37 +00:00
|
|
|
use std::fs::File;
|
2019-07-22 18:50:56 +00:00
|
|
|
use std::io;
|
2021-09-02 15:56:37 +00:00
|
|
|
use std::io::{Read, Write};
|
2020-08-11 15:38:13 +00:00
|
|
|
use std::os::unix::io::AsRawFd;
|
2019-07-22 18:50:56 +00:00
|
|
|
use std::result;
|
2019-11-19 00:42:31 +00:00
|
|
|
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
2020-08-11 14:05:06 +00:00
|
|
|
use std::sync::{Arc, Barrier, Mutex};
|
2021-05-06 13:34:31 +00:00
|
|
|
use versionize::{VersionMap, Versionize, VersionizeResult};
|
|
|
|
use versionize_derive::Versionize;
|
2022-07-06 14:08:08 +00:00
|
|
|
use virtio_queue::{Queue, QueueOwnedT, QueueT};
|
|
|
|
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic};
|
2021-05-06 13:34:31 +00:00
|
|
|
use vm_migration::VersionMapped;
|
2021-04-08 09:20:10 +00:00
|
|
|
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
2022-01-26 22:44:31 +00:00
|
|
|
use vm_virtio::{AccessPlatform, Translatable};
|
2019-08-02 14:23:52 +00:00
|
|
|
use vmm_sys_util::eventfd::EventFd;
|
2019-07-22 18:50:56 +00:00
|
|
|
|
|
|
|
const QUEUE_SIZE: u16 = 256;
|
|
|
|
const NUM_QUEUES: usize = 2;
|
|
|
|
const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
|
|
|
|
|
|
|
|
// New descriptors are pending on the virtio queue.
|
2020-08-11 15:38:13 +00:00
|
|
|
const INPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
|
|
|
|
const OUTPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
|
2019-07-22 18:50:56 +00:00
|
|
|
// Some input from the VMM is ready to be injected into the VM.
|
2020-08-11 15:38:13 +00:00
|
|
|
const INPUT_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
// Console configuration change event is triggered.
|
2020-08-11 15:38:13 +00:00
|
|
|
const CONFIG_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 4;
|
2021-09-02 15:56:37 +00:00
|
|
|
// File written to (input ready)
|
|
|
|
const FILE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 5;
|
vmm: notify virtio-console of pty resizes
When a pty is resized (using the TIOCSWINSZ ioctl -- see ioctl_tty(2)),
the kernel will send a SIGWINCH signal to the pty's foreground process
group to notify it of the resize. This is the only way to be notified
by the kernel of a pty resize.
We can't just make the cloud-hypervisor process's process group the
foreground process group though, because a process can only set the
foreground process group of its controlling terminal, and
cloud-hypervisor's controlling terminal will often be the terminal the
user is running it in. To work around this, we fork a subprocess in a
new process group, and set its process group to be the foreground
process group of the pty. The subprocess additionally must be running
in a new session so that it can have a different controlling
terminal. This subprocess writes a byte to a pipe every time the pty
is resized, and the virtio-console device can listen for this in its
epoll loop.
Alternatives I considered were to have the subprocess just send
SIGWINCH to its parent, and to use an eventfd instead of a pipe.
I decided against the signal approach because re-purposing a signal
that has a very specific meaning (even if this use was only slightly
different to its normal meaning) felt unclean, and because it would
have required using pidfds to avoid race conditions if
cloud-hypervisor had terminated, which added complexity. I decided
against using an eventfd because using a pipe instead allows the child
to be notified (via poll(2)) when nothing is reading from the pipe any
more, meaning it can be reliably notified of parent death and
terminate itself immediately.
I used clone3(2) instead of fork(2) because without
CLONE_CLEAR_SIGHAND the subprocess would inherit signal-hook's signal
handlers, and there's no other straightforward way to restore all signal
handlers to their defaults in the child process. The only way to do
it would be to iterate through all possible signals, or maintain a
global list of monitored signals ourselves (vmm:vm::HANDLED_SIGNALS is
insufficient because it doesn't take into account e.g. the SIGSYS
signal handler that catches seccomp violations).
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2021-09-10 11:12:17 +00:00
|
|
|
// Console resized
|
|
|
|
const RESIZE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 6;
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
|
|
|
|
//Console size feature bit
|
|
|
|
const VIRTIO_CONSOLE_F_SIZE: u64 = 0;
|
|
|
|
|
2021-09-10 11:27:08 +00:00
|
|
|
#[derive(Copy, Clone, Debug, Versionize)]
|
2020-04-09 08:41:52 +00:00
|
|
|
#[repr(C, packed)]
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
pub struct VirtioConsoleConfig {
|
|
|
|
cols: u16,
|
|
|
|
rows: u16,
|
|
|
|
max_nr_ports: u32,
|
|
|
|
emerg_wr: u32,
|
|
|
|
}
|
|
|
|
|
2021-09-10 11:27:08 +00:00
|
|
|
impl Default for VirtioConsoleConfig {
|
|
|
|
fn default() -> Self {
|
|
|
|
VirtioConsoleConfig {
|
|
|
|
cols: 0,
|
|
|
|
rows: 0,
|
|
|
|
max_nr_ports: 1,
|
|
|
|
emerg_wr: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-17 13:39:53 +00:00
|
|
|
// SAFETY: it only has data and has no implicit padding.
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
unsafe impl ByteValued for VirtioConsoleConfig {}
|
2019-07-22 18:50:56 +00:00
|
|
|
|
|
|
|
struct ConsoleEpollHandler {
|
2022-07-06 14:08:08 +00:00
|
|
|
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
|
|
|
queues: Vec<Queue>,
|
2020-01-13 17:52:19 +00:00
|
|
|
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
2019-07-22 18:50:56 +00:00
|
|
|
in_buffer: Arc<Mutex<VecDeque<u8>>>,
|
vmm: notify virtio-console of pty resizes
When a pty is resized (using the TIOCSWINSZ ioctl -- see ioctl_tty(2)),
the kernel will send a SIGWINCH signal to the pty's foreground process
group to notify it of the resize. This is the only way to be notified
by the kernel of a pty resize.
We can't just make the cloud-hypervisor process's process group the
foreground process group though, because a process can only set the
foreground process group of its controlling terminal, and
cloud-hypervisor's controlling terminal will often be the terminal the
user is running it in. To work around this, we fork a subprocess in a
new process group, and set its process group to be the foreground
process group of the pty. The subprocess additionally must be running
in a new session so that it can have a different controlling
terminal. This subprocess writes a byte to a pipe every time the pty
is resized, and the virtio-console device can listen for this in its
epoll loop.
Alternatives I considered were to have the subprocess just send
SIGWINCH to its parent, and to use an eventfd instead of a pipe.
I decided against the signal approach because re-purposing a signal
that has a very specific meaning (even if this use was only slightly
different to its normal meaning) felt unclean, and because it would
have required using pidfds to avoid race conditions if
cloud-hypervisor had terminated, which added complexity. I decided
against using an eventfd because using a pipe instead allows the child
to be notified (via poll(2)) when nothing is reading from the pipe any
more, meaning it can be reliably notified of parent death and
terminate itself immediately.
I used clone3(2) instead of fork(2) because without
CLONE_CLEAR_SIGHAND the subprocess would inherit signal-hook's signal
handlers, and there's no other straightforward way to restore all signal
handlers to their defaults in the child process. The only way to do
it would be to iterate through all possible signals, or maintain a
global list of monitored signals ourselves (vmm:vm::HANDLED_SIGNALS is
insufficient because it doesn't take into account e.g. the SIGSYS
signal handler that catches seccomp violations).
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2021-09-10 11:12:17 +00:00
|
|
|
resizer: Arc<ConsoleResizer>,
|
2021-09-02 15:56:37 +00:00
|
|
|
endpoint: Endpoint,
|
2019-07-22 18:50:56 +00:00
|
|
|
input_queue_evt: EventFd,
|
|
|
|
output_queue_evt: EventFd,
|
|
|
|
input_evt: EventFd,
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
config_evt: EventFd,
|
vmm: notify virtio-console of pty resizes
When a pty is resized (using the TIOCSWINSZ ioctl -- see ioctl_tty(2)),
the kernel will send a SIGWINCH signal to the pty's foreground process
group to notify it of the resize. This is the only way to be notified
by the kernel of a pty resize.
We can't just make the cloud-hypervisor process's process group the
foreground process group though, because a process can only set the
foreground process group of its controlling terminal, and
cloud-hypervisor's controlling terminal will often be the terminal the
user is running it in. To work around this, we fork a subprocess in a
new process group, and set its process group to be the foreground
process group of the pty. The subprocess additionally must be running
in a new session so that it can have a different controlling
terminal. This subprocess writes a byte to a pipe every time the pty
is resized, and the virtio-console device can listen for this in its
epoll loop.
Alternatives I considered were to have the subprocess just send
SIGWINCH to its parent, and to use an eventfd instead of a pipe.
I decided against the signal approach because re-purposing a signal
that has a very specific meaning (even if this use was only slightly
different to its normal meaning) felt unclean, and because it would
have required using pidfds to avoid race conditions if
cloud-hypervisor had terminated, which added complexity. I decided
against using an eventfd because using a pipe instead allows the child
to be notified (via poll(2)) when nothing is reading from the pipe any
more, meaning it can be reliably notified of parent death and
terminate itself immediately.
I used clone3(2) instead of fork(2) because without
CLONE_CLEAR_SIGHAND the subprocess would inherit signal-hook's signal
handlers, and there's no other straightforward way to restore all signal
handlers to their defaults in the child process. The only way to do
it would be to iterate through all possible signals, or maintain a
global list of monitored signals ourselves (vmm:vm::HANDLED_SIGNALS is
insufficient because it doesn't take into account e.g. the SIGSYS
signal handler that catches seccomp violations).
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2021-09-10 11:12:17 +00:00
|
|
|
resize_pipe: Option<File>,
|
2019-07-22 18:50:56 +00:00
|
|
|
kill_evt: EventFd,
|
2019-11-19 00:42:31 +00:00
|
|
|
pause_evt: EventFd,
|
2022-01-26 16:11:18 +00:00
|
|
|
access_platform: Option<Arc<dyn AccessPlatform>>,
|
2022-08-25 08:38:42 +00:00
|
|
|
out: Option<Box<dyn Write + Send>>,
|
|
|
|
write_out: Option<Arc<AtomicBool>>,
|
|
|
|
file_event_registered: bool,
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
2021-09-02 15:56:37 +00:00
|
|
|
pub enum Endpoint {
|
|
|
|
File(File),
|
|
|
|
FilePair(File, File),
|
2022-08-25 08:38:42 +00:00
|
|
|
PtyPair(File, File),
|
2021-09-02 15:56:37 +00:00
|
|
|
Null,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Endpoint {
|
|
|
|
fn out_file(&self) -> Option<&File> {
|
|
|
|
match self {
|
|
|
|
Self::File(f) => Some(f),
|
|
|
|
Self::FilePair(f, _) => Some(f),
|
2022-08-25 08:38:42 +00:00
|
|
|
Self::PtyPair(f, _) => Some(f),
|
2021-09-02 15:56:37 +00:00
|
|
|
Self::Null => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn in_file(&self) -> Option<&File> {
|
|
|
|
match self {
|
|
|
|
Self::File(_) => None,
|
|
|
|
Self::FilePair(_, f) => Some(f),
|
2022-08-25 08:38:42 +00:00
|
|
|
Self::PtyPair(_, f) => Some(f),
|
2021-09-02 15:56:37 +00:00
|
|
|
Self::Null => None,
|
|
|
|
}
|
|
|
|
}
|
2022-08-25 08:38:42 +00:00
|
|
|
|
|
|
|
fn is_pty(&self) -> bool {
|
|
|
|
matches!(self, Self::PtyPair(_, _))
|
|
|
|
}
|
2021-09-02 15:56:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Clone for Endpoint {
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
match self {
|
|
|
|
Self::File(f) => Self::File(f.try_clone().unwrap()),
|
|
|
|
Self::FilePair(f_out, f_in) => {
|
|
|
|
Self::FilePair(f_out.try_clone().unwrap(), f_in.try_clone().unwrap())
|
|
|
|
}
|
2022-08-25 08:38:42 +00:00
|
|
|
Self::PtyPair(f_out, f_in) => {
|
|
|
|
Self::PtyPair(f_out.try_clone().unwrap(), f_in.try_clone().unwrap())
|
|
|
|
}
|
2021-09-02 15:56:37 +00:00
|
|
|
Self::Null => Self::Null,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-22 18:50:56 +00:00
|
|
|
impl ConsoleEpollHandler {
|
2022-08-25 08:38:42 +00:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
fn new(
|
|
|
|
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
|
|
|
queues: Vec<Queue>,
|
|
|
|
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
|
|
|
in_buffer: Arc<Mutex<VecDeque<u8>>>,
|
|
|
|
resizer: Arc<ConsoleResizer>,
|
|
|
|
endpoint: Endpoint,
|
|
|
|
input_queue_evt: EventFd,
|
|
|
|
output_queue_evt: EventFd,
|
|
|
|
input_evt: EventFd,
|
|
|
|
config_evt: EventFd,
|
|
|
|
resize_pipe: Option<File>,
|
|
|
|
kill_evt: EventFd,
|
|
|
|
pause_evt: EventFd,
|
|
|
|
access_platform: Option<Arc<dyn AccessPlatform>>,
|
|
|
|
) -> Self {
|
|
|
|
let out_file = endpoint.out_file();
|
|
|
|
let (out, write_out) = if let Some(out_file) = out_file {
|
|
|
|
let writer = out_file.try_clone().unwrap();
|
|
|
|
if endpoint.is_pty() {
|
|
|
|
let pty_write_out = Arc::new(AtomicBool::new(false));
|
|
|
|
let write_out = Some(pty_write_out.clone());
|
|
|
|
let buffer = SerialBuffer::new(Box::new(writer), pty_write_out);
|
|
|
|
(Some(Box::new(buffer) as Box<dyn Write + Send>), write_out)
|
|
|
|
} else {
|
|
|
|
(Some(Box::new(writer) as Box<dyn Write + Send>), None)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
(None, None)
|
|
|
|
};
|
|
|
|
|
|
|
|
ConsoleEpollHandler {
|
|
|
|
mem,
|
|
|
|
queues,
|
|
|
|
interrupt_cb,
|
|
|
|
in_buffer,
|
|
|
|
resizer,
|
|
|
|
endpoint,
|
|
|
|
input_queue_evt,
|
|
|
|
output_queue_evt,
|
|
|
|
input_evt,
|
|
|
|
config_evt,
|
|
|
|
resize_pipe,
|
|
|
|
kill_evt,
|
|
|
|
pause_evt,
|
|
|
|
access_platform,
|
|
|
|
out,
|
|
|
|
write_out,
|
|
|
|
file_event_registered: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-22 18:50:56 +00:00
|
|
|
/*
|
|
|
|
* Each port of virtio console device has one receive
|
|
|
|
* queue. One or more empty buffers are placed by the
|
2021-05-07 00:57:33 +00:00
|
|
|
* driver in the receive queue for incoming data. Here,
|
2019-07-22 18:50:56 +00:00
|
|
|
* we place the input data to these empty buffers.
|
|
|
|
*/
|
|
|
|
fn process_input_queue(&mut self) -> bool {
|
|
|
|
let mut in_buffer = self.in_buffer.lock().unwrap();
|
|
|
|
let recv_queue = &mut self.queues[0]; //receiveq
|
2022-07-25 12:26:17 +00:00
|
|
|
let mut used_descs = false;
|
2020-01-08 10:55:30 +00:00
|
|
|
|
|
|
|
if in_buffer.is_empty() {
|
|
|
|
return false;
|
|
|
|
}
|
2019-07-22 18:50:56 +00:00
|
|
|
|
2022-07-08 12:30:50 +00:00
|
|
|
while let Some(mut desc_chain) = recv_queue.pop_descriptor_chain(self.mem.memory()) {
|
2021-10-21 10:41:16 +00:00
|
|
|
let desc = desc_chain.next().unwrap();
|
|
|
|
let len = cmp::min(desc.len() as u32, in_buffer.len() as u32);
|
2020-01-08 10:55:30 +00:00
|
|
|
let source_slice = in_buffer.drain(..len as usize).collect::<Vec<u8>>();
|
2022-01-26 16:11:18 +00:00
|
|
|
|
2022-01-26 22:44:31 +00:00
|
|
|
if let Err(e) = desc_chain.memory().write_slice(
|
|
|
|
&source_slice[..],
|
2022-01-26 16:11:18 +00:00
|
|
|
desc.addr()
|
2022-04-04 11:35:24 +00:00
|
|
|
.translate_gva(self.access_platform.as_ref(), desc.len() as usize),
|
2022-01-26 22:44:31 +00:00
|
|
|
) {
|
2020-01-08 10:55:30 +00:00
|
|
|
error!("Failed to write slice: {:?}", e);
|
2022-07-08 12:30:50 +00:00
|
|
|
recv_queue.go_to_previous_position();
|
2020-01-08 10:55:30 +00:00
|
|
|
break;
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
2022-07-25 12:26:17 +00:00
|
|
|
recv_queue
|
|
|
|
.add_used(desc_chain.memory(), desc_chain.head_index(), len)
|
|
|
|
.unwrap();
|
|
|
|
used_descs = true;
|
2019-07-22 18:50:56 +00:00
|
|
|
|
2020-01-08 10:55:30 +00:00
|
|
|
if in_buffer.is_empty() {
|
2019-07-22 18:50:56 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-25 12:26:17 +00:00
|
|
|
used_descs
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Each port of virtio console device has one transmit
|
|
|
|
* queue. For outgoing data, characters are placed in
|
|
|
|
* the transmit queue by the driver. Therefore, here
|
|
|
|
* we read data from the transmit queue and flush them
|
|
|
|
* to the referenced address.
|
|
|
|
*/
|
|
|
|
fn process_output_queue(&mut self) -> bool {
|
|
|
|
let trans_queue = &mut self.queues[1]; //transmitq
|
2022-07-25 12:26:17 +00:00
|
|
|
let mut used_descs = false;
|
2019-07-22 18:50:56 +00:00
|
|
|
|
2022-07-08 12:30:50 +00:00
|
|
|
while let Some(mut desc_chain) = trans_queue.pop_descriptor_chain(self.mem.memory()) {
|
2021-10-21 10:41:16 +00:00
|
|
|
let desc = desc_chain.next().unwrap();
|
2022-08-25 08:38:42 +00:00
|
|
|
if let Some(out) = &mut self.out {
|
2022-01-26 22:44:31 +00:00
|
|
|
let _ = desc_chain.memory().write_to(
|
2022-01-26 16:11:18 +00:00
|
|
|
desc.addr()
|
2022-04-04 11:35:24 +00:00
|
|
|
.translate_gva(self.access_platform.as_ref(), desc.len() as usize),
|
2022-01-26 22:44:31 +00:00
|
|
|
out,
|
|
|
|
desc.len() as usize,
|
|
|
|
);
|
2021-09-02 15:56:37 +00:00
|
|
|
let _ = out.flush();
|
|
|
|
}
|
2022-07-25 12:26:17 +00:00
|
|
|
trans_queue
|
|
|
|
.add_used(desc_chain.memory(), desc_chain.head_index(), desc.len())
|
|
|
|
.unwrap();
|
|
|
|
used_descs = true;
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
2022-07-25 12:26:17 +00:00
|
|
|
used_descs
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
2022-01-24 14:30:42 +00:00
|
|
|
fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> {
|
2020-01-13 17:52:19 +00:00
|
|
|
self.interrupt_cb
|
2022-01-24 14:30:42 +00:00
|
|
|
.trigger(VirtioInterruptType::Queue(queue_index))
|
2020-01-13 17:52:19 +00:00
|
|
|
.map_err(|e| {
|
|
|
|
error!("Failed to signal used queue: {:?}", e);
|
|
|
|
DeviceError::FailedSignalingUsedQueue(e)
|
|
|
|
})
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
2020-08-11 14:05:06 +00:00
|
|
|
fn run(
|
|
|
|
&mut self,
|
|
|
|
paused: Arc<AtomicBool>,
|
|
|
|
paused_sync: Arc<Barrier>,
|
|
|
|
) -> result::Result<(), EpollHelperError> {
|
2020-08-11 15:38:13 +00:00
|
|
|
let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
|
|
|
|
helper.add_event(self.input_queue_evt.as_raw_fd(), INPUT_QUEUE_EVENT)?;
|
|
|
|
helper.add_event(self.output_queue_evt.as_raw_fd(), OUTPUT_QUEUE_EVENT)?;
|
|
|
|
helper.add_event(self.input_evt.as_raw_fd(), INPUT_EVENT)?;
|
|
|
|
helper.add_event(self.config_evt.as_raw_fd(), CONFIG_EVENT)?;
|
vmm: notify virtio-console of pty resizes
When a pty is resized (using the TIOCSWINSZ ioctl -- see ioctl_tty(2)),
the kernel will send a SIGWINCH signal to the pty's foreground process
group to notify it of the resize. This is the only way to be notified
by the kernel of a pty resize.
We can't just make the cloud-hypervisor process's process group the
foreground process group though, because a process can only set the
foreground process group of its controlling terminal, and
cloud-hypervisor's controlling terminal will often be the terminal the
user is running it in. To work around this, we fork a subprocess in a
new process group, and set its process group to be the foreground
process group of the pty. The subprocess additionally must be running
in a new session so that it can have a different controlling
terminal. This subprocess writes a byte to a pipe every time the pty
is resized, and the virtio-console device can listen for this in its
epoll loop.
Alternatives I considered were to have the subprocess just send
SIGWINCH to its parent, and to use an eventfd instead of a pipe.
I decided against the signal approach because re-purposing a signal
that has a very specific meaning (even if this use was only slightly
different to its normal meaning) felt unclean, and because it would
have required using pidfds to avoid race conditions if
cloud-hypervisor had terminated, which added complexity. I decided
against using an eventfd because using a pipe instead allows the child
to be notified (via poll(2)) when nothing is reading from the pipe any
more, meaning it can be reliably notified of parent death and
terminate itself immediately.
I used clone3(2) instead of fork(2) because without
CLONE_CLEAR_SIGHAND the subprocess would inherit signal-hook's signal
handlers, and there's no other straightforward way to restore all signal
handlers to their defaults in the child process. The only way to do
it would be to iterate through all possible signals, or maintain a
global list of monitored signals ourselves (vmm:vm::HANDLED_SIGNALS is
insufficient because it doesn't take into account e.g. the SIGSYS
signal handler that catches seccomp violations).
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2021-09-10 11:12:17 +00:00
|
|
|
if let Some(resize_pipe) = self.resize_pipe.as_ref() {
|
|
|
|
helper.add_event(resize_pipe.as_raw_fd(), RESIZE_EVENT)?;
|
|
|
|
}
|
2021-09-02 15:56:37 +00:00
|
|
|
if let Some(in_file) = self.endpoint.in_file() {
|
2022-08-25 08:38:42 +00:00
|
|
|
let mut events = epoll::Events::EPOLLIN;
|
|
|
|
if self.endpoint.is_pty() {
|
|
|
|
events |= epoll::Events::EPOLLONESHOT;
|
|
|
|
}
|
|
|
|
helper.add_event_custom(in_file.as_raw_fd(), FILE_EVENT, events)?;
|
|
|
|
self.file_event_registered = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// In case of PTY, we want to be able to detect a connection on the
|
|
|
|
// other end of the PTY. This is done by detecting there's no event
|
|
|
|
// triggered on the epoll, which is the reason why we want the
|
|
|
|
// epoll_wait() function to return after the timeout expired.
|
|
|
|
// In case of TTY, we don't expect to detect such behavior, which is
|
|
|
|
// why we can afford to block until an actual event is triggered.
|
|
|
|
let (timeout, enable_event_list) = if self.endpoint.is_pty() {
|
|
|
|
(500, true)
|
|
|
|
} else {
|
|
|
|
(-1, false)
|
|
|
|
};
|
|
|
|
helper.run_with_timeout(paused, paused_sync, self, timeout, enable_event_list)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function should be called when the other end of the PTY is
|
|
|
|
// connected. It verifies if this is the first time it's been invoked
|
|
|
|
// after the connection happened, and if that's the case it flushes
|
|
|
|
// all output from the console to the PTY. Otherwise, it's a no-op.
|
|
|
|
fn trigger_pty_flush(&mut self) -> result::Result<(), anyhow::Error> {
|
|
|
|
if let (Some(pty_write_out), Some(out)) = (&self.write_out, &mut self.out) {
|
|
|
|
if pty_write_out.load(Ordering::Acquire) {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
pty_write_out.store(true, Ordering::Release);
|
|
|
|
out.flush()
|
|
|
|
.map_err(|e| anyhow!("Failed to flush PTY: {:?}", e))
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn register_file_event(
|
|
|
|
&mut self,
|
|
|
|
helper: &mut EpollHelper,
|
|
|
|
) -> result::Result<(), EpollHelperError> {
|
|
|
|
if self.file_event_registered {
|
|
|
|
return Ok(());
|
2021-09-02 15:56:37 +00:00
|
|
|
}
|
2022-08-25 08:38:42 +00:00
|
|
|
|
|
|
|
// Re-arm the file event.
|
|
|
|
helper.mod_event_custom(
|
|
|
|
self.endpoint.in_file().unwrap().as_raw_fd(),
|
|
|
|
FILE_EVENT,
|
|
|
|
epoll::Events::EPOLLIN | epoll::Events::EPOLLONESHOT,
|
|
|
|
)?;
|
|
|
|
self.file_event_registered = true;
|
2020-06-22 14:00:02 +00:00
|
|
|
|
2020-08-11 15:38:13 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
|
2020-08-11 15:38:13 +00:00
|
|
|
impl EpollHelperHandler for ConsoleEpollHandler {
|
2022-08-12 01:30:13 +00:00
|
|
|
fn handle_event(
|
|
|
|
&mut self,
|
2022-08-25 08:38:42 +00:00
|
|
|
helper: &mut EpollHelper,
|
2022-08-12 01:30:13 +00:00
|
|
|
event: &epoll::Event,
|
|
|
|
) -> result::Result<(), EpollHelperError> {
|
2020-08-11 17:12:02 +00:00
|
|
|
let ev_type = event.data as u16;
|
2022-08-25 08:38:42 +00:00
|
|
|
|
2020-08-11 17:12:02 +00:00
|
|
|
match ev_type {
|
2020-08-11 15:38:13 +00:00
|
|
|
INPUT_QUEUE_EVENT => {
|
2022-08-12 01:30:13 +00:00
|
|
|
self.input_queue_evt.read().map_err(|e| {
|
|
|
|
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
|
|
|
})?;
|
|
|
|
if self.process_input_queue() {
|
|
|
|
self.signal_used_queue(0).map_err(|e| {
|
|
|
|
EpollHelperError::HandleEvent(anyhow!(
|
|
|
|
"Failed to signal used queue: {:?}",
|
|
|
|
e
|
|
|
|
))
|
|
|
|
})?;
|
2020-08-11 15:38:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
OUTPUT_QUEUE_EVENT => {
|
2022-08-12 01:30:13 +00:00
|
|
|
self.output_queue_evt.read().map_err(|e| {
|
|
|
|
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
|
|
|
})?;
|
|
|
|
if self.process_output_queue() {
|
|
|
|
self.signal_used_queue(1).map_err(|e| {
|
|
|
|
EpollHelperError::HandleEvent(anyhow!(
|
|
|
|
"Failed to signal used queue: {:?}",
|
|
|
|
e
|
|
|
|
))
|
|
|
|
})?;
|
2020-08-11 15:38:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
INPUT_EVENT => {
|
2022-08-12 01:30:13 +00:00
|
|
|
self.input_evt.read().map_err(|e| {
|
|
|
|
EpollHelperError::HandleEvent(anyhow!("Failed to get input event: {:?}", e))
|
|
|
|
})?;
|
|
|
|
if self.process_input_queue() {
|
|
|
|
self.signal_used_queue(0).map_err(|e| {
|
|
|
|
EpollHelperError::HandleEvent(anyhow!(
|
|
|
|
"Failed to signal used queue: {:?}",
|
|
|
|
e
|
|
|
|
))
|
|
|
|
})?;
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-11 15:38:13 +00:00
|
|
|
CONFIG_EVENT => {
|
2022-08-12 01:30:13 +00:00
|
|
|
self.config_evt.read().map_err(|e| {
|
|
|
|
EpollHelperError::HandleEvent(anyhow!("Failed to get config event: {:?}", e))
|
|
|
|
})?;
|
|
|
|
self.interrupt_cb
|
|
|
|
.trigger(VirtioInterruptType::Config)
|
|
|
|
.map_err(|e| {
|
|
|
|
EpollHelperError::HandleEvent(anyhow!(
|
|
|
|
"Failed to signal console driver: {:?}",
|
|
|
|
e
|
|
|
|
))
|
|
|
|
})?;
|
2020-08-11 15:38:13 +00:00
|
|
|
}
|
vmm: notify virtio-console of pty resizes
When a pty is resized (using the TIOCSWINSZ ioctl -- see ioctl_tty(2)),
the kernel will send a SIGWINCH signal to the pty's foreground process
group to notify it of the resize. This is the only way to be notified
by the kernel of a pty resize.
We can't just make the cloud-hypervisor process's process group the
foreground process group though, because a process can only set the
foreground process group of its controlling terminal, and
cloud-hypervisor's controlling terminal will often be the terminal the
user is running it in. To work around this, we fork a subprocess in a
new process group, and set its process group to be the foreground
process group of the pty. The subprocess additionally must be running
in a new session so that it can have a different controlling
terminal. This subprocess writes a byte to a pipe every time the pty
is resized, and the virtio-console device can listen for this in its
epoll loop.
Alternatives I considered were to have the subprocess just send
SIGWINCH to its parent, and to use an eventfd instead of a pipe.
I decided against the signal approach because re-purposing a signal
that has a very specific meaning (even if this use was only slightly
different to its normal meaning) felt unclean, and because it would
have required using pidfds to avoid race conditions if
cloud-hypervisor had terminated, which added complexity. I decided
against using an eventfd because using a pipe instead allows the child
to be notified (via poll(2)) when nothing is reading from the pipe any
more, meaning it can be reliably notified of parent death and
terminate itself immediately.
I used clone3(2) instead of fork(2) because without
CLONE_CLEAR_SIGHAND the subprocess would inherit signal-hook's signal
handlers, and there's no other straightforward way to restore all signal
handlers to their defaults in the child process. The only way to do
it would be to iterate through all possible signals, or maintain a
global list of monitored signals ourselves (vmm:vm::HANDLED_SIGNALS is
insufficient because it doesn't take into account e.g. the SIGSYS
signal handler that catches seccomp violations).
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2021-09-10 11:12:17 +00:00
|
|
|
RESIZE_EVENT => {
|
2022-08-12 01:30:13 +00:00
|
|
|
self.resize_pipe
|
|
|
|
.as_ref()
|
|
|
|
.unwrap()
|
|
|
|
.read_exact(&mut [0])
|
|
|
|
.map_err(|e| {
|
|
|
|
EpollHelperError::HandleEvent(anyhow!(
|
|
|
|
"Failed to get resize event: {:?}",
|
|
|
|
e
|
|
|
|
))
|
|
|
|
})?;
|
vmm: notify virtio-console of pty resizes
When a pty is resized (using the TIOCSWINSZ ioctl -- see ioctl_tty(2)),
the kernel will send a SIGWINCH signal to the pty's foreground process
group to notify it of the resize. This is the only way to be notified
by the kernel of a pty resize.
We can't just make the cloud-hypervisor process's process group the
foreground process group though, because a process can only set the
foreground process group of its controlling terminal, and
cloud-hypervisor's controlling terminal will often be the terminal the
user is running it in. To work around this, we fork a subprocess in a
new process group, and set its process group to be the foreground
process group of the pty. The subprocess additionally must be running
in a new session so that it can have a different controlling
terminal. This subprocess writes a byte to a pipe every time the pty
is resized, and the virtio-console device can listen for this in its
epoll loop.
Alternatives I considered were to have the subprocess just send
SIGWINCH to its parent, and to use an eventfd instead of a pipe.
I decided against the signal approach because re-purposing a signal
that has a very specific meaning (even if this use was only slightly
different to its normal meaning) felt unclean, and because it would
have required using pidfds to avoid race conditions if
cloud-hypervisor had terminated, which added complexity. I decided
against using an eventfd because using a pipe instead allows the child
to be notified (via poll(2)) when nothing is reading from the pipe any
more, meaning it can be reliably notified of parent death and
terminate itself immediately.
I used clone3(2) instead of fork(2) because without
CLONE_CLEAR_SIGHAND the subprocess would inherit signal-hook's signal
handlers, and there's no other straightforward way to restore all signal
handlers to their defaults in the child process. The only way to do
it would be to iterate through all possible signals, or maintain a
global list of monitored signals ourselves (vmm:vm::HANDLED_SIGNALS is
insufficient because it doesn't take into account e.g. the SIGSYS
signal handler that catches seccomp violations).
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2021-09-10 11:12:17 +00:00
|
|
|
self.resizer.update_console_size();
|
|
|
|
}
|
2021-09-02 15:56:37 +00:00
|
|
|
FILE_EVENT => {
|
2022-08-25 08:38:42 +00:00
|
|
|
if event.events & libc::EPOLLIN as u32 != 0 {
|
|
|
|
let mut input = [0u8; 64];
|
|
|
|
if let Some(ref mut in_file) = self.endpoint.in_file() {
|
|
|
|
if let Ok(count) = in_file.read(&mut input) {
|
|
|
|
let mut in_buffer = self.in_buffer.lock().unwrap();
|
|
|
|
in_buffer.extend(&input[..count]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.process_input_queue() {
|
|
|
|
self.signal_used_queue(0).map_err(|e| {
|
|
|
|
EpollHelperError::HandleEvent(anyhow!(
|
|
|
|
"Failed to signal used queue: {:?}",
|
|
|
|
e
|
|
|
|
))
|
|
|
|
})?;
|
|
|
|
}
|
2021-09-02 15:56:37 +00:00
|
|
|
}
|
2022-08-25 08:38:42 +00:00
|
|
|
}
|
|
|
|
if self.endpoint.is_pty() {
|
|
|
|
self.file_event_registered = false;
|
|
|
|
if event.events & libc::EPOLLHUP as u32 != 0 {
|
|
|
|
if let Some(pty_write_out) = &self.write_out {
|
|
|
|
if pty_write_out.load(Ordering::Acquire) {
|
|
|
|
pty_write_out.store(false, Ordering::Release);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If the EPOLLHUP flag is not up on the associated event, we
|
|
|
|
// can assume the other end of the PTY is connected and therefore
|
|
|
|
// we can flush the output of the serial to it.
|
|
|
|
self.trigger_pty_flush()
|
|
|
|
.map_err(EpollHelperError::HandleTimeout)?;
|
|
|
|
|
|
|
|
self.register_file_event(helper)?;
|
2021-09-02 15:56:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-08-11 15:38:13 +00:00
|
|
|
_ => {
|
2022-08-12 01:30:13 +00:00
|
|
|
return Err(EpollHelperError::HandleEvent(anyhow!(
|
|
|
|
"Unknown event for virtio-console"
|
|
|
|
)));
|
2020-08-11 15:38:13 +00:00
|
|
|
}
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
2022-08-12 01:30:13 +00:00
|
|
|
Ok(())
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
2022-08-25 08:38:42 +00:00
|
|
|
|
|
|
|
// This function will be invoked whenever the timeout is reached before
|
|
|
|
// any other event was triggered while waiting for the epoll.
|
|
|
|
fn handle_timeout(&mut self, helper: &mut EpollHelper) -> Result<(), EpollHelperError> {
|
|
|
|
if !self.endpoint.is_pty() {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.file_event_registered {
|
|
|
|
// This very specific case happens when the console is connected
|
|
|
|
// to a PTY. We know EPOLLHUP is always present when there's nothing
|
|
|
|
// connected at the other end of the PTY. That's why getting no event
|
|
|
|
// means we can flush the output of the console through the PTY.
|
|
|
|
self.trigger_pty_flush()
|
|
|
|
.map_err(EpollHelperError::HandleTimeout)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Every time we hit the timeout, let's register the FILE_EVENT to give
|
|
|
|
// us a chance to catch a possible event that might have been triggered.
|
|
|
|
self.register_file_event(helper)
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function returns the full list of events found on the epoll before
|
|
|
|
// iterating through it calling handle_event(). It allows the detection of
|
|
|
|
// the PTY connection even when the timeout is not being triggered, which
|
|
|
|
// happens when there are other events preventing the timeout from being
|
|
|
|
// reached. This is an additional way of detecting a PTY connection.
|
|
|
|
fn event_list(
|
|
|
|
&mut self,
|
|
|
|
helper: &mut EpollHelper,
|
|
|
|
events: &[epoll::Event],
|
|
|
|
) -> Result<(), EpollHelperError> {
|
|
|
|
if self.file_event_registered {
|
|
|
|
for event in events {
|
|
|
|
if event.data as u16 == FILE_EVENT && (event.events & libc::EPOLLHUP as u32) != 0 {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This very specific case happens when the console is connected
|
|
|
|
// to a PTY. We know EPOLLHUP is always present when there's nothing
|
|
|
|
// connected at the other end of the PTY. That's why getting no event
|
|
|
|
// means we can flush the output of the console through the PTY.
|
|
|
|
self.trigger_pty_flush()
|
|
|
|
.map_err(EpollHelperError::HandleTimeout)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.register_file_event(helper)
|
|
|
|
}
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
2021-09-02 15:56:37 +00:00
|
|
|
/// Resize handler
|
|
|
|
pub struct ConsoleResizer {
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
config_evt: EventFd,
|
2021-09-10 11:42:28 +00:00
|
|
|
tty: Option<File>,
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
config: Arc<Mutex<VirtioConsoleConfig>>,
|
|
|
|
acked_features: AtomicU64,
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
2021-09-02 15:56:37 +00:00
|
|
|
impl ConsoleResizer {
|
2021-09-10 11:27:08 +00:00
|
|
|
pub fn update_console_size(&self) {
|
2021-09-10 11:42:28 +00:00
|
|
|
if let Some(tty) = self.tty.as_ref() {
|
|
|
|
let (cols, rows) = get_win_size(tty);
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
self.config.lock().unwrap().update_console_size(cols, rows);
|
2021-09-10 11:42:28 +00:00
|
|
|
if self
|
|
|
|
.acked_features
|
|
|
|
.fetch_and(1u64 << VIRTIO_CONSOLE_F_SIZE, Ordering::AcqRel)
|
|
|
|
!= 0
|
|
|
|
{
|
|
|
|
// Send the interrupt to the driver
|
|
|
|
let _ = self.config_evt.write(1);
|
|
|
|
}
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl VirtioConsoleConfig {
|
|
|
|
pub fn update_console_size(&mut self, cols: u16, rows: u16) {
|
|
|
|
self.cols = cols;
|
|
|
|
self.rows = rows;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Virtio device for exposing console to the guest OS through virtio.
|
|
|
|
pub struct Console {
|
2020-09-03 09:37:36 +00:00
|
|
|
common: VirtioCommon,
|
2020-04-27 12:05:29 +00:00
|
|
|
id: String,
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
config: Arc<Mutex<VirtioConsoleConfig>>,
|
2021-09-02 15:56:37 +00:00
|
|
|
resizer: Arc<ConsoleResizer>,
|
vmm: notify virtio-console of pty resizes
When a pty is resized (using the TIOCSWINSZ ioctl -- see ioctl_tty(2)),
the kernel will send a SIGWINCH signal to the pty's foreground process
group to notify it of the resize. This is the only way to be notified
by the kernel of a pty resize.
We can't just make the cloud-hypervisor process's process group the
foreground process group though, because a process can only set the
foreground process group of its controlling terminal, and
cloud-hypervisor's controlling terminal will often be the terminal the
user is running it in. To work around this, we fork a subprocess in a
new process group, and set its process group to be the foreground
process group of the pty. The subprocess additionally must be running
in a new session so that it can have a different controlling
terminal. This subprocess writes a byte to a pipe every time the pty
is resized, and the virtio-console device can listen for this in its
epoll loop.
Alternatives I considered were to have the subprocess just send
SIGWINCH to its parent, and to use an eventfd instead of a pipe.
I decided against the signal approach because re-purposing a signal
that has a very specific meaning (even if this use was only slightly
different to its normal meaning) felt unclean, and because it would
have required using pidfds to avoid race conditions if
cloud-hypervisor had terminated, which added complexity. I decided
against using an eventfd because using a pipe instead allows the child
to be notified (via poll(2)) when nothing is reading from the pipe any
more, meaning it can be reliably notified of parent death and
terminate itself immediately.
I used clone3(2) instead of fork(2) because without
CLONE_CLEAR_SIGHAND the subprocess would inherit signal-hook's signal
handlers, and there's no other straightforward way to restore all signal
handlers to their defaults in the child process. The only way to do
it would be to iterate through all possible signals, or maintain a
global list of monitored signals ourselves (vmm:vm::HANDLED_SIGNALS is
insufficient because it doesn't take into account e.g. the SIGSYS
signal handler that catches seccomp violations).
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2021-09-10 11:12:17 +00:00
|
|
|
resize_pipe: Option<File>,
|
2021-09-02 15:56:37 +00:00
|
|
|
endpoint: Endpoint,
|
2020-08-04 18:12:05 +00:00
|
|
|
seccomp_action: SeccompAction,
|
2021-09-02 15:56:37 +00:00
|
|
|
in_buffer: Arc<Mutex<VecDeque<u8>>>,
|
2021-09-07 15:10:48 +00:00
|
|
|
exit_evt: EventFd,
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
2021-05-11 14:02:43 +00:00
|
|
|
#[derive(Versionize)]
|
2020-04-09 08:41:52 +00:00
|
|
|
pub struct ConsoleState {
|
|
|
|
avail_features: u64,
|
|
|
|
acked_features: u64,
|
|
|
|
config: VirtioConsoleConfig,
|
2021-04-23 09:55:05 +00:00
|
|
|
in_buffer: Vec<u8>,
|
2020-04-09 08:41:52 +00:00
|
|
|
}
|
|
|
|
|
2021-09-10 11:42:28 +00:00
|
|
|
fn get_win_size(tty: &dyn AsRawFd) -> (u16, u16) {
|
2021-09-10 11:27:08 +00:00
|
|
|
#[repr(C)]
|
|
|
|
#[derive(Default)]
|
|
|
|
struct WindowSize {
|
|
|
|
rows: u16,
|
|
|
|
cols: u16,
|
|
|
|
xpixel: u16,
|
|
|
|
ypixel: u16,
|
|
|
|
}
|
|
|
|
let ws: WindowSize = WindowSize::default();
|
|
|
|
|
|
|
|
unsafe {
|
2021-09-10 11:42:28 +00:00
|
|
|
libc::ioctl(tty.as_raw_fd(), TIOCGWINSZ, &ws);
|
2021-09-10 11:27:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
(ws.cols, ws.rows)
|
|
|
|
}
|
|
|
|
|
2021-05-06 13:34:31 +00:00
|
|
|
impl VersionMapped for ConsoleState {}
|
|
|
|
|
2019-07-22 18:50:56 +00:00
|
|
|
impl Console {
|
|
|
|
/// Create a new virtio console device that gets random data from /dev/urandom.
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
pub fn new(
|
2020-04-27 12:05:29 +00:00
|
|
|
id: String,
|
2021-09-02 15:56:37 +00:00
|
|
|
endpoint: Endpoint,
|
vmm: notify virtio-console of pty resizes
When a pty is resized (using the TIOCSWINSZ ioctl -- see ioctl_tty(2)),
the kernel will send a SIGWINCH signal to the pty's foreground process
group to notify it of the resize. This is the only way to be notified
by the kernel of a pty resize.
We can't just make the cloud-hypervisor process's process group the
foreground process group though, because a process can only set the
foreground process group of its controlling terminal, and
cloud-hypervisor's controlling terminal will often be the terminal the
user is running it in. To work around this, we fork a subprocess in a
new process group, and set its process group to be the foreground
process group of the pty. The subprocess additionally must be running
in a new session so that it can have a different controlling
terminal. This subprocess writes a byte to a pipe every time the pty
is resized, and the virtio-console device can listen for this in its
epoll loop.
Alternatives I considered were to have the subprocess just send
SIGWINCH to its parent, and to use an eventfd instead of a pipe.
I decided against the signal approach because re-purposing a signal
that has a very specific meaning (even if this use was only slightly
different to its normal meaning) felt unclean, and because it would
have required using pidfds to avoid race conditions if
cloud-hypervisor had terminated, which added complexity. I decided
against using an eventfd because using a pipe instead allows the child
to be notified (via poll(2)) when nothing is reading from the pipe any
more, meaning it can be reliably notified of parent death and
terminate itself immediately.
I used clone3(2) instead of fork(2) because without
CLONE_CLEAR_SIGHAND the subprocess would inherit signal-hook's signal
handlers, and there's no other straightforward way to restore all signal
handlers to their defaults in the child process. The only way to do
it would be to iterate through all possible signals, or maintain a
global list of monitored signals ourselves (vmm:vm::HANDLED_SIGNALS is
insufficient because it doesn't take into account e.g. the SIGSYS
signal handler that catches seccomp violations).
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2021-09-10 11:12:17 +00:00
|
|
|
resize_pipe: Option<File>,
|
2019-10-04 17:32:26 +00:00
|
|
|
iommu: bool,
|
2020-08-04 18:12:05 +00:00
|
|
|
seccomp_action: SeccompAction,
|
2021-09-07 15:10:48 +00:00
|
|
|
exit_evt: EventFd,
|
2021-09-02 15:56:37 +00:00
|
|
|
) -> io::Result<(Console, Arc<ConsoleResizer>)> {
|
2019-10-04 17:32:26 +00:00
|
|
|
let mut avail_features = 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_CONSOLE_F_SIZE;
|
|
|
|
|
|
|
|
if iommu {
|
|
|
|
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
|
|
|
}
|
2019-07-22 18:50:56 +00:00
|
|
|
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
let config_evt = EventFd::new(EFD_NONBLOCK).unwrap();
|
2021-09-10 11:27:08 +00:00
|
|
|
let console_config = Arc::new(Mutex::new(VirtioConsoleConfig::default()));
|
2021-09-02 15:56:37 +00:00
|
|
|
let resizer = Arc::new(ConsoleResizer {
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
config_evt,
|
|
|
|
config: console_config.clone(),
|
2021-09-10 11:42:28 +00:00
|
|
|
tty: endpoint.out_file().as_ref().map(|t| t.try_clone().unwrap()),
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
acked_features: AtomicU64::new(0),
|
2019-07-22 18:50:56 +00:00
|
|
|
});
|
|
|
|
|
2021-09-10 11:27:08 +00:00
|
|
|
resizer.update_console_size();
|
|
|
|
|
2019-07-22 18:50:56 +00:00
|
|
|
Ok((
|
|
|
|
Console {
|
2020-09-03 09:37:36 +00:00
|
|
|
common: VirtioCommon {
|
2021-03-25 16:54:09 +00:00
|
|
|
device_type: VirtioDeviceType::Console as u32,
|
2020-09-04 08:37:37 +00:00
|
|
|
queue_sizes: QUEUE_SIZES.to_vec(),
|
2020-09-03 09:37:36 +00:00
|
|
|
avail_features,
|
2020-09-04 08:37:37 +00:00
|
|
|
paused_sync: Some(Arc::new(Barrier::new(2))),
|
2021-01-19 06:11:07 +00:00
|
|
|
min_queues: NUM_QUEUES as u16,
|
2020-09-03 15:56:32 +00:00
|
|
|
..Default::default()
|
2020-09-03 09:37:36 +00:00
|
|
|
},
|
2020-04-27 12:05:29 +00:00
|
|
|
id,
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
config: console_config,
|
2021-09-02 15:56:37 +00:00
|
|
|
resizer: resizer.clone(),
|
vmm: notify virtio-console of pty resizes
When a pty is resized (using the TIOCSWINSZ ioctl -- see ioctl_tty(2)),
the kernel will send a SIGWINCH signal to the pty's foreground process
group to notify it of the resize. This is the only way to be notified
by the kernel of a pty resize.
We can't just make the cloud-hypervisor process's process group the
foreground process group though, because a process can only set the
foreground process group of its controlling terminal, and
cloud-hypervisor's controlling terminal will often be the terminal the
user is running it in. To work around this, we fork a subprocess in a
new process group, and set its process group to be the foreground
process group of the pty. The subprocess additionally must be running
in a new session so that it can have a different controlling
terminal. This subprocess writes a byte to a pipe every time the pty
is resized, and the virtio-console device can listen for this in its
epoll loop.
Alternatives I considered were to have the subprocess just send
SIGWINCH to its parent, and to use an eventfd instead of a pipe.
I decided against the signal approach because re-purposing a signal
that has a very specific meaning (even if this use was only slightly
different to its normal meaning) felt unclean, and because it would
have required using pidfds to avoid race conditions if
cloud-hypervisor had terminated, which added complexity. I decided
against using an eventfd because using a pipe instead allows the child
to be notified (via poll(2)) when nothing is reading from the pipe any
more, meaning it can be reliably notified of parent death and
terminate itself immediately.
I used clone3(2) instead of fork(2) because without
CLONE_CLEAR_SIGHAND the subprocess would inherit signal-hook's signal
handlers, and there's no other straightforward way to restore all signal
handlers to their defaults in the child process. The only way to do
it would be to iterate through all possible signals, or maintain a
global list of monitored signals ourselves (vmm:vm::HANDLED_SIGNALS is
insufficient because it doesn't take into account e.g. the SIGSYS
signal handler that catches seccomp violations).
Signed-off-by: Alyssa Ross <hi@alyssa.is>
2021-09-10 11:12:17 +00:00
|
|
|
resize_pipe,
|
2021-09-02 15:56:37 +00:00
|
|
|
endpoint,
|
2020-08-04 18:12:05 +00:00
|
|
|
seccomp_action,
|
2021-09-02 15:56:37 +00:00
|
|
|
in_buffer: Arc::new(Mutex::new(VecDeque::new())),
|
2021-09-07 15:10:48 +00:00
|
|
|
exit_evt,
|
2019-07-22 18:50:56 +00:00
|
|
|
},
|
2021-09-02 15:56:37 +00:00
|
|
|
resizer,
|
2019-07-22 18:50:56 +00:00
|
|
|
))
|
|
|
|
}
|
2020-04-09 08:41:52 +00:00
|
|
|
|
|
|
|
fn state(&self) -> ConsoleState {
|
|
|
|
ConsoleState {
|
2020-09-03 09:37:36 +00:00
|
|
|
avail_features: self.common.avail_features,
|
|
|
|
acked_features: self.common.acked_features,
|
2020-04-09 08:41:52 +00:00
|
|
|
config: *(self.config.lock().unwrap()),
|
2021-09-02 15:56:37 +00:00
|
|
|
in_buffer: self.in_buffer.lock().unwrap().clone().into(),
|
2020-04-09 08:41:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-11 16:00:53 +00:00
|
|
|
fn set_state(&mut self, state: &ConsoleState) {
|
2020-09-03 09:37:36 +00:00
|
|
|
self.common.avail_features = state.avail_features;
|
|
|
|
self.common.acked_features = state.acked_features;
|
2020-04-09 08:41:52 +00:00
|
|
|
*(self.config.lock().unwrap()) = state.config;
|
2021-09-02 15:56:37 +00:00
|
|
|
*(self.in_buffer.lock().unwrap()) = state.in_buffer.clone().into();
|
2020-04-09 08:41:52 +00:00
|
|
|
}
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for Console {
|
|
|
|
fn drop(&mut self) {
|
2020-09-04 08:37:37 +00:00
|
|
|
if let Some(kill_evt) = self.common.kill_evt.take() {
|
2019-07-22 18:50:56 +00:00
|
|
|
// Ignore the result because there is nothing we can do about it.
|
|
|
|
let _ = kill_evt.write(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl VirtioDevice for Console {
|
|
|
|
fn device_type(&self) -> u32 {
|
2020-09-04 08:37:37 +00:00
|
|
|
self.common.device_type
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn queue_max_sizes(&self) -> &[u16] {
|
2020-09-04 08:37:37 +00:00
|
|
|
&self.common.queue_sizes
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
2020-01-23 10:14:38 +00:00
|
|
|
fn features(&self) -> u64 {
|
2020-09-03 09:37:36 +00:00
|
|
|
self.common.avail_features
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
2020-01-23 10:14:38 +00:00
|
|
|
fn ack_features(&mut self, value: u64) {
|
2020-09-03 09:37:36 +00:00
|
|
|
self.common.ack_features(value)
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
2020-07-16 09:34:51 +00:00
|
|
|
fn read_config(&self, offset: u64, data: &mut [u8]) {
|
|
|
|
self.read_config_from_slice(self.config.lock().unwrap().as_slice(), offset, data);
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn activate(
|
|
|
|
&mut self,
|
2022-07-06 14:08:08 +00:00
|
|
|
mem: GuestMemoryAtomic<GuestMemoryMmap>,
|
2020-01-13 17:52:19 +00:00
|
|
|
interrupt_cb: Arc<dyn VirtioInterrupt>,
|
2022-07-06 14:08:08 +00:00
|
|
|
mut queues: Vec<(usize, Queue, EventFd)>,
|
2019-07-22 18:50:56 +00:00
|
|
|
) -> ActivateResult {
|
2022-07-20 14:45:49 +00:00
|
|
|
self.common.activate(&queues, &interrupt_cb)?;
|
2021-09-02 15:56:37 +00:00
|
|
|
self.resizer
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
.acked_features
|
2020-09-03 09:37:36 +00:00
|
|
|
.store(self.common.acked_features, Ordering::Relaxed);
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
|
2020-09-03 09:37:36 +00:00
|
|
|
if self.common.feature_acked(VIRTIO_CONSOLE_F_SIZE) {
|
2022-01-24 14:30:42 +00:00
|
|
|
if let Err(e) = interrupt_cb.trigger(VirtioInterruptType::Config) {
|
vm-virtio: Implement console size config feature
One of the features of the virtio console device is its size can be
configured and updated. Our first iteration of the console device
implementation is lack of this feature. As a result, it had a
default fixed size which could not be changed. This commit implements
the console config feature and lets us change the console size from
the vmm side.
During the activation of the device, vmm reads the current terminal
size, sets the console configuration accordinly, and lets the driver
know about this configuration by sending an interrupt. Later, if
someone changes the terminal size, the vmm detects the corresponding
event, updates the configuration, and sends interrupt as before. As a
result, the console device driver, in the guest, updates the console
size.
Signed-off-by: A K M Fazla Mehrab <fazla.mehrab.akm@intel.com>
2019-07-23 19:18:20 +00:00
|
|
|
error!("Failed to signal console driver: {:?}", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-02 18:08:06 +00:00
|
|
|
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
2021-09-02 15:56:37 +00:00
|
|
|
let input_evt = EventFd::new(EFD_NONBLOCK).unwrap();
|
2020-09-04 08:37:37 +00:00
|
|
|
|
2022-07-20 14:45:49 +00:00
|
|
|
let mut virtqueues = Vec::new();
|
|
|
|
let (_, queue, queue_evt) = queues.remove(0);
|
|
|
|
virtqueues.push(queue);
|
|
|
|
let input_queue_evt = queue_evt;
|
|
|
|
let (_, queue, queue_evt) = queues.remove(0);
|
|
|
|
virtqueues.push(queue);
|
|
|
|
let output_queue_evt = queue_evt;
|
|
|
|
|
2022-08-25 08:38:42 +00:00
|
|
|
let mut handler = ConsoleEpollHandler::new(
|
2022-07-06 14:08:08 +00:00
|
|
|
mem,
|
2022-08-25 08:38:42 +00:00
|
|
|
virtqueues,
|
2019-10-04 16:38:43 +00:00
|
|
|
interrupt_cb,
|
2022-08-25 08:38:42 +00:00
|
|
|
self.in_buffer.clone(),
|
|
|
|
Arc::clone(&self.resizer),
|
|
|
|
self.endpoint.clone(),
|
2022-07-20 14:45:49 +00:00
|
|
|
input_queue_evt,
|
|
|
|
output_queue_evt,
|
2021-09-02 15:56:37 +00:00
|
|
|
input_evt,
|
2022-08-25 08:38:42 +00:00
|
|
|
self.resizer.config_evt.try_clone().unwrap(),
|
|
|
|
self.resize_pipe.as_ref().map(|p| p.try_clone().unwrap()),
|
2019-10-04 16:38:43 +00:00
|
|
|
kill_evt,
|
2019-11-19 00:42:31 +00:00
|
|
|
pause_evt,
|
2022-08-25 08:38:42 +00:00
|
|
|
self.common.access_platform.clone(),
|
|
|
|
);
|
2019-07-22 18:50:56 +00:00
|
|
|
|
2020-09-04 08:37:37 +00:00
|
|
|
let paused = self.common.paused.clone();
|
|
|
|
let paused_sync = self.common.paused_sync.clone();
|
2020-01-27 12:56:05 +00:00
|
|
|
let mut epoll_threads = Vec::new();
|
2021-09-03 10:43:30 +00:00
|
|
|
|
|
|
|
spawn_virtio_thread(
|
|
|
|
&self.id,
|
|
|
|
&self.seccomp_action,
|
|
|
|
Thread::VirtioConsole,
|
|
|
|
&mut epoll_threads,
|
2021-09-07 15:10:48 +00:00
|
|
|
&self.exit_evt,
|
2022-08-12 00:16:27 +00:00
|
|
|
move || handler.run(paused, paused_sync.unwrap()),
|
2021-09-03 10:43:30 +00:00
|
|
|
)?;
|
2019-07-22 18:50:56 +00:00
|
|
|
|
2020-09-04 08:37:37 +00:00
|
|
|
self.common.epoll_threads = Some(epoll_threads);
|
2020-01-27 12:56:05 +00:00
|
|
|
|
2021-02-18 15:10:51 +00:00
|
|
|
event!("virtio-device", "activated", "id", &self.id);
|
2019-10-04 16:38:43 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-01-18 12:38:08 +00:00
|
|
|
fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
|
2021-02-18 15:10:51 +00:00
|
|
|
let result = self.common.reset();
|
|
|
|
event!("virtio-device", "reset", "id", &self.id);
|
|
|
|
result
|
2020-09-04 08:37:37 +00:00
|
|
|
}
|
2022-01-26 16:11:18 +00:00
|
|
|
|
|
|
|
fn set_access_platform(&mut self, access_platform: Arc<dyn AccessPlatform>) {
|
|
|
|
self.common.set_access_platform(access_platform)
|
|
|
|
}
|
2020-09-04 08:37:37 +00:00
|
|
|
}
|
2019-11-19 00:42:31 +00:00
|
|
|
|
2020-09-04 08:37:37 +00:00
|
|
|
impl Pausable for Console {
|
|
|
|
fn pause(&mut self) -> result::Result<(), MigratableError> {
|
|
|
|
self.common.pause()
|
|
|
|
}
|
2019-10-04 16:38:43 +00:00
|
|
|
|
2020-09-04 08:37:37 +00:00
|
|
|
fn resume(&mut self) -> result::Result<(), MigratableError> {
|
|
|
|
self.common.resume()
|
2019-07-22 18:50:56 +00:00
|
|
|
}
|
|
|
|
}
|
2019-11-19 00:42:31 +00:00
|
|
|
|
2020-04-09 08:41:52 +00:00
|
|
|
impl Snapshottable for Console {
|
|
|
|
fn id(&self) -> String {
|
2020-04-27 12:05:29 +00:00
|
|
|
self.id.clone()
|
2020-04-09 08:41:52 +00:00
|
|
|
}
|
|
|
|
|
2020-08-21 12:31:58 +00:00
|
|
|
fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
|
2021-05-06 13:34:31 +00:00
|
|
|
Snapshot::new_from_versioned_state(&self.id, &self.state())
|
2020-04-09 08:41:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
|
2021-05-06 13:34:31 +00:00
|
|
|
self.set_state(&snapshot.to_versioned_state(&self.id)?);
|
2021-04-08 09:20:10 +00:00
|
|
|
Ok(())
|
2020-04-09 08:41:52 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-01 16:59:51 +00:00
|
|
|
impl Transportable for Console {}
|
2019-11-19 00:42:31 +00:00
|
|
|
impl Migratable for Console {}
|