mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-09-18 21:11:00 +00:00
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: #6580 Signed-off-by: wuxinyue <wuxinyue.wxy@antgroup.com>
This commit is contained in:
parent
bb4af57219
commit
a2438700e4
@ -40,6 +40,7 @@ use std::sync::{Arc, Barrier};
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use virtio_bindings::virtio_blk::*;
|
use virtio_bindings::virtio_blk::*;
|
||||||
use virtio_bindings::virtio_config::*;
|
use virtio_bindings::virtio_config::*;
|
||||||
|
use virtio_bindings::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
|
||||||
use virtio_queue::{Queue, QueueOwnedT, QueueT};
|
use virtio_queue::{Queue, QueueOwnedT, QueueT};
|
||||||
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryError};
|
use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryError};
|
||||||
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
|
||||||
@ -79,6 +80,8 @@ pub enum Error {
|
|||||||
QueueIterator(virtio_queue::Error),
|
QueueIterator(virtio_queue::Error),
|
||||||
#[error("Failed to update request status: {0}")]
|
#[error("Failed to update request status: {0}")]
|
||||||
RequestStatus(GuestMemoryError),
|
RequestStatus(GuestMemoryError),
|
||||||
|
#[error("Failed to enable notification: {0}")]
|
||||||
|
QueueEnableNotification(virtio_queue::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
@ -137,11 +140,9 @@ struct BlockEpollHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BlockEpollHandler {
|
impl BlockEpollHandler {
|
||||||
fn process_queue_submit(&mut self) -> Result<bool> {
|
fn process_queue_submit(&mut self) -> Result<()> {
|
||||||
let queue = &mut self.queue;
|
let queue = &mut self.queue;
|
||||||
|
|
||||||
let mut used_descs = false;
|
|
||||||
|
|
||||||
while let Some(mut desc_chain) = queue.pop_descriptor_chain(self.mem.memory()) {
|
while let Some(mut desc_chain) = queue.pop_descriptor_chain(self.mem.memory()) {
|
||||||
let mut request = Request::parse(&mut desc_chain, self.access_platform.as_ref())
|
let mut request = Request::parse(&mut desc_chain, self.access_platform.as_ref())
|
||||||
.map_err(Error::RequestParsing)?;
|
.map_err(Error::RequestParsing)?;
|
||||||
@ -163,7 +164,9 @@ impl BlockEpollHandler {
|
|||||||
queue
|
queue
|
||||||
.add_used(desc_chain.memory(), desc_chain.head_index(), 0)
|
.add_used(desc_chain.memory(), desc_chain.head_index(), 0)
|
||||||
.map_err(Error::QueueAddUsed)?;
|
.map_err(Error::QueueAddUsed)?;
|
||||||
used_descs = true;
|
queue
|
||||||
|
.enable_notification(self.mem.memory().deref())
|
||||||
|
.map_err(Error::QueueEnableNotification)?;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,23 +226,34 @@ impl BlockEpollHandler {
|
|||||||
queue
|
queue
|
||||||
.add_used(desc_chain.memory(), desc_chain.head_index(), 0)
|
.add_used(desc_chain.memory(), desc_chain.head_index(), 0)
|
||||||
.map_err(Error::QueueAddUsed)?;
|
.map_err(Error::QueueAddUsed)?;
|
||||||
used_descs = true;
|
queue
|
||||||
|
.enable_notification(self.mem.memory().deref())
|
||||||
|
.map_err(Error::QueueEnableNotification)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(used_descs)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_queue_submit_and_signal(&mut self) -> result::Result<(), EpollHelperError> {
|
fn process_queue_submit_and_signal(&mut self) -> result::Result<(), EpollHelperError> {
|
||||||
let needs_notification = self.process_queue_submit().map_err(|e| {
|
self.process_queue_submit().map_err(|e| {
|
||||||
EpollHelperError::HandleEvent(anyhow!("Failed to process queue (submit): {:?}", e))
|
EpollHelperError::HandleEvent(anyhow!("Failed to process queue (submit): {:?}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if needs_notification {
|
if self
|
||||||
|
.queue
|
||||||
|
.needs_notification(self.mem.memory().deref())
|
||||||
|
.map_err(|e| {
|
||||||
|
EpollHelperError::HandleEvent(anyhow!(
|
||||||
|
"Failed to check needs_notification: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?
|
||||||
|
{
|
||||||
self.signal_used_queue().map_err(|e| {
|
self.signal_used_queue().map_err(|e| {
|
||||||
EpollHelperError::HandleEvent(anyhow!("Failed to signal used queue: {:?}", e))
|
EpollHelperError::HandleEvent(anyhow!("Failed to signal used queue: {:?}", e))
|
||||||
})?
|
})?;
|
||||||
};
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -265,8 +279,7 @@ impl BlockEpollHandler {
|
|||||||
Err(Error::MissingEntryRequestList)
|
Err(Error::MissingEntryRequestList)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_queue_complete(&mut self) -> Result<bool> {
|
fn process_queue_complete(&mut self) -> Result<()> {
|
||||||
let mut used_descs = false;
|
|
||||||
let mem = self.mem.memory();
|
let mem = self.mem.memory();
|
||||||
let mut read_bytes = Wrapping(0);
|
let mut read_bytes = Wrapping(0);
|
||||||
let mut write_bytes = Wrapping(0);
|
let mut write_bytes = Wrapping(0);
|
||||||
@ -379,7 +392,9 @@ impl BlockEpollHandler {
|
|||||||
queue
|
queue
|
||||||
.add_used(mem.deref(), desc_index, len)
|
.add_used(mem.deref(), desc_index, len)
|
||||||
.map_err(Error::QueueAddUsed)?;
|
.map_err(Error::QueueAddUsed)?;
|
||||||
used_descs = true;
|
queue
|
||||||
|
.enable_notification(mem.deref())
|
||||||
|
.map_err(Error::QueueEnableNotification)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.counters
|
self.counters
|
||||||
@ -396,7 +411,7 @@ impl BlockEpollHandler {
|
|||||||
.read_ops
|
.read_ops
|
||||||
.fetch_add(read_ops.0, Ordering::AcqRel);
|
.fetch_add(read_ops.0, Ordering::AcqRel);
|
||||||
|
|
||||||
Ok(used_descs)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
|
fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
|
||||||
@ -487,20 +502,19 @@ impl EpollHelperHandler for BlockEpollHandler {
|
|||||||
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let needs_notification = self.process_queue_complete().map_err(|e| {
|
self.process_queue_complete().map_err(|e| {
|
||||||
EpollHelperError::HandleEvent(anyhow!(
|
EpollHelperError::HandleEvent(anyhow!(
|
||||||
"Failed to process queue (complete): {:?}",
|
"Failed to process queue (complete): {:?}",
|
||||||
e
|
e
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if needs_notification {
|
let rate_limit_reached =
|
||||||
self.signal_used_queue().map_err(|e| {
|
self.rate_limiter.as_ref().map_or(false, |r| r.is_blocked());
|
||||||
EpollHelperError::HandleEvent(anyhow!(
|
|
||||||
"Failed to signal used queue: {:?}",
|
// Process the queue only when the rate limit is not reached
|
||||||
e
|
if !rate_limit_reached {
|
||||||
))
|
self.process_queue_submit_and_signal()?
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RATE_LIMITER_EVENT => {
|
RATE_LIMITER_EVENT => {
|
||||||
@ -606,7 +620,8 @@ impl Block {
|
|||||||
| (1u64 << VIRTIO_BLK_F_FLUSH)
|
| (1u64 << VIRTIO_BLK_F_FLUSH)
|
||||||
| (1u64 << VIRTIO_BLK_F_CONFIG_WCE)
|
| (1u64 << VIRTIO_BLK_F_CONFIG_WCE)
|
||||||
| (1u64 << VIRTIO_BLK_F_BLK_SIZE)
|
| (1u64 << VIRTIO_BLK_F_BLK_SIZE)
|
||||||
| (1u64 << VIRTIO_BLK_F_TOPOLOGY);
|
| (1u64 << VIRTIO_BLK_F_TOPOLOGY)
|
||||||
|
| (1u64 << VIRTIO_RING_F_EVENT_IDX);
|
||||||
|
|
||||||
if iommu {
|
if iommu {
|
||||||
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
|
||||||
@ -779,8 +794,12 @@ impl VirtioDevice for Block {
|
|||||||
self.update_writeback();
|
self.update_writeback();
|
||||||
|
|
||||||
let mut epoll_threads = Vec::new();
|
let mut epoll_threads = Vec::new();
|
||||||
|
let event_idx = self.common.feature_acked(VIRTIO_RING_F_EVENT_IDX.into());
|
||||||
|
|
||||||
for i in 0..queues.len() {
|
for i in 0..queues.len() {
|
||||||
let (_, queue, queue_evt) = queues.remove(0);
|
let (_, mut queue, queue_evt) = queues.remove(0);
|
||||||
|
queue.set_event_idx(event_idx);
|
||||||
|
|
||||||
let queue_size = queue.size();
|
let queue_size = queue.size();
|
||||||
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
let (kill_evt, pause_evt) = self.common.dup_eventfds();
|
||||||
let queue_idx = i as u16;
|
let queue_idx = i as u16;
|
||||||
|
Loading…
Reference in New Issue
Block a user