Skip to content

Commit 981e88f

Browse files
committed
fix: patching the path of Async block devices
The asynchronous engine maintains an event file descriptor which passes to the IO uring interface when creating a new ring. IO uring uses this EventFd to notify us about completion of IO requests. When we PATCH an async block device, we create a new asynchronous engine, including a new EventFd. However, we still monitor the old EventFd. This breaks the use of async drives post PATCH requests, because we never get notified about the results of requests we submit to the IO uring engine. This commit changes the implementation along the PATCH code path, to reuse the previous EventFd for the asynchronous engine. Signed-off-by: Babis Chalios <[email protected]>
1 parent f529b3f commit 981e88f

File tree

4 files changed

+74
-19
lines changed

4 files changed

+74
-19
lines changed

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

+38-11
Original file line numberDiff line numberDiff line change
@@ -66,24 +66,26 @@ impl FileEngineType {
6666
pub struct DiskProperties {
6767
pub file_path: String,
6868
pub file_engine: FileEngine<PendingRequest>,
69+
pub is_disk_read_only: bool,
6970
pub nsectors: u64,
7071
pub image_id: [u8; VIRTIO_BLK_ID_BYTES as usize],
7172
}
7273

7374
impl DiskProperties {
74-
pub fn new(
75-
disk_image_path: String,
76-
is_disk_read_only: bool,
77-
file_engine_type: FileEngineType,
78-
) -> Result<Self, VirtioBlockError> {
79-
let mut disk_image = OpenOptions::new()
75+
// Helper function that opens the file with the proper access permissions
76+
fn open_file(disk_image_path: &str, is_disk_read_only: bool) -> Result<File, VirtioBlockError> {
77+
OpenOptions::new()
8078
.read(true)
8179
.write(!is_disk_read_only)
8280
.open(PathBuf::from(&disk_image_path))
83-
.map_err(|x| VirtioBlockError::BackingFile(x, disk_image_path.clone()))?;
81+
.map_err(|x| VirtioBlockError::BackingFile(x, disk_image_path.to_string()))
82+
}
83+
84+
// Helper function that gets the size of the file
85+
fn file_size(disk_image_path: &str, disk_image: &mut File) -> Result<u64, VirtioBlockError> {
8486
let disk_size = disk_image
8587
.seek(SeekFrom::End(0))
86-
.map_err(|x| VirtioBlockError::BackingFile(x, disk_image_path.clone()))?;
88+
.map_err(|x| VirtioBlockError::BackingFile(x, disk_image_path.to_string()))?;
8789

8890
// We only support disk size, which uses the first two words of the configuration space.
8991
// If the image is not a multiple of the sector size, the tail bits are not exposed.
@@ -95,17 +97,44 @@ impl DiskProperties {
9597
);
9698
}
9799

100+
Ok(disk_size)
101+
}
102+
103+
/// Create a new file for the block device using a FileEngine
104+
pub fn new(
105+
disk_image_path: String,
106+
is_disk_read_only: bool,
107+
file_engine_type: FileEngineType,
108+
) -> Result<Self, VirtioBlockError> {
109+
let mut disk_image = Self::open_file(&disk_image_path, is_disk_read_only)?;
110+
let disk_size = Self::file_size(&disk_image_path, &mut disk_image)?;
98111
let image_id = Self::build_disk_image_id(&disk_image);
99112

100113
Ok(Self {
101114
file_path: disk_image_path,
102115
file_engine: FileEngine::from_file(disk_image, file_engine_type)
103116
.map_err(VirtioBlockError::FileEngine)?,
117+
is_disk_read_only,
104118
nsectors: disk_size >> SECTOR_SHIFT,
105119
image_id,
106120
})
107121
}
108122

123+
/// Update the path to the file backing the block device
124+
pub fn update(&mut self, disk_image_path: String) -> Result<(), VirtioBlockError> {
125+
let mut disk_image = Self::open_file(&disk_image_path, self.is_disk_read_only)?;
126+
let disk_size = Self::file_size(&disk_image_path, &mut disk_image)?;
127+
128+
self.image_id = Self::build_disk_image_id(&disk_image);
129+
self.file_engine
130+
.update_file_path(disk_image)
131+
.map_err(VirtioBlockError::FileEngine)?;
132+
self.nsectors = disk_size >> SECTOR_SHIFT;
133+
self.file_path = disk_image_path;
134+
135+
Ok(())
136+
}
137+
109138
fn build_device_id(disk_file: &File) -> Result<String, VirtioBlockError> {
110139
let blk_metadata = disk_file
111140
.metadata()
@@ -506,9 +535,7 @@ impl VirtioBlock {
506535

507536
/// Update the backing file and the config space of the block device.
508537
pub fn update_disk_image(&mut self, disk_image_path: String) -> Result<(), VirtioBlockError> {
509-
let disk_properties =
510-
DiskProperties::new(disk_image_path, self.read_only, self.file_engine_type())?;
511-
self.disk = disk_properties;
538+
self.disk.update(disk_image_path)?;
512539
self.config_space = self.disk.virtio_block_config_space();
513540

514541
// Kick the driver to pick up the changes.

src/vmm/src/devices/virtio/virtio_block/io/async_io.rs

+22-8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use std::fmt::Debug;
55
use std::fs::File;
66
use std::marker::PhantomData;
7+
use std::os::fd::RawFd;
78
use std::os::unix::io::AsRawFd;
89

910
use utils::eventfd::EventFd;
@@ -13,7 +14,7 @@ use crate::devices::virtio::virtio_block::io::UserDataError;
1314
use crate::devices::virtio::virtio_block::IO_URING_NUM_ENTRIES;
1415
use crate::io_uring::operation::{Cqe, OpCode, Operation};
1516
use crate::io_uring::restriction::Restriction;
16-
use crate::io_uring::{IoUring, IoUringError};
17+
use crate::io_uring::{self, IoUring, IoUringError};
1718
use crate::logger::log_dev_preview_warning;
1819
use crate::vstate::memory::{GuestAddress, GuestMemory, GuestMemoryExtension, GuestMemoryMmap};
1920

@@ -66,11 +67,8 @@ impl<T: Debug> WrappedUserData<T> {
6667
}
6768

6869
impl<T: Debug> AsyncFileEngine<T> {
69-
pub fn from_file(file: File) -> Result<AsyncFileEngine<T>, AsyncIoError> {
70-
log_dev_preview_warning("Async file IO", Option::None);
71-
72-
let completion_evt = EventFd::new(libc::EFD_NONBLOCK).map_err(AsyncIoError::EventFd)?;
73-
let ring = IoUring::new(
70+
fn new_ring(file: &File, completion_fd: RawFd) -> Result<IoUring, io_uring::IoUringError> {
71+
IoUring::new(
7472
u32::from(IO_URING_NUM_ENTRIES),
7573
vec![&file],
7674
vec![
@@ -81,9 +79,16 @@ impl<T: Debug> AsyncFileEngine<T> {
8179
Restriction::AllowOpCode(OpCode::Write),
8280
Restriction::AllowOpCode(OpCode::Fsync),
8381
],
84-
Some(completion_evt.as_raw_fd()),
82+
Some(completion_fd),
8583
)
86-
.map_err(AsyncIoError::IoUring)?;
84+
}
85+
86+
pub fn from_file(file: File) -> Result<AsyncFileEngine<T>, AsyncIoError> {
87+
log_dev_preview_warning("Async file IO", Option::None);
88+
89+
let completion_evt = EventFd::new(libc::EFD_NONBLOCK).map_err(AsyncIoError::EventFd)?;
90+
let ring =
91+
Self::new_ring(&file, completion_evt.as_raw_fd()).map_err(AsyncIoError::IoUring)?;
8792

8893
Ok(AsyncFileEngine {
8994
file,
@@ -93,6 +98,15 @@ impl<T: Debug> AsyncFileEngine<T> {
9398
})
9499
}
95100

101+
pub fn update_file(&mut self, file: File) -> Result<(), AsyncIoError> {
102+
let ring = Self::new_ring(&file, self.completion_evt.as_raw_fd())
103+
.map_err(AsyncIoError::IoUring)?;
104+
105+
self.file = file;
106+
self.ring = ring;
107+
Ok(())
108+
}
109+
96110
#[cfg(test)]
97111
pub fn file(&self) -> &File {
98112
&self.file

src/vmm/src/devices/virtio/virtio_block/io/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ impl<T: Debug> FileEngine<T> {
7474
}
7575
}
7676

77+
pub fn update_file_path(&mut self, file: File) -> Result<(), BlockIoError> {
78+
match self {
79+
FileEngine::Async(engine) => engine.update_file(file).map_err(BlockIoError::Async)?,
80+
FileEngine::Sync(engine) => engine.update_file(file),
81+
};
82+
83+
Ok(())
84+
}
85+
7786
#[cfg(test)]
7887
pub fn file(&self) -> &File {
7988
match self {

src/vmm/src/devices/virtio/virtio_block/io/sync_io.rs

+5
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ impl SyncFileEngine {
3434
&self.file
3535
}
3636

37+
/// Update the backing file of the engine
38+
pub fn update_file(&mut self, file: File) {
39+
self.file = file
40+
}
41+
3742
pub fn read(
3843
&mut self,
3944
offset: u64,

0 commit comments

Comments
 (0)