devices: virtio: ensure all block data is transferred

Add _exact/_all variants of the FileReadWriteAtVolatile functions on
descriptor Reader/Writer, and use them in the block device to replace
the short read/short write error cases.  This ensures all data is
read/written even if the underlying implementation (in particular,
qcow2) does not transfer the full amount of data in one
read_vectored_at_volatile/write_vectored_at_volatile call.

BUG=chromium:1023422
TEST=`mkfs.btrfs /dev/vdb` with a qcow2 disk

Change-Id: Ia37a333947f6f63faf3d4a06cfcc297309d5aff6
Signed-off-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1907443
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Stephen Barber <smbarber@chromium.org>
This commit is contained in:
Daniel Verkamp 2019-11-11 09:06:31 -08:00 committed by Commit Bot
parent 62fd776c5c
commit 6cf8651dc3
2 changed files with 63 additions and 58 deletions

View file

@ -141,11 +141,6 @@ enum ExecuteError {
sector: u64, sector: u64,
desc_error: io::Error, desc_error: io::Error,
}, },
ShortRead {
sector: u64,
expected_length: usize,
actual_length: usize,
},
Seek { Seek {
ioerr: io::Error, ioerr: io::Error,
sector: u64, sector: u64,
@ -156,11 +151,6 @@ enum ExecuteError {
sector: u64, sector: u64,
desc_error: io::Error, desc_error: io::Error,
}, },
ShortWrite {
sector: u64,
expected_length: usize,
actual_length: usize,
},
DiscardWriteZeroes { DiscardWriteZeroes {
ioerr: Option<io::Error>, ioerr: Option<io::Error>,
sector: u64, sector: u64,
@ -193,15 +183,6 @@ impl Display for ExecuteError {
"io error reading {} bytes from sector {}: {}", "io error reading {} bytes from sector {}: {}",
length, sector, desc_error, length, sector, desc_error,
), ),
ShortRead {
sector,
expected_length,
actual_length,
} => write!(
f,
"short read: {} bytes of {} at sector {}",
actual_length, expected_length, sector
),
Seek { ioerr, sector } => write!(f, "failed to seek to sector {}: {}", sector, ioerr), Seek { ioerr, sector } => write!(f, "failed to seek to sector {}: {}", sector, ioerr),
TimerFd(e) => write!(f, "{}", e), TimerFd(e) => write!(f, "{}", e),
WriteIo { WriteIo {
@ -213,15 +194,6 @@ impl Display for ExecuteError {
"io error writing {} bytes to sector {}: {}", "io error writing {} bytes to sector {}: {}",
length, sector, desc_error, length, sector, desc_error,
), ),
ShortWrite {
sector,
expected_length,
actual_length,
} => write!(
f,
"short write: {} bytes of {} at sector {}",
actual_length, expected_length, sector
),
DiscardWriteZeroes { DiscardWriteZeroes {
ioerr: Some(ioerr), ioerr: Some(ioerr),
sector, sector,
@ -258,11 +230,9 @@ impl ExecuteError {
ExecuteError::WriteStatus(_) => VIRTIO_BLK_S_IOERR, ExecuteError::WriteStatus(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::Flush(_) => VIRTIO_BLK_S_IOERR, ExecuteError::Flush(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::ReadIo { .. } => VIRTIO_BLK_S_IOERR, ExecuteError::ReadIo { .. } => VIRTIO_BLK_S_IOERR,
ExecuteError::ShortRead { .. } => VIRTIO_BLK_S_IOERR,
ExecuteError::Seek { .. } => VIRTIO_BLK_S_IOERR, ExecuteError::Seek { .. } => VIRTIO_BLK_S_IOERR,
ExecuteError::TimerFd(_) => VIRTIO_BLK_S_IOERR, ExecuteError::TimerFd(_) => VIRTIO_BLK_S_IOERR,
ExecuteError::WriteIo { .. } => VIRTIO_BLK_S_IOERR, ExecuteError::WriteIo { .. } => VIRTIO_BLK_S_IOERR,
ExecuteError::ShortWrite { .. } => VIRTIO_BLK_S_IOERR,
ExecuteError::DiscardWriteZeroes { .. } => VIRTIO_BLK_S_IOERR, ExecuteError::DiscardWriteZeroes { .. } => VIRTIO_BLK_S_IOERR,
ExecuteError::ReadOnly { .. } => VIRTIO_BLK_S_IOERR, ExecuteError::ReadOnly { .. } => VIRTIO_BLK_S_IOERR,
ExecuteError::OutOfRange { .. } => VIRTIO_BLK_S_IOERR, ExecuteError::OutOfRange { .. } => VIRTIO_BLK_S_IOERR,
@ -613,21 +583,13 @@ impl Block {
.checked_shl(u32::from(SECTOR_SHIFT)) .checked_shl(u32::from(SECTOR_SHIFT))
.ok_or(ExecuteError::OutOfRange)?; .ok_or(ExecuteError::OutOfRange)?;
check_range(offset, data_len as u64, disk_size)?; check_range(offset, data_len as u64, disk_size)?;
let actual_length =
writer writer
.write_from_at(disk, data_len, offset) .write_all_from_at(disk, data_len, offset)
.map_err(|desc_error| ExecuteError::ReadIo { .map_err(|desc_error| ExecuteError::ReadIo {
length: data_len, length: data_len,
sector, sector,
desc_error, desc_error,
})?; })?;
if actual_length < data_len {
return Err(ExecuteError::ShortRead {
sector,
expected_length: data_len,
actual_length,
});
}
} }
VIRTIO_BLK_T_OUT => { VIRTIO_BLK_T_OUT => {
let data_len = reader.available_bytes(); let data_len = reader.available_bytes();
@ -635,21 +597,13 @@ impl Block {
.checked_shl(u32::from(SECTOR_SHIFT)) .checked_shl(u32::from(SECTOR_SHIFT))
.ok_or(ExecuteError::OutOfRange)?; .ok_or(ExecuteError::OutOfRange)?;
check_range(offset, data_len as u64, disk_size)?; check_range(offset, data_len as u64, disk_size)?;
let actual_length =
reader reader
.read_to_at(disk, data_len, offset) .read_exact_to_at(disk, data_len, offset)
.map_err(|desc_error| ExecuteError::WriteIo { .map_err(|desc_error| ExecuteError::WriteIo {
length: data_len, length: data_len,
sector, sector,
desc_error, desc_error,
})?; })?;
if actual_length < data_len {
return Err(ExecuteError::ShortWrite {
sector,
expected_length: data_len,
actual_length,
});
}
if !*flush_timer_armed { if !*flush_timer_armed {
flush_timer flush_timer
.reset(flush_delay, None) .reset(flush_delay, None)

View file

@ -309,6 +309,32 @@ impl<'a> Reader<'a> {
Ok(()) Ok(())
} }
pub fn read_exact_to_at<F: FileReadWriteAtVolatile>(
&mut self,
mut dst: F,
mut count: usize,
mut off: u64,
) -> io::Result<()> {
while count > 0 {
match self.read_to_at(&mut dst, count, off) {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
}
Ok(n) => {
count -= n;
off += n as u64;
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}
/// Returns number of bytes available for reading. May return an error if the combined /// Returns number of bytes available for reading. May return an error if the combined
/// lengths of all the buffers in the DescriptorChain would cause an integer overflow. /// lengths of all the buffers in the DescriptorChain would cause an integer overflow.
pub fn available_bytes(&self) -> usize { pub fn available_bytes(&self) -> usize {
@ -460,6 +486,31 @@ impl<'a> Writer<'a> {
Ok(()) Ok(())
} }
pub fn write_all_from_at<F: FileReadWriteAtVolatile>(
&mut self,
mut src: F,
mut count: usize,
mut off: u64,
) -> io::Result<()> {
while count > 0 {
match self.write_from_at(&mut src, count, off) {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::WriteZero,
"failed to write whole buffer",
))
}
Ok(n) => {
count -= n;
off += n as u64;
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}
/// Returns number of bytes already written to the descriptor chain buffer. /// Returns number of bytes already written to the descriptor chain buffer.
pub fn bytes_written(&self) -> usize { pub fn bytes_written(&self) -> usize {
self.buffer.bytes_consumed() self.buffer.bytes_consumed()