Skip to content

Commit 1083c09

Browse files
ShadowCurseManciukic
authored andcommitted
fix(block): only notify guest once
Currently block device has a guest notification logic inside it's request processing loop. This can create a situation when guest can continuously add more requests to the queue, making the whole request processing loop arbitrary long. This is an issue, since it block any other IO from being processed. The solution is to simply notify guest one time, after all current requests are processed. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 522d3f0 commit 1083c09

File tree

1 file changed

+47
-43
lines changed
  • src/vmm/src/devices/virtio/block/virtio

1 file changed

+47
-43
lines changed

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

Lines changed: 47 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -384,24 +384,6 @@ impl VirtioBlock {
384384
}
385385
}
386386

387-
fn add_used_descriptor(
388-
queue: &mut Queue,
389-
index: u16,
390-
len: u32,
391-
irq_trigger: &IrqTrigger,
392-
block_metrics: &BlockDeviceMetrics,
393-
) {
394-
queue.add_used(index, len).unwrap_or_else(|err| {
395-
error!("Failed to add available descriptor head {}: {}", index, err)
396-
});
397-
398-
if queue.prepare_kick() {
399-
irq_trigger.trigger_irq(IrqType::Vring).unwrap_or_else(|_| {
400-
block_metrics.event_fails.inc();
401-
});
402-
}
403-
}
404-
405387
/// Device specific function for peaking inside a queue and processing descriptors.
406388
pub fn process_queue(&mut self, queue_index: usize) -> Result<(), InvalidAvailIdx> {
407389
// This is safe since we checked in the event handler that the device is activated.
@@ -443,17 +425,26 @@ impl VirtioBlock {
443425
break;
444426
}
445427
ProcessingResult::Executed(finished) => {
446-
Self::add_used_descriptor(
447-
queue,
448-
head.index,
449-
finished.num_bytes_to_mem,
450-
&self.irq_trigger,
451-
&self.metrics,
452-
);
428+
queue
429+
.add_used(head.index, finished.num_bytes_to_mem)
430+
.unwrap_or_else(|err| {
431+
error!(
432+
"Failed to add available descriptor head {}: {}",
433+
head.index, err
434+
)
435+
});
453436
}
454437
}
455438
}
456439

440+
if queue.prepare_kick() {
441+
self.irq_trigger
442+
.trigger_irq(IrqType::Vring)
443+
.unwrap_or_else(|_| {
444+
self.metrics.event_fails.inc();
445+
});
446+
}
447+
457448
if let FileEngine::Async(ref mut engine) = self.disk.file_engine {
458449
if let Err(err) = engine.kick_submission_queue() {
459450
error!("BlockError submitting pending block requests: {:?}", err);
@@ -495,17 +486,25 @@ impl VirtioBlock {
495486
),
496487
};
497488
let finished = pending.finish(mem, res, &self.metrics);
498-
499-
Self::add_used_descriptor(
500-
queue,
501-
finished.desc_idx,
502-
finished.num_bytes_to_mem,
503-
&self.irq_trigger,
504-
&self.metrics,
505-
);
489+
queue
490+
.add_used(finished.desc_idx, finished.num_bytes_to_mem)
491+
.unwrap_or_else(|err| {
492+
error!(
493+
"Failed to add available descriptor head {}: {}",
494+
finished.desc_idx, err
495+
)
496+
});
506497
}
507498
}
508499
}
500+
501+
if queue.prepare_kick() {
502+
self.irq_trigger
503+
.trigger_irq(IrqType::Vring)
504+
.unwrap_or_else(|_| {
505+
self.metrics.event_fails.inc();
506+
});
507+
}
509508
}
510509

