Skip to content

Commit b0e110b

Browse files
ShadowCursezulinx86
authored andcommitted
refactor(virtio-block): use a type for config space
Instead of using a vector of bytes as a config space it is better to use a proper type. This was already done in the virtio-net device, so just repeat same in block device. Signed-off-by: Egor Lazarchuk <yegorlz@amazon.co.uk>
1 parent 44a50b4 commit b0e110b

File tree

3 files changed

+58
-61
lines changed

3 files changed

+58
-61
lines changed

src/vmm/src/devices/virtio/block/virtio/device.rs

Lines changed: 52 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,19 @@
88
use std::cmp;
99
use std::convert::From;
1010
use std::fs::{File, OpenOptions};
11-
use std::io::{Seek, SeekFrom, Write};
11+
use std::io::{Seek, SeekFrom};
1212
use std::os::linux::fs::MetadataExt;
1313
use std::path::PathBuf;
1414
use std::sync::Arc;
1515

1616
use block_io::FileEngine;
1717
use serde::{Deserialize, Serialize};
18+
use vm_memory::ByteValued;
1819
use vmm_sys_util::eventfd::EventFd;
1920

2021
use super::io::async_io;
2122
use super::request::*;
22-
use super::{
23-
io as block_io, VirtioBlockError, BLOCK_CONFIG_SPACE_SIZE, BLOCK_QUEUE_SIZES, SECTOR_SHIFT,
24-
SECTOR_SIZE,
25-
};
23+
use super::{io as block_io, VirtioBlockError, BLOCK_QUEUE_SIZES, SECTOR_SHIFT, SECTOR_SIZE};
2624
use crate::devices::virtio::block::virtio::metrics::{BlockDeviceMetrics, BlockMetricsPerDevice};
2725
use crate::devices::virtio::block::CacheType;
2826
use crate::devices::virtio::device::{DeviceState, IrqTrigger, IrqType, VirtioDevice};
@@ -155,20 +153,17 @@ impl DiskProperties {
155153
}
156154
default_id
157155
}
156+
}
158157

159-
/// Provides vec containing the virtio block configuration space
160-
/// buffer. The config space is populated with the disk size based
161-
/// on the backing file size.
162-
pub fn virtio_block_config_space(&self) -> Vec<u8> {
163-
// The config space is little endian.
164-
let mut config = Vec::with_capacity(BLOCK_CONFIG_SPACE_SIZE);
165-
for i in 0..BLOCK_CONFIG_SPACE_SIZE {
166-
config.push(((self.nsectors >> (8 * i)) & 0xff) as u8);
167-
}
168-
config
169-
}
158+
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
159+
#[repr(C)]
160+
pub struct ConfigSpace {
161+
pub capacity: u64,
170162
}
171163

