virtio-devices: console: Handle output queue errors gracefully

Address translation, guest memory read, write, and flush failures on
the transmitq propagated errors that killed the console device thread.
A host-side I/O error such as a PTY disconnect would permanently
disable the console. Log warnings and continue processing instead.

Signed-off-by: Rob Bradford <rbradford@meta.com>
Assisted-by: Claude:claude-opus-4-6
This commit is contained in:
Rob Bradford
2026-05-06 15:09:26 +01:00
parent 4725a997cc
commit 56c18b51cc

View File

@@ -51,12 +51,6 @@ const VIRTIO_CONSOLE_F_SIZE: u64 = 0;
#[derive(Error, Debug)] #[derive(Error, Debug)]
enum Error { enum Error {
#[error("Failed to read from guest memory")]
GuestMemoryRead(#[source] vm_memory::guest_memory::Error),
#[error("Failed to write_all output")]
OutputWriteAll(#[source] io::Error),
#[error("Failed to flush output")]
OutputFlush(#[source] io::Error),
#[error("Failed to add used index")] #[error("Failed to add used index")]
QueueAddUsed(#[source] virtio_queue::Error), QueueAddUsed(#[source] virtio_queue::Error),
} }
@@ -266,21 +260,28 @@ impl ConsoleEpollHandler {
continue; continue;
} }
if let Some(out) = &mut self.out { if let Some(out) = &mut self.out {
let addr = match desc
.addr()
.translate_gva(self.access_platform.as_deref(), desc.len() as usize)
{
Ok(a) => a,
Err(e) => {
warn!("Failed to translate transmitq descriptor address: {e}");
continue;
}
};
let mut buf: Vec<u8> = Vec::new(); let mut buf: Vec<u8> = Vec::new();
desc_chain if let Err(e) =
.memory() desc_chain
.write_volatile_to( .memory()
desc.addr() .write_volatile_to(addr, &mut buf, desc.len() as usize)
.translate_gva(self.access_platform.as_deref(), desc.len() as usize) {
.map_err(|e| { warn!("Failed to read from transmitq descriptor: {e}");
Error::GuestMemoryRead(vm_memory::GuestMemoryError::IOError(e)) continue;
})?, }
&mut buf, if let Err(e) = out.write_all(&buf) {
desc.len() as usize, warn!("Failed to write console output: {e}");
) }
.map_err(Error::GuestMemoryRead)?;
out.write_all(&buf).map_err(Error::OutputWriteAll)?;
} }
} }
trans_queue trans_queue
@@ -289,8 +290,11 @@ impl ConsoleEpollHandler {
used_descs = true; used_descs = true;
} }
if used_descs && let Some(out) = &mut self.out { if used_descs
out.flush().map_err(Error::OutputFlush)?; && let Some(out) = &mut self.out
&& let Err(e) = out.flush()
{
warn!("Failed to flush console output: {e}");
} }
Ok(used_descs) Ok(used_descs)