vmm: Handle epoll events for PTYs separately

Use two separate events for the console and serial PTY and then drive
the handling of the inputs on the PTY separately. This results in the
correct behaviour when both console and serial are attached to the PTY
as they are triggered separately on the epoll so events are not lost.

Fixes: #3012

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
Rob Bradford 2021-08-25 12:30:49 +01:00
parent 6233f6f68e
commit 4d2a4e2805
2 changed files with 29 additions and 24 deletions

View File

@ -148,7 +148,8 @@ pub enum EpollDispatch {
Stdin,
Api,
ActivateVirtioDevices,
Pty,
ConsolePty,
SerialPty,
}
pub struct EpollContext {
@ -392,12 +393,12 @@ impl Vmm {
)?;
if let Some(serial_pty) = vm.serial_pty() {
self.epoll
.add_event(&serial_pty.main, EpollDispatch::Pty)
.add_event(&serial_pty.main, EpollDispatch::SerialPty)
.map_err(VmError::EventfdError)?;
};
if let Some(console_pty) = vm.console_pty() {
self.epoll
.add_event(&console_pty.main, EpollDispatch::Pty)
.add_event(&console_pty.main, EpollDispatch::ConsolePty)
.map_err(VmError::EventfdError)?;
};
self.vm = Some(vm);
@ -1295,9 +1296,9 @@ impl Vmm {
.map_err(Error::ActivateVirtioDevices)?;
}
}
EpollDispatch::Pty => {
event @ (EpollDispatch::ConsolePty | EpollDispatch::SerialPty) => {
if let Some(ref vm) = self.vm {
vm.handle_pty().map_err(Error::Pty)?;
vm.handle_pty(event).map_err(Error::Pty)?;
}
}
EpollDispatch::Api => {

View File

@ -17,7 +17,6 @@ use crate::config::{
ConsoleOutputMode, DeviceConfig, DiskConfig, FsConfig, HotplugMethod, NetConfig, PmemConfig,
UserDeviceConfig, ValidationError, VmConfig, VsockConfig,
};
use crate::cpu;
use crate::device_manager::{
self, get_win_size, Console, DeviceManager, DeviceManagerError, PtyPair,
};
@ -26,6 +25,7 @@ use crate::memory_manager::{Error as MemoryManagerError, MemoryManager};
use crate::migration::{get_vm_snapshot, url_to_path, VM_SNAPSHOT_FILE};
use crate::seccomp_filters::{get_seccomp_filter, Thread};
use crate::GuestMemoryMmap;
use crate::{cpu, EpollDispatch};
use crate::{
PciDeviceInfo, CPU_MANAGER_SNAPSHOT_ID, DEVICE_MANAGER_SNAPSHOT_ID, MEMORY_MANAGER_SNAPSHOT_ID,
};
@ -1911,28 +1911,32 @@ impl Vm {
Ok(())
}
pub fn handle_pty(&self) -> Result<()> {
pub fn handle_pty(&self, event: EpollDispatch) -> Result<()> {
// Could be a little dangerous, picks up a lock on device_manager
// and goes into a blocking read. If the epoll loops starts to be
// services by multiple threads likely need to revist this.
let dm = self.device_manager.lock().unwrap();
let mut out = [0u8; 64];
if let Some(mut pty) = dm.serial_pty() {
let count = pty.main.read(&mut out).map_err(Error::PtyConsole)?;
let console = dm.console();
if console.input_enabled() {
console
.queue_input_bytes_serial(&out[..count])
.map_err(Error::Console)?;
}
};
let count = match dm.console_pty() {
Some(mut pty) => pty.main.read(&mut out).map_err(Error::PtyConsole)?,
None => return Ok(()),
};
let console = dm.console();
if console.input_enabled() {
console.queue_input_bytes_console(&out[..count])
if matches!(event, EpollDispatch::SerialPty) {
if let Some(mut pty) = dm.serial_pty() {
let mut out = [0u8; 64];
let count = pty.main.read(&mut out).map_err(Error::PtyConsole)?;
let console = dm.console();
if console.input_enabled() {
console
.queue_input_bytes_serial(&out[..count])
.map_err(Error::Console)?;
}
};
} else if matches!(event, EpollDispatch::ConsolePty) {
if let Some(mut pty) = dm.console_pty() {
let mut out = [0u8; 64];
let count = pty.main.read(&mut out).map_err(Error::PtyConsole)?;
let console = dm.console();
if console.input_enabled() {
console.queue_input_bytes_console(&out[..count])
}
};
}
Ok(())