diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 1c09042fd..df54e45e5 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -22,6 +22,7 @@ use crate::interrupt::LegacyUserspaceInterruptManager; #[cfg(feature = "acpi")] use crate::memory_manager::MEMORY_MANAGER_ACPI_SIZE; use crate::memory_manager::{Error as MemoryManagerError, MemoryManager}; +use crate::serial_buffer::SerialBuffer; use crate::GuestRegionMmap; use crate::PciDeviceInfo; use crate::{device_node, DEVICE_MANAGER_SNAPSHOT_ID}; @@ -1707,8 +1708,9 @@ impl DeviceManager { if let Some(pty) = serial_pty { self.config.lock().unwrap().serial.file = Some(pty.path.clone()); let writer = pty.main.try_clone().unwrap(); + let buffer = SerialBuffer::new(Box::new(writer)); self.serial_pty = Some(Arc::new(Mutex::new(pty))); - Some(Box::new(writer)) + Some(Box::new(buffer)) } else { let (main, mut sub, path) = create_pty().map_err(DeviceManagerError::SerialPtyOpen)?; @@ -1716,8 +1718,9 @@ impl DeviceManager { .map_err(DeviceManagerError::SetPtyRaw)?; self.config.lock().unwrap().serial.file = Some(path.clone()); 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 }))); - Some(Box::new(writer)) + Some(Box::new(buffer)) } } ConsoleOutputMode::Tty => Some(Box::new(stdout())), diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 9b12c5b9c..aecc399b2 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -59,6 +59,7 @@ pub mod vm; #[cfg(feature = "acpi")] mod acpi; +mod serial_buffer; type GuestMemoryMmap = vm_memory::GuestMemoryMmap; type GuestRegionMmap = vm_memory::GuestRegionMmap; diff --git a/vmm/src/serial_buffer.rs b/vmm/src/serial_buffer.rs new file mode 100644 index 000000000..c9b4b13be --- /dev/null +++ b/vmm/src/serial_buffer.rs @@ -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, + head: usize, + tail: usize, + out: Box, +} + +const MAX_BUFFER_SIZE: usize = 16 << 10; + +impl SerialBuffer { + pub(crate) fn new(out: Box) -> 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 { + // 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(()) + } +}