Skip to content

Commit 7abce01

Browse files
elainewu1222snue
authored andcommitted
virtio-devices: support event idx for virtio-blk
Support event idx feature for virtio-blk device. This feature could improve disk IO performance by suppressing notifications from guest to host and interrupts from host to guest, which has been already supported in virtio-net and vhost-user devices. To achieve this, virtqueue's event-idx-related API is leveraged for avail_event field update and needs_notification check. Fixes: cloud-hypervisor#6580 Signed-off-by: wuxinyue <wuxinyue.wxy@antgroup.com> (cherry picked from commit a243870) Signed-off-by: Stefan Nürnberger <stefan.nuernberger@cyberus-technology.de>
1 parent ea7d9d6 commit 7abce01

File tree

1 file changed

+43
-24
lines changed

1 file changed

+43
-24
lines changed

virtio-devices/src/block.rs

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use versionize::{VersionMap, Versionize, VersionizeResult};
4040
use versionize_derive::Versionize;
4141
use virtio_bindings::virtio_blk::*;
4242
use virtio_bindings::virtio_config::*;
43+
use virtio_bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
4344
use virtio_queue::{Queue, QueueOwnedT, QueueT};
4445
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryError};
4546
use vm_migration::VersionMapped;
@@ -80,6 +81,8 @@ pub enum Error {
8081
QueueIterator(virtio_queue::Error),
8182
#[error("Failed to update request status: {0}")]
8283
RequestStatus(GuestMemoryError),
84+
#[error("Failed to enable notification: {0}")]
85+
QueueEnableNotification(virtio_queue::Error),
8386
}
8487

8588
pub type Result<T> = result::Result<T, Error>;
@@ -137,11 +140,9 @@ struct BlockEpollHandler {
137140
}
138141

139142
impl BlockEpollHandler {
140-
fn process_queue_submit(&mut self) -> Result<bool> {
143+
fn process_queue_submit(&mut self) -> Result<()> {
141144
let queue = &mut self.queue;
142145

143-
let mut used_descs = false;
144-
145146
while let Some(mut desc_chain) = queue.pop_descriptor_chain(self.mem.memory()) {
146147
let mut request = Request::parse(&mut desc_chain, self.access_platform.as_ref())
147148
.map_err(Error::RequestParsing)?;
@@ -163,7 +164,9 @@ impl BlockEpollHandler {
163164
queue
164165
.add_used(desc_chain.memory(), desc_chain.head_index(), 0)
165166
.map_err(Error::QueueAddUsed)?;
166-
used_descs = true;
167+
queue
168+
.enable_notification(self.mem.memory().deref())
169+
.map_err(Error::QueueEnableNotification)?;
167170
continue;
168171
}
169172

@@ -223,23 +226,34 @@ impl BlockEpollHandler {
223226
queue
224227
.add_used(desc_chain.memory(), desc_chain.head_index(), 0)
225228
.map_err(Error::QueueAddUsed)?;
226-
used_descs = true;
229+
queue
230+
.enable_notification(self.mem.memory().deref())
231+
.map_err(Error::QueueEnableNotification)?;
227232
}
228233
}
229234

230-
Ok(used_descs)
235+
Ok(())
231236
}
232237

233238
fn process_queue_submit_and_signal(&mut self) -> result::Result<(), EpollHelperError> {
234-
let needs_notification = self.process_queue_submit().map_err(|e| {
239+
self.process_queue_submit().map_err(|e| {
235240
EpollHelperError::HandleEvent(anyhow!("Failed to process queue (submit): {:?}", e))
236241
})?;
237242

238-
if needs_notification {
243+
if self
244+
.queue
245+
.needs_notification(self.mem.memory().deref())
246+
.map_err(|e| {
247+
EpollHelperError::HandleEvent(anyhow!(
248+
"Failed to check needs_notification: {:?}",
249+
e
250+
))
251+
})?
252+
{
239253
self.signal_used_queue().map_err(|e| {
240254
EpollHelperError::HandleEvent(anyhow!("Failed to signal used queue: {:?}", e))
241-
})?
242-
};
255+
})?;
256+
}
243257

244258
Ok(())
245259
}
@@ -265,8 +279,7 @@ impl BlockEpollHandler {
265279
Err(Error::MissingEntryRequestList)
266280
}
267281

268-
fn process_queue_complete(&mut self) -> Result<bool> {
269-
let mut used_descs = false;
282+
fn process_queue_complete(&mut self) -> Result<()> {
270283
let mem = self.mem.memory();
271284
let mut read_bytes = Wrapping(0);
272285
let mut write_bytes = Wrapping(0);
@@ -379,7 +392,9 @@ impl BlockEpollHandler {
379392
queue
380393
.add_used(mem.deref(), desc_index, len)
381394
.map_err(Error::QueueAddUsed)?;
382-
used_descs = true;
395+
queue
396+
.enable_notification(mem.deref())
397+
.map_err(Error::QueueEnableNotification)?;
383398
}
384399

385400
self.counters
@@ -396,7 +411,7 @@ impl BlockEpollHandler {
396411
.read_ops
397412
.fetch_add(read_ops.0, Ordering::AcqRel);
398413

399-
Ok(used_descs)
414+
Ok(())
400415
}
401416

402417
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
@@ -451,20 +466,19 @@ impl EpollHelperHandler for BlockEpollHandler {
451466
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
452467
})?;
453468

454-
let needs_notification = self.process_queue_complete().map_err(|e| {
469+
self.process_queue_complete().map_err(|e| {
455470
EpollHelperError::HandleEvent(anyhow!(
456471
"Failed to process queue (complete): {:?}",
457472
e
458473
))
459474
})?;
460475

461-
if needs_notification {
462-
self.signal_used_queue().map_err(|e| {
463-
EpollHelperError::HandleEvent(anyhow!(
464-
"Failed to signal used queue: {:?}",
465-
e
466-
))
467-
})?;
476+
let rate_limit_reached =
477+
self.rate_limiter.as_ref().map_or(false, |r| r.is_blocked());
478+
479+
// Process the queue only when the rate limit is not reached
480+
if !rate_limit_reached {
481+
self.process_queue_submit_and_signal()?
468482
}
469483
}
470484
RATE_LIMITER_EVENT => {
@@ -570,7 +584,8 @@ impl Block {
570584
| (1u64 << VIRTIO_BLK_F_FLUSH)
571585
| (1u64 << VIRTIO_BLK_F_CONFIG_WCE)
572586
| (1u64 << VIRTIO_BLK_F_BLK_SIZE)
573-
| (1u64 << VIRTIO_BLK_F_TOPOLOGY);
587+
| (1u64 << VIRTIO_BLK_F_TOPOLOGY)
588+
| (1u64 << VIRTIO_RING_F_EVENT_IDX);
574589

575590
if iommu {
576591
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
@@ -742,8 +757,12 @@ impl VirtioDevice for Block {
742757
self.update_writeback();
743758

744759
let mut epoll_threads = Vec::new();
760+
let event_idx = self.common.feature_acked(VIRTIO_RING_F_EVENT_IDX.into());
761+
745762
for i in 0..queues.len() {
746-
let (_, queue, queue_evt) = queues.remove(0);
763+
let (_, mut queue, queue_evt) = queues.remove(0);
764+
queue.set_event_idx(event_idx);
765+
747766
let queue_size = queue.size();
748767
let (kill_evt, pause_evt) = self.common.dup_eventfds();
749768

0 commit comments

Comments
 (0)