From 2698cfed9824d804fd2b7e43172d917ef9854547 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Sat, 7 Mar 2026 01:25:04 +0000 Subject: [PATCH] virtio-devices: console: Fix descriptor chain processing Both process_input_queue and process_output_queue were only processing the first descriptor of a chain, leading to data loss if the driver used chained descriptors. This change iterates through all descriptors in a chain. It also moves the flush call out of the descriptor loops to improve performance. Signed-off-by: Andrei Vagin --- virtio-devices/src/console.rs | 67 ++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/virtio-devices/src/console.rs b/virtio-devices/src/console.rs index 5430673e7..ab907db20 100644 --- a/virtio-devices/src/console.rs +++ b/virtio-devices/src/console.rs @@ -51,8 +51,6 @@ const VIRTIO_CONSOLE_F_SIZE: u64 = 0; #[derive(Error, Debug)] enum Error { - #[error("Descriptor chain too short")] - DescriptorChainTooShort, #[error("Failed to read from guest memory")] GuestMemoryRead(#[source] vm_memory::guest_memory::Error), #[error("Failed to write to guest memory")] @@ -210,21 +208,28 @@ impl ConsoleEpollHandler { } while let Some(mut desc_chain) = recv_queue.pop_descriptor_chain(self.mem.memory()) { - let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?; - let len = cmp::min(desc.len(), in_buffer.len() as u32); - let source_slice = in_buffer.drain(..len as usize).collect::>(); + let mut total_len = 0; + while let Some(desc) = desc_chain.next() { + if in_buffer.is_empty() { + break; + } + let len = cmp::min(desc.len(), in_buffer.len() as u32); + let source_slice = in_buffer.drain(..len as usize).collect::>(); - desc_chain - .memory() - .write_slice( - &source_slice[..], - desc.addr() - .translate_gva(self.access_platform.as_deref(), desc.len() as usize), - ) - .map_err(Error::GuestMemoryWrite)?; + desc_chain + .memory() + .write_slice( + &source_slice[..], + desc.addr() + .translate_gva(self.access_platform.as_deref(), desc.len() as usize), + ) + .map_err(Error::GuestMemoryWrite)?; + + total_len += len; + } recv_queue - .add_used(desc_chain.memory(), desc_chain.head_index(), len) + .add_used(desc_chain.memory(), desc_chain.head_index(), total_len) .map_err(Error::QueueAddUsed)?; used_descs = true; @@ -248,21 +253,23 @@ impl ConsoleEpollHandler { let mut used_descs = false; while let Some(mut desc_chain) = trans_queue.pop_descriptor_chain(self.mem.memory()) { - let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?; - if let Some(out) = &mut self.out { - let mut buf: Vec = Vec::new(); - desc_chain - .memory() - .write_volatile_to( - desc.addr() - .translate_gva(self.access_platform.as_deref(), desc.len() as usize), - &mut buf, - desc.len() as usize, - ) - .map_err(Error::GuestMemoryRead)?; + while let Some(desc) = desc_chain.next() { + if let Some(out) = &mut self.out { + let mut buf: Vec = Vec::new(); + desc_chain + .memory() + .write_volatile_to( + desc.addr().translate_gva( + self.access_platform.as_deref(), + desc.len() as usize, + ), + &mut buf, + desc.len() as usize, + ) + .map_err(Error::GuestMemoryRead)?; - out.write_all(&buf).map_err(Error::OutputWriteAll)?; - out.flush().map_err(Error::OutputFlush)?; + out.write_all(&buf).map_err(Error::OutputWriteAll)?; + } } trans_queue .add_used(desc_chain.memory(), desc_chain.head_index(), 0) @@ -270,6 +277,10 @@ impl ConsoleEpollHandler { used_descs = true; } + if used_descs && let Some(out) = &mut self.out { + out.flush().map_err(Error::OutputFlush)?; + } + Ok(used_descs) }