From 2f00d887e7f717ed7a95dc2f41c9045a26e455c2 Mon Sep 17 00:00:00 2001 From: longjin Date: Mon, 4 May 2026 13:45:41 +0800 Subject: [PATCH] feat(blk): add non-blocking flush support for VirtIO block device - Add`supports_flush()`method to check device flush capability - Implement`flush_nb()`for non-blocking flush submission - Implement`complete_flush()`to finalize flush operations - Fix missing lifetime annotation in`PciRoot::capabilities()` Signed-off-by: longjin --- src/device/blk.rs | 51 ++++++++++++++++++++++++++++++++++++++++ src/transport/pci/bus.rs | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/device/blk.rs b/src/device/blk.rs index cbd2dcc..138ce9a 100644 --- a/src/device/blk.rs +++ b/src/device/blk.rs @@ -153,6 +153,57 @@ impl VirtIOBlk { } } + /// Returns true if the device supports flushing pending writes to storage. + pub fn supports_flush(&self) -> bool { + self.negotiated_features.contains(BlkFeature::FLUSH) + } + + /// Submits a flush request, but returns immediately without waiting for it to complete. + /// + /// Returns `Ok(None)` if the device doesn't support `VIRTIO_BLK_F_FLUSH`, matching + /// [`Self::flush`]'s no-op behavior in that case. + /// + /// # Safety + /// + /// `req` and `resp` are still borrowed by the underlying VirtIO block device even after this + /// method returns `Some(token)`. Thus, it is the caller's responsibility to guarantee that they + /// are not accessed before the request is completed in order to avoid data races. + pub unsafe fn flush_nb(&mut self, req: &mut BlkReq, resp: &mut BlkResp) -> Result> { + if !self.supports_flush() { + return Ok(None); + } + + *req = BlkReq { + type_: ReqType::Flush, + ..Default::default() + }; + let token = self + .queue + .add(&[req.as_bytes()], &mut [resp.as_bytes_mut()])?; + if self.queue.should_notify() { + self.transport.notify(QUEUE); + } + + Ok(Some(token)) + } + + /// Completes a flush operation which was started by [`Self::flush_nb`]. + /// + /// # Safety + /// + /// The same buffers must be passed in again as were passed to [`Self::flush_nb`] when it + /// returned the token. + pub unsafe fn complete_flush( + &mut self, + token: u16, + req: &BlkReq, + resp: &mut BlkResp, + ) -> Result<()> { + self.queue + .pop_used(token, &[req.as_bytes()], &mut [resp.as_bytes_mut()])?; + resp.status.into() + } + /// Gets the device ID. /// /// The ID is written as ASCII into the given buffer, which must be 20 bytes long, and the used diff --git a/src/transport/pci/bus.rs b/src/transport/pci/bus.rs index 0a3014b..4fbf808 100644 --- a/src/transport/pci/bus.rs +++ b/src/transport/pci/bus.rs @@ -238,7 +238,7 @@ impl PciRoot { } /// Gets an iterator over the capabilities of the given device function. - pub fn capabilities(&self, device_function: DeviceFunction) -> CapabilityIterator { + pub fn capabilities(&self, device_function: DeviceFunction) -> CapabilityIterator<'_> { CapabilityIterator { root: self, device_function,