mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-10-01 02:55:45 +00:00
vmm: Introduce a SerialBuffer for buffering serial output
Introduce a dynamic buffer for storing output from the serial port. The SerialBuffer implements std::io::Write and can be used in place of the direct output for the serial device. The internals of the buffer is a vector that grows dynamically based on demand up to a fixed size at which point old data will be overwritten. Currently the buffer is only flushed upon writes. Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
parent
9a634f07cb
commit
d92707afc5
@ -22,6 +22,7 @@ use crate::interrupt::LegacyUserspaceInterruptManager;
|
|||||||
#[cfg(feature = "acpi")]
|
#[cfg(feature = "acpi")]
|
||||||
use crate::memory_manager::MEMORY_MANAGER_ACPI_SIZE;
|
use crate::memory_manager::MEMORY_MANAGER_ACPI_SIZE;
|
||||||
use crate::memory_manager::{Error as MemoryManagerError, MemoryManager};
|
use crate::memory_manager::{Error as MemoryManagerError, MemoryManager};
|
||||||
|
use crate::serial_buffer::SerialBuffer;
|
||||||
use crate::GuestRegionMmap;
|
use crate::GuestRegionMmap;
|
||||||
use crate::PciDeviceInfo;
|
use crate::PciDeviceInfo;
|
||||||
use crate::{device_node, DEVICE_MANAGER_SNAPSHOT_ID};
|
use crate::{device_node, DEVICE_MANAGER_SNAPSHOT_ID};
|
||||||
@ -1707,8 +1708,9 @@ impl DeviceManager {
|
|||||||
if let Some(pty) = serial_pty {
|
if let Some(pty) = serial_pty {
|
||||||
self.config.lock().unwrap().serial.file = Some(pty.path.clone());
|
self.config.lock().unwrap().serial.file = Some(pty.path.clone());
|
||||||
let writer = pty.main.try_clone().unwrap();
|
let writer = pty.main.try_clone().unwrap();
|
||||||
|
let buffer = SerialBuffer::new(Box::new(writer));
|
||||||
self.serial_pty = Some(Arc::new(Mutex::new(pty)));
|
self.serial_pty = Some(Arc::new(Mutex::new(pty)));
|
||||||
Some(Box::new(writer))
|
Some(Box::new(buffer))
|
||||||
} else {
|
} else {
|
||||||
let (main, mut sub, path) =
|
let (main, mut sub, path) =
|
||||||
create_pty().map_err(DeviceManagerError::SerialPtyOpen)?;
|
create_pty().map_err(DeviceManagerError::SerialPtyOpen)?;
|
||||||
@ -1716,8 +1718,9 @@ impl DeviceManager {
|
|||||||
.map_err(DeviceManagerError::SetPtyRaw)?;
|
.map_err(DeviceManagerError::SetPtyRaw)?;
|
||||||
self.config.lock().unwrap().serial.file = Some(path.clone());
|
self.config.lock().unwrap().serial.file = Some(path.clone());
|
||||||
let writer = main.try_clone().unwrap();
|
let writer = main.try_clone().unwrap();
|
||||||
|
let buffer = SerialBuffer::new(Box::new(writer));
|
||||||
self.serial_pty = Some(Arc::new(Mutex::new(PtyPair { main, sub, path })));
|
self.serial_pty = Some(Arc::new(Mutex::new(PtyPair { main, sub, path })));
|
||||||
Some(Box::new(writer))
|
Some(Box::new(buffer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConsoleOutputMode::Tty => Some(Box::new(stdout())),
|
ConsoleOutputMode::Tty => Some(Box::new(stdout())),
|
||||||
|
@ -59,6 +59,7 @@ pub mod vm;
|
|||||||
|
|
||||||
#[cfg(feature = "acpi")]
|
#[cfg(feature = "acpi")]
|
||||||
mod acpi;
|
mod acpi;
|
||||||
|
mod serial_buffer;
|
||||||
|
|
||||||
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
type GuestMemoryMmap = vm_memory::GuestMemoryMmap<AtomicBitmap>;
|
||||||
type GuestRegionMmap = vm_memory::GuestRegionMmap<AtomicBitmap>;
|
type GuestRegionMmap = vm_memory::GuestRegionMmap<AtomicBitmap>;
|
||||||
|
121
vmm/src/serial_buffer.rs
Normal file
121
vmm/src/serial_buffer.rs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// Copyright © 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
// Circular buffer implementation for serial output.
|
||||||
|
// Read from head; push to tail
|
||||||
|
pub(crate) struct SerialBuffer {
|
||||||
|
buffer: Vec<u8>,
|
||||||
|
head: usize,
|
||||||
|
tail: usize,
|
||||||
|
out: Box<dyn Write + Send>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_BUFFER_SIZE: usize = 16 << 10;
|
||||||
|
|
||||||
|
impl SerialBuffer {
|
||||||
|
pub(crate) fn new(out: Box<dyn Write + Send>) -> Self {
|
||||||
|
Self {
|
||||||
|
buffer: vec![],
|
||||||
|
head: 0,
|
||||||
|
tail: 0,
|
||||||
|
out,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn flush_buffer(&mut self) -> Result<(), std::io::Error> {
|
||||||
|
if self.tail <= self.head {
|
||||||
|
// The buffer to be written is in two parts
|
||||||
|
let buf = &self.buffer[self.head..];
|
||||||
|
match self.out.write(buf) {
|
||||||
|
Ok(bytes_written) => {
|
||||||
|
if bytes_written == buf.len() {
|
||||||
|
self.head = 0;
|
||||||
|
// Can now proceed to write the other part of the buffer
|
||||||
|
} else {
|
||||||
|
self.head += bytes_written;
|
||||||
|
self.out.flush()?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if !matches!(e.kind(), std::io::ErrorKind::WouldBlock) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf = &self.buffer[self.head..self.tail];
|
||||||
|
match self.out.write(buf) {
|
||||||
|
Ok(bytes_written) => {
|
||||||
|
if bytes_written == buf.len() {
|
||||||
|
self.buffer.clear();
|
||||||
|
self.buffer.shrink_to_fit();
|
||||||
|
self.head = 0;
|
||||||
|
self.tail = 0;
|
||||||
|
} else {
|
||||||
|
self.head += bytes_written;
|
||||||
|
}
|
||||||
|
self.out.flush()?;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if !matches!(e.kind(), std::io::ErrorKind::WouldBlock) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Write for SerialBuffer {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
|
||||||
|
// The serial output only writes one byte at a time
|
||||||
|
for v in buf {
|
||||||
|
if self.buffer.is_empty() {
|
||||||
|
// This case exists to avoid allocating the buffer if it's not needed
|
||||||
|
if let Err(e) = self.out.write(&[*v]) {
|
||||||
|
if !matches!(e.kind(), std::io::ErrorKind::WouldBlock) {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
self.buffer.push(*v);
|
||||||
|
self.tail += 1;
|
||||||
|
} else {
|
||||||
|
self.out.flush()?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Buffer is completely full, lose the oldest byte by moving head forward
|
||||||
|
if self.head == self.tail {
|
||||||
|
self.head = self.tail + 1;
|
||||||
|
if self.head == MAX_BUFFER_SIZE {
|
||||||
|
self.head = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.buffer.len() < MAX_BUFFER_SIZE {
|
||||||
|
self.buffer.push(*v);
|
||||||
|
} else {
|
||||||
|
self.buffer[self.tail] = *v;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tail += 1;
|
||||||
|
if self.tail == MAX_BUFFER_SIZE {
|
||||||
|
self.tail = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.flush_buffer()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> Result<(), std::io::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user