Skip to content

Commit 0e29058

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 29a9aec commit 0e29058

File tree

4 files changed

+77
-20
lines changed

4 files changed

+77
-20
lines changed

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

+40-11
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,20 @@ pub struct DiskProperties {
7171
}
7272

7373
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()
74+
// Helper function that opens the file with the proper access permissions
75+
fn open_file(disk_image_path: &str, is_disk_read_only: bool) -> Result<File, VirtioBlockError> {
76+
OpenOptions::new()
8077
.read(true)
8178
.write(!is_disk_read_only)
8279
.open(PathBuf::from(&disk_image_path))
83-
.map_err(|x| VirtioBlockError::BackingFile(x, disk_image_path.clone()))?;
80+
.map_err(|x| VirtioBlockError::BackingFile(x, disk_image_path.to_string()))
81+
}
82+
83+
// Helper function that gets the size of the file
84+
fn file_size(disk_image_path: &str, disk_image: &mut File) -> Result<u64, VirtioBlockError> {
8485
let disk_size = disk_image
8586
.seek(SeekFrom::End(0))
86-
.map_err(|x| VirtioBlockError::BackingFile(x, disk_image_path.clone()))?;
87+
.map_err(|x| VirtioBlockError::BackingFile(x, disk_image_path.to_string()))?;
8788

8889
// We only support disk size, which uses the first two words of the configuration space.
8990
// If the image is not a multiple of the sector size, the tail bits are not exposed.
@@ -95,6 +96,17 @@ impl DiskProperties {
9596
);
9697
}
9798

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

100112
Ok(Self {
@@ -106,6 +118,25 @@ impl DiskProperties {
106118
})
107119
}
108120

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

507538
/// Update the backing file and the config space of the block device.
508539
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;
540+
self.disk.update(disk_image_path, self.read_only)?;
512541
self.config_space = self.disk.virtio_block_config_space();
513542

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

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

+23-9
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,13 +67,10 @@ 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),
75-
vec![&file],
73+
vec![file],
7674
vec![
7775
// Make sure we only allow operations on pre-registered fds.
7876
Restriction::RequireFixedFds,
@@ -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)