164+
// SAFETY: `ConfigSpace` contains only PODs in `repr(C)` or `repr(transparent)`, without padding.
165+
unsafe impl ByteValued for ConfigSpace {}
166+
172167
/// Use this structure to set up the Block Device before booting the kernel.
173168
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
174169
#[serde(deny_unknown_fields)]
@@ -246,7 +241,7 @@ pub struct VirtioBlock {
246241
// Virtio fields.
247242
pub avail_features: u64,
248243
pub acked_features: u64,
249-
pub config_space: Vec<u8>,
244+
pub config_space: ConfigSpace,
250245
pub activate_evt: EventFd,
251246

252247
// Transport related fields.
@@ -313,10 +308,14 @@ impl VirtioBlock {
313308

314309
let queues = BLOCK_QUEUE_SIZES.iter().map(|&s| Queue::new(s)).collect();
315310

311+
let config_space = ConfigSpace {
312+
capacity: disk_properties.nsectors.to_le(),
313+
};
314+
316315
Ok(VirtioBlock {
317316
avail_features,
318317
acked_features: 0u64,
319-
config_space: disk_properties.virtio_block_config_space(),
318+
config_space,
320319
activate_evt: EventFd::new(libc::EFD_NONBLOCK).map_err(VirtioBlockError::EventFd)?,
321320

322321
queues,
@@ -524,7 +523,7 @@ impl VirtioBlock {
524523
/// Update the backing file and the config space of the block device.
525524
pub fn update_disk_image(&mut self, disk_image_path: String) -> Result<(), VirtioBlockError> {
526525
self.disk.update(disk_image_path, self.read_only)?;
527-
self.config_space = self.disk.virtio_block_config_space();
526+
self.config_space.capacity = self.disk.nsectors.to_le(); // virtio_block_config_space();
528527

529528
// Kick the driver to pick up the changes.
530529
self.irq_trigger.trigger_irq(IrqType::Config).unwrap();
@@ -598,28 +597,23 @@ impl VirtioDevice for VirtioBlock {
598597
&self.irq_trigger
599598
}
600599

601-
fn read_config(&self, offset: u64, mut data: &mut [u8]) {
602-
let config_len = self.config_space.len() as u64;
603-
if offset >= config_len {
600+
fn read_config(&self, offset: u64, data: &mut [u8]) {
601+
if let Some(config_space_bytes) = self.config_space.as_slice().get(u64_to_usize(offset)..) {
602+
let len = config_space_bytes.len().min(data.len());
603+
data[..len].copy_from_slice(&config_space_bytes[..len]);
604+
} else {
604605
error!("Failed to read config space");
605606
self.metrics.cfg_fails.inc();
606-
return;
607-
}
608-
if let Some(end) = offset.checked_add(data.len() as u64) {
609-
// This write can't fail, offset and end are checked against config_len.
610-
data.write_all(
611-
&self.config_space[u64_to_usize(offset)..u64_to_usize(cmp::min(end, config_len))],
612-
)
613-
.unwrap();
614607
}
615608
}
616609

617610
fn write_config(&mut self, offset: u64, data: &[u8]) {
611+
let config_space_bytes = self.config_space.as_mut_slice();
618612
let start = usize::try_from(offset).ok();
619613
let end = start.and_then(|s| s.checked_add(data.len()));
620614
let Some(dst) = start
621615
.zip(end)
622-
.and_then(|(start, end)| self.config_space.get_mut(start..end))
616+
.and_then(|(start, end)| config_space_bytes.get_mut(start..end))
623617
else {
624618
error!("Failed to write config space");
625619
self.metrics.cfg_fails.inc();
@@ -673,7 +667,7 @@ impl Drop for VirtioBlock {
673667
#[cfg(test)]
674668
mod tests {
675669
use std::fs::metadata;
676-
use std::io::Read;
670+
use std::io::{Read, Write};
677671
use std::os::unix::ffi::OsStrExt;
678672
use std::thread;
679673
use std::time::Duration;
@@ -755,11 +749,6 @@ mod tests {
755749

756750
assert_eq!(size, u64::from(SECTOR_SIZE) * num_sectors);
757751
assert_eq!(disk_properties.nsectors, num_sectors);
758-
let cfg = disk_properties.virtio_block_config_space();
759-
assert_eq!(cfg.len(), BLOCK_CONFIG_SPACE_SIZE);
760-
for (i, byte) in cfg.iter().enumerate() {
761-
assert_eq!(*byte, ((num_sectors >> (8 * i)) & 0xff) as u8);
762-
}
763752
// Testing `backing_file.virtio_block_disk_image_id()` implies
764753
// duplicating that logic in tests, so skipping it.
765754

@@ -803,20 +792,21 @@ mod tests {
803792
for engine in [FileEngineType::Sync, FileEngineType::Async] {
804793
let block = default_block(engine);
805794

806-
let mut actual_config_space = [0u8; BLOCK_CONFIG_SPACE_SIZE];
807-
block.read_config(0, &mut actual_config_space);
795+
let mut actual_config_space = ConfigSpace::default();
796+
block.read_config(0, actual_config_space.as_mut_slice());
808797
// This will read the number of sectors.
809798
// The block's backing file size is 0x1000, so there are 8 (4096/512) sectors.
810799
// The config space is little endian.
811-
let expected_config_space: [u8; BLOCK_CONFIG_SPACE_SIZE] =
812-
[0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
800+
let expected_config_space = ConfigSpace { capacity: 8 };
813801
assert_eq!(actual_config_space, expected_config_space);
814802

815803
// Invalid read.
816-
let expected_config_space: [u8; BLOCK_CONFIG_SPACE_SIZE] =
817-
[0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf];
804+
let expected_config_space = ConfigSpace { capacity: 696969 };
818805
actual_config_space = expected_config_space;
819-
block.read_config(BLOCK_CONFIG_SPACE_SIZE as u64 + 1, &mut actual_config_space);
806+
block.read_config(
807+
std::mem::size_of::<ConfigSpace>() as u64 + 1,
808+
actual_config_space.as_mut_slice(),
809+
);
820810

821811
// Validate read failed (the config space was not updated).
822812
assert_eq!(actual_config_space, expected_config_space);
@@ -828,33 +818,37 @@ mod tests {
828818
for engine in [FileEngineType::Sync, FileEngineType::Async] {
829819
let mut block = default_block(engine);
830820

831-
let expected_config_space: [u8; BLOCK_CONFIG_SPACE_SIZE] =
832-
[0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
833-
block.write_config(0, &expected_config_space);
821+
let expected_config_space = ConfigSpace { capacity: 696969 };
822+
block.write_config(0, expected_config_space.as_slice());
834823

835-
let mut actual_config_space = [0u8; BLOCK_CONFIG_SPACE_SIZE];
836-
block.read_config(0, &mut actual_config_space);
824+
let mut actual_config_space = ConfigSpace::default();
825+
block.read_config(0, actual_config_space.as_mut_slice());
837826
assert_eq!(actual_config_space, expected_config_space);
838827

839828
// If priviledged user writes to `/dev/mem`, in block config space - byte by byte.
840-
let expected_config_space = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x00, 0x11];
841-
for i in 0..expected_config_space.len() {
842-
block.write_config(i as u64, &expected_config_space[i..=i]);
829+
let expected_config_space = ConfigSpace {
830+
capacity: 0x1122334455667788,
831+
};
832+
let expected_config_space_slice = expected_config_space.as_slice();
833+
for (i, b) in expected_config_space_slice.iter().enumerate() {
834+
block.write_config(i as u64, &[*b]);
843835
}
844-
block.read_config(0, &mut actual_config_space);
836+
block.read_config(0, actual_config_space.as_mut_slice());
845837
assert_eq!(actual_config_space, expected_config_space);
846838

847839
// Invalid write.
848-
let new_config_space = [0xd, 0xe, 0xa, 0xd, 0xb, 0xe, 0xe, 0xf];
849-
block.write_config(5, &new_config_space);
840+
let new_config_space = ConfigSpace {
841+
capacity: 0xDEADBEEF,
842+
};
843+
block.write_config(5, new_config_space.as_slice());
850844
// Make sure nothing got written.
851-
block.read_config(0, &mut actual_config_space);
845+
block.read_config(0, actual_config_space.as_mut_slice());
852846
assert_eq!(actual_config_space, expected_config_space);
853847

854848
// Large offset that may cause an overflow.
855-
block.write_config(u64::MAX, &new_config_space);
849+
block.write_config(u64::MAX, new_config_space.as_slice());
856850
// Make sure nothing got written.
857-
block.read_config(0, &mut actual_config_space);
851+
block.read_config(0, actual_config_space.as_mut_slice());
858852
assert_eq!(actual_config_space, expected_config_space);
859853
}
860854
}

src/vmm/src/devices/virtio/block/virtio/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ pub use self::request::*;
1818
pub use crate::devices::virtio::block::CacheType;
1919
use crate::devices::virtio::queue::FIRECRACKER_MAX_QUEUE_SIZE;
2020

21-
/// Size of config space for block device.
22-
pub const BLOCK_CONFIG_SPACE_SIZE: usize = 8;
2321
/// Sector shift for block device.
2422
pub const SECTOR_SHIFT: u8 = 9;
2523
/// Size of block sector.

src/vmm/src/devices/virtio/block/virtio/persist.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use std::sync::atomic::AtomicU32;
77
use std::sync::Arc;
88

9+
use device::ConfigSpace;
910
use serde::{Deserialize, Serialize};
1011
use vmm_sys_util::eventfd::EventFd;
1112

@@ -122,10 +123,14 @@ impl Persist<'_> for VirtioBlock {
122123
DeviceState::Inactive
123124
};
124125

126+
let config_space = ConfigSpace {
127+
capacity: disk_properties.nsectors.to_le(),
128+
};
129+
125130
Ok(VirtioBlock {
126131
avail_features,
127132
acked_features,
128-
config_space: disk_properties.virtio_block_config_space(),
133+
config_space,
129134
activate_evt: EventFd::new(libc::EFD_NONBLOCK).map_err(VirtioBlockError::EventFd)?,
130135

131136
queues,

0 commit comments

Comments
 (0)