511510
pub fn process_async_completion_event(&mut self) {
@@ -1573,14 +1572,14 @@ mod tests {
15731572

15741573
// Run scenario that doesn't trigger FullSq BlockError: Add sq_size flush requests.
15751574
add_flush_requests_batch(&mut block, &vq, IO_URING_NUM_ENTRIES);
1576-
simulate_queue_event(&mut block, Some(false));
1575+
simulate_queue_event(&mut block, Some(true));
15771576
assert!(!block.is_io_engine_throttled);
15781577
simulate_async_completion_event(&mut block, true);
15791578
check_flush_requests_batch(IO_URING_NUM_ENTRIES, &vq);
15801579

15811580
// Run scenario that triggers FullSqError : Add sq_size + 10 flush requests.
15821581
add_flush_requests_batch(&mut block, &vq, IO_URING_NUM_ENTRIES + 10);
1583-
simulate_queue_event(&mut block, Some(false));
1582+
simulate_queue_event(&mut block, Some(true));
15841583
assert!(block.is_io_engine_throttled);
15851584
// When the async_completion_event is triggered:
15861585
// 1. sq_size requests should be processed processed.
@@ -1607,16 +1606,16 @@ mod tests {
16071606
// Run scenario that triggers FullCqError. Push 2 * IO_URING_NUM_ENTRIES and wait for
16081607
// completion. Then try to push another entry.
16091608
add_flush_requests_batch(&mut block, &vq, IO_URING_NUM_ENTRIES);
1610-
simulate_queue_event(&mut block, Some(false));
1609+
simulate_queue_event(&mut block, Some(true));
16111610
assert!(!block.is_io_engine_throttled);
16121611
thread::sleep(Duration::from_millis(150));
16131612
add_flush_requests_batch(&mut block, &vq, IO_URING_NUM_ENTRIES);
1614-
simulate_queue_event(&mut block, Some(false));
1613+
simulate_queue_event(&mut block, Some(true));
16151614
assert!(!block.is_io_engine_throttled);
16161615
thread::sleep(Duration::from_millis(150));
16171616

16181617
add_flush_requests_batch(&mut block, &vq, 1);
1619-
simulate_queue_event(&mut block, Some(false));
1618+
simulate_queue_event(&mut block, Some(true));
16201619
assert!(block.is_io_engine_throttled);
16211620
simulate_async_completion_event(&mut block, true);
16221621
assert!(!block.is_io_engine_throttled);
@@ -1672,13 +1671,15 @@ mod tests {
16721671
vq.dtable[1].len.set(512);
16731672
mem.write_obj::<u64>(123_456_789, data_addr).unwrap();
16741673

1675-
// Following write procedure should fail because of bandwidth rate limiting.
1674+
// This will fail because of bandwidth rate limiting.
1675+
// The irq is still triggered because notification suppression
1676+
// is not enabled.
16761677
{
16771678
// Trigger the attempt to write.
16781679
check_metric_after_block!(
16791680
&block.metrics.rate_limiter_throttled_events,
16801681
1,
1681-
simulate_queue_event(&mut block, Some(false))
1682+
simulate_queue_event(&mut block, Some(true))
16821683
);
16831684

16841685
// Assert that limiter is blocked.
@@ -1740,13 +1741,15 @@ mod tests {
17401741
vq.dtable[1].len.set(512);
17411742
mem.write_obj::<u64>(123_456_789, data_addr).unwrap();
17421743

1743-
// Following write procedure should fail because of ops rate limiting.
1744+
// This will fail because of ops rate limiting.
1745+
// The irq is still triggered because notification suppression
1746+
// is not enabled.
17441747
{
17451748
// Trigger the attempt to write.
17461749
check_metric_after_block!(
17471750
&block.metrics.rate_limiter_throttled_events,
17481751
1,
1749-
simulate_queue_event(&mut block, Some(false))
1752+
simulate_queue_event(&mut block, Some(true))
17501753
);
17511754

17521755
// Assert that limiter is blocked.
@@ -1755,7 +1758,8 @@ mod tests {
17551758
assert_eq!(vq.used.idx.get(), 0);
17561759
}
17571760

1758-
// Do a second write that still fails but this time on the fast path.
1761+
// Do a second write that still fails but this time on the fast path
1762+
// which does not call `process_queue`, so no irq notifications.
17591763
{
17601764
// Trigger the attempt to write.
17611765
check_metric_after_block!(

0 commit comments

Comments
 (0)