mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
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 <avagin@google.com>
This commit is contained in:
@@ -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::<Vec<u8>>();
|
||||
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::<Vec<u8>>();
|
||||
|
||||
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<u8> = 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<u8> = 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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user