Skip to content

Commit 26a1394

Browse files
committed
TEST: use queue opt for block device
Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 96d6729 commit 26a1394

File tree

3 files changed

+180
-9
lines changed

3 files changed

+180
-9
lines changed

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -388,15 +388,14 @@ impl VirtioBlock {
388388
queue: &mut Queue,
389389
index: u16,
390390
len: u32,
391-
mem: &GuestMemoryMmap,
392391
irq_trigger: &IrqTrigger,
393392
block_metrics: &BlockDeviceMetrics,
394393
) {
395-
queue.add_used(mem, index, len).unwrap_or_else(|err| {
394+
queue.add_used_opt(index, len).unwrap_or_else(|err| {
396395
error!("Failed to add available descriptor head {}: {}", index, err)
397396
});
398397

399-
if queue.prepare_kick(mem) {
398+
if queue.prepare_kick_opt() {
400399
irq_trigger.trigger_irq(IrqType::Vring).unwrap_or_else(|_| {
401400
block_metrics.event_fails.inc();
402401
});
@@ -411,9 +410,11 @@ impl VirtioBlock {
411410
let queue = &mut self.queues[queue_index];
412411
let mut used_any = false;
413412

414-
while let Some(head) = queue.pop_or_enable_notification(mem) {
415-
self.metrics.remaining_reqs_count.add(queue.len(mem).into());
416-
let processing_result = match Request::parse(&head, mem, self.disk.nsectors) {
413+
while let Some(head) = queue.pop_or_enable_notification_opt() {
414+
self.metrics
415+
.remaining_reqs_count
416+
.add(queue.len_opt().into());
417+
let processing_result = match Request::parse_opt(&head, mem, self.disk.nsectors) {
417418
Ok(request) => {
418419
if request.rate_limit(&mut self.rate_limiter) {
419420
// Stop processing the queue and return this descriptor chain to the
@@ -448,7 +449,6 @@ impl VirtioBlock {
448449
queue,
449450
head.index,
450451
finished.num_bytes_to_mem,
451-
mem,
452452
&self.irq_trigger,
453453
&self.metrics,
454454
);
@@ -500,7 +500,6 @@ impl VirtioBlock {
500500
queue,
501501
finished.desc_idx,
502502
finished.num_bytes_to_mem,
503-
mem,
504503
&self.irq_trigger,
505504
&self.metrics,
506505
);
@@ -633,6 +632,7 @@ impl VirtioDevice for VirtioBlock {
633632
}
634633

635634
fn activate(&mut self, mem: GuestMemoryMmap) -> Result<(), ActivateError> {
635+
self.queues[0].set_pointers(&mem);
636636
let event_idx = self.has_feature(u64::from(VIRTIO_RING_F_EVENT_IDX));
637637
if event_idx {
638638
for queue in &mut self.queues {

src/vmm/src/devices/virtio/block/virtio/request.rs

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub use crate::devices::virtio::gen::virtio_blk::{
1616
VIRTIO_BLK_ID_BYTES, VIRTIO_BLK_S_IOERR, VIRTIO_BLK_S_OK, VIRTIO_BLK_S_UNSUPP,
1717
VIRTIO_BLK_T_FLUSH, VIRTIO_BLK_T_GET_ID, VIRTIO_BLK_T_IN, VIRTIO_BLK_T_OUT,
1818
};
19-
use crate::devices::virtio::queue::DescriptorChain;
19+
use crate::devices::virtio::queue::{DescriptorChain, DescriptorChainOpt};
2020
use crate::logger::{error, IncMetric};
2121
use crate::rate_limiter::{RateLimiter, TokenType};
2222
use crate::vstate::memory::{ByteValued, Bytes, GuestAddress, GuestMemoryMmap};
@@ -330,6 +330,95 @@ impl Request {
330330
Ok(req)
331331
}
332332

333+
pub fn parse_opt(
334+
avail_desc: &DescriptorChainOpt,
335+
mem: &GuestMemoryMmap,
336+
num_disk_sectors: u64,
337+
) -> Result<Request, VirtioBlockError> {
338+
// The head contains the request type which MUST be readable.
339+
if avail_desc.is_write_only() {
340+
return Err(VirtioBlockError::UnexpectedWriteOnlyDescriptor);
341+
}
342+
343+
let request_header = RequestHeader::read_from(mem, avail_desc.addr)?;
344+
let mut req = Request {
345+
r#type: RequestType::from(request_header.request_type),
346+
sector: request_header.sector,
347+
data_addr: GuestAddress(0),
348+
data_len: 0,
349+
status_addr: GuestAddress(0),
350+
};
351+
352+
let data_desc;
353+
let status_desc;
354+
let desc = avail_desc
355+
.next_descriptor()
356+
.ok_or(VirtioBlockError::DescriptorChainTooShort)?;
357+
358+
if !desc.has_next() {
359+
status_desc = desc;
360+
// Only flush requests are allowed to skip the data descriptor.
361+
if req.r#type != RequestType::Flush {
362+
return Err(VirtioBlockError::DescriptorChainTooShort);
363+
}
364+
} else {
365+
data_desc = desc;
366+
status_desc = data_desc
367+
.next_descriptor()
368+
.ok_or(VirtioBlockError::DescriptorChainTooShort)?;
369+
370+
if data_desc.is_write_only() && req.r#type == RequestType::Out {
371+
return Err(VirtioBlockError::UnexpectedWriteOnlyDescriptor);
372+
}
373+
if !data_desc.is_write_only() && req.r#type == RequestType::In {
374+
return Err(VirtioBlockError::UnexpectedReadOnlyDescriptor);
375+
}
376+
if !data_desc.is_write_only() && req.r#type == RequestType::GetDeviceID {
377+
return Err(VirtioBlockError::UnexpectedReadOnlyDescriptor);
378+
}
379+
380+
req.data_addr = data_desc.addr;
381+
req.data_len = data_desc.len;
382+
}
383+
384+
// check request validity
385+
match req.r#type {
386+
RequestType::In | RequestType::Out => {
387+
// Check that the data length is a multiple of 512 as specified in the virtio
388+
// standard.
389+
if req.data_len % SECTOR_SIZE != 0 {
390+
return Err(VirtioBlockError::InvalidDataLength);
391+
}
392+
let top_sector = req
393+
.sector
394+
.checked_add(u64::from(req.data_len) >> SECTOR_SHIFT)
395+
.ok_or(VirtioBlockError::InvalidOffset)?;
396+
if top_sector > num_disk_sectors {
397+
return Err(VirtioBlockError::InvalidOffset);
398+
}
399+
}
400+
RequestType::GetDeviceID => {
401+
if req.data_len < VIRTIO_BLK_ID_BYTES {
402+
return Err(VirtioBlockError::InvalidDataLength);
403+
}
404+
}
405+
_ => {}
406+
}
407+
408+
// The status MUST always be writable.
409+
if !status_desc.is_write_only() {
410+
return Err(VirtioBlockError::UnexpectedReadOnlyDescriptor);
411+
}
412+
413+
if status_desc.len < 1 {
414+
return Err(VirtioBlockError::DescriptorLengthTooSmall);
415+
}
416+
417+
req.status_addr = status_desc.addr;
418+
419+
Ok(req)
420+
}
421+
333422
pub(crate) fn rate_limit(&self, rate_limiter: &mut RateLimiter) -> bool {
334423
// If limiter.consume() fails it means there is no more TokenType::Ops
335424
// budget and rate limiting is in effect.

src/vmm/src/devices/virtio/queue.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,21 @@ impl Queue {
620620
self.do_pop_unchecked(mem)
621621
}
622622

623+
pub fn pop_opt(&mut self) -> Option<DescriptorChainOpt> {
624+
let len = self.len_opt();
625+
if len > self.actual_size() {
626+
panic!(
627+
"The number of available virtio descriptors {len} is greater than queue size: {}!",
628+
self.actual_size()
629+
);
630+
}
631+
if len == 0 {
632+
return None;
633+
}
634+
635+
self.do_pop_unchecked_opt()
636+
}
637+
623638
/// Try to pop the first available descriptor chain from the avail ring.
624639
/// If no descriptor is available, enable notifications.
625640
pub fn pop_or_enable_notification<'b, M: GuestMemory>(
@@ -637,6 +652,18 @@ impl Queue {
637652
self.do_pop_unchecked(mem)
638653
}
639654

655+
pub fn pop_or_enable_notification_opt(&mut self) -> Option<DescriptorChainOpt> {
656+
if !self.uses_notif_suppression {
657+
return self.pop_opt();
658+
}
659+
660+
if self.try_enable_notification_opt() {
661+
return None;
662+
}
663+
664+
self.do_pop_unchecked_opt()
665+
}
666+
640667
/// Pop the first available descriptor chain from the avail ring.
641668
///
642669
/// # Important
@@ -723,6 +750,16 @@ impl Queue {
723750
Ok(())
724751
}
725752

753+
pub fn add_used_opt(&mut self, desc_index: u16, len: u32) -> Result<(), QueueError> {
754+
let used_element = UsedElement {
755+
id: u32::from(desc_index),
756+
len,
757+
};
758+
self.write_used_ring_opt(self.next_used.0, used_element)?;
759+
self.advance_used_ring_opt(1);
760+
Ok(())
761+
}
762+
726763
/// Advance number of used descriptor heads by `n`.
727764
pub fn advance_used_ring<M: GuestMemory>(&mut self, mem: &M, n: u16) {
728765
self.num_added += Wrapping(n);
@@ -963,6 +1000,33 @@ impl Queue {
9631000
self.next_avail.0 == self.avail_idx(mem).0
9641001
}
9651002

1003+
pub fn try_enable_notification_opt(&mut self) -> bool {
1004+
if !self.uses_notif_suppression {
1005+
return true;
1006+
}
1007+
1008+
let len = self.len_opt();
1009+
if len != 0 {
1010+
if len > self.actual_size() {
1011+
panic!(
1012+
"The number of available virtio descriptors {len} is greater than queue size: \
1013+
{}!",
1014+
self.actual_size()
1015+
);
1016+
}
1017+
return false;
1018+
}
1019+
1020+
*self.ur.avail_event() = self.next_avail.0;
1021+
1022+
// Make sure all subsequent reads are performed after `set_used_ring_avail_event`.
1023+
fence(Ordering::SeqCst);
1024+
1025+
// If the actual avail_idx is different than next_avail one or more descriptors can still
1026+
// be consumed from the available ring.
1027+
self.next_avail.0 == *self.ar.idx()
1028+
}
1029+
9661030
/// Enable notification suppression.
9671031
pub fn enable_notif_suppression(&mut self) {
9681032
self.uses_notif_suppression = true;
@@ -994,6 +1058,24 @@ impl Queue {
9941058

9951059
new - used_event - Wrapping(1) < new - old
9961060
}
1061+
1062+
pub fn prepare_kick_opt(&mut self) -> bool {
1063+
// If the device doesn't use notification suppression, always return true
1064+
if !self.uses_notif_suppression {
1065+
return true;
1066+
}
1067+
1068+
// We need to expose used array entries before checking the used_event.
1069+
fence(Ordering::SeqCst);
1070+
1071+
let new = self.next_used;
1072+
let old = self.next_used - self.num_added;
1073+
let used_event = Wrapping(*self.ar.used_event());
1074+
1075+
self.num_added = Wrapping(0);
1076+
1077+
new - used_event - Wrapping(1) < new - old
1078+
}
9971079
}
9981080

9991081
#[cfg(kani)]

0 commit comments

Comments
 (0)