Skip to content

Commit 084b7a8

Browse files
committed
fix: more compliant VIRTIO notifications
VIRTIO spec states: ``` After the device writes a descriptor index into the used ring: If the idx field in the used ring (which determined where that descriptor index was placed) was equal to used_event, the device MUST send a notification. ``` The current implementation does not follow this very precisely. It bumps used ring index when new descriptors are added to the used ring. But the check if the notification is needed is postponed to later processing stage. To be more VIRTIO spec compliant simply move the logic for updating the used ring index into the later processing stage as well, just before the check if the notification should be send. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent f13e813 commit 084b7a8

File tree

6 files changed

+38
-29
lines changed

6 files changed

+38
-29
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ impl Balloon {
362362
}
363363
}
364364
}
365+
queue.advance_used_ring_idx();
365366

366367
if needs_interrupt {
367368
self.signal_used_queue()?;
@@ -380,6 +381,7 @@ impl Balloon {
380381
queue.add_used(head.index, 0)?;
381382
needs_interrupt = true;
382383
}
384+
queue.advance_used_ring_idx();
383385

384386
if needs_interrupt {
385387
self.signal_used_queue()
@@ -453,6 +455,7 @@ impl Balloon {
453455
// and sending a used buffer notification
454456
if let Some(index) = self.stats_desc_index.take() {
455457
self.queues[STATS_INDEX].add_used(index, 0)?;
458+
self.queues[STATS_INDEX].advance_used_ring_idx();
456459
self.signal_used_queue()
457460
} else {
458461
error!("Failed to update balloon stats, missing descriptor.");

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ impl VirtioBlock {
436436
}
437437
}
438438
}
439+
queue.advance_used_ring_idx();
439440

440441
if queue.prepare_kick() {
441442
self.irq_trigger
@@ -497,6 +498,7 @@ impl VirtioBlock {
497498
}
498499
}
499500
}
501+
queue.advance_used_ring_idx();
500502

501503
if queue.prepare_kick() {
502504
self.irq_trigger

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ impl RxBuffers {
207207
/// This will let the guest know that about all the `DescriptorChain` object that has been
208208
/// used to receive a frame from the TAP.
209209
fn finish_frame(&mut self, rx_queue: &mut Queue) {
210-
rx_queue.advance_used_ring(self.used_descriptors);
210+
rx_queue.advance_next_used(self.used_descriptors);
211211
self.used_descriptors = 0;
212212
self.used_bytes = 0;
213213
}
@@ -396,6 +396,7 @@ impl Net {
396396
NetQueue::Rx => &mut self.queues[RX_INDEX],
397397
NetQueue::Tx => &mut self.queues[TX_INDEX],
398398
};
399+
queue.advance_used_ring_idx();
399400

400401
if queue.prepare_kick() {
401402
self.irq_trigger

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -650,20 +650,23 @@ impl Queue {
650650
}
651651

652652
/// Advance queue and used ring by `n` elements.
653-
pub fn advance_used_ring(&mut self, n: u16) {
653+
pub fn advance_next_used(&mut self, n: u16) {
654654
self.num_added += Wrapping(n);
655655
self.next_used += Wrapping(n);
656+
}
656657

658+
/// Set the used ring index to the current `next_used` value.
659+
/// Should be called once after number of `add_used` calls.
660+
pub fn advance_used_ring_idx(&mut self) {
657661
// This fence ensures all descriptor writes are visible before the index update is.
658662
fence(Ordering::Release);
659-
660663
self.used_ring_idx_set(self.next_used.0);
661664
}
662665

663666
/// Puts an available descriptor head into the used ring for use by the guest.
664667
pub fn add_used(&mut self, desc_index: u16, len: u32) -> Result<(), QueueError> {
665668
self.write_used_element(0, desc_index, len)?;
666-
self.advance_used_ring(1);
669+
self.advance_next_used(1);
667670
Ok(())
668671
}
669672

@@ -1573,6 +1576,7 @@ mod tests {
15731576

15741577
// should be ok
15751578
q.add_used(1, 0x1000).unwrap();
1579+
q.advance_used_ring_idx();
15761580
assert_eq!(vq.used.idx.get(), 1);
15771581
let x = vq.used.ring[0].get();
15781582
assert_eq!(x.id, 1);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ impl Entropy {
185185
}
186186
}
187187
}
188+
self.queues[RNG_QUEUE].advance_used_ring_idx();
188189

189190
if used_any {
190191
self.signal_used_queue().unwrap_or_else(|err| {

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

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,10 @@ where
149149
// This is safe since we checked in the event handler that the device is activated.
150150
let mem = self.device_state.mem().unwrap();
151151

152+
let queue = &mut self.queues[RXQ_INDEX];
152153
let mut have_used = false;
153154

154-
while let Some(head) = self.queues[RXQ_INDEX].pop()? {
155+
while let Some(head) = queue.pop()? {
155156
let index = head.index;
156157
let used_len = match self.rx_packet.parse(mem, head) {
157158
Ok(()) => {
@@ -174,7 +175,7 @@ where
174175
// We are using a consuming iterator over the virtio buffers, so, if we
175176
// can't fill in this buffer, we'll need to undo the
176177
// last iterator step.
177-
self.queues[RXQ_INDEX].undo_pop();
178+
queue.undo_pop();
178179
break;
179180
}
180181
}
@@ -185,12 +186,11 @@ where
185186
};
186187

187188
have_used = true;
188-
self.queues[RXQ_INDEX]
189-
.add_used(index, used_len)
190-
.unwrap_or_else(|err| {
191-
error!("Failed to add available descriptor {}: {}", index, err)
192-
});
189+
queue.add_used(index, used_len).unwrap_or_else(|err| {
190+
error!("Failed to add available descriptor {}: {}", index, err)
191+
});
193192
}
193+
queue.advance_used_ring_idx();
194194

195195
Ok(have_used)
196196
}
@@ -202,37 +202,35 @@ where
202202
// This is safe since we checked in the event handler that the device is activated.
203203
let mem = self.device_state.mem().unwrap();
204204

205+
let queue = &mut self.queues[TXQ_INDEX];
205206
let mut have_used = false;
206207

207-
while let Some(head) = self.queues[TXQ_INDEX].pop()? {
208+
while let Some(head) = queue.pop()? {
208209
let index = head.index;
209210
// let pkt = match VsockPacket::from_tx_virtq_head(mem, head) {
210211
match self.tx_packet.parse(mem, head) {
211212
Ok(()) => (),
212213
Err(err) => {
213214
error!("vsock: error reading TX packet: {:?}", err);
214215
have_used = true;
215-
self.queues[TXQ_INDEX]
216-
.add_used(index, 0)
217-
.unwrap_or_else(|err| {
218-
error!("Failed to add available descriptor {}: {}", index, err);
219-
});
216+
queue.add_used(index, 0).unwrap_or_else(|err| {
217+
error!("Failed to add available descriptor {}: {}", index, err);
218+
});
220219
continue;
221220
}
222221
};
223222

224223
if self.backend.send_pkt(&self.tx_packet).is_err() {
225-
self.queues[TXQ_INDEX].undo_pop();
224+
queue.undo_pop();
226225
break;
227226
}
228227

229228
have_used = true;
230-
self.queues[TXQ_INDEX]
231-
.add_used(index, 0)
232-
.unwrap_or_else(|err| {
233-
error!("Failed to add available descriptor {}: {}", index, err);
234-
});
229+
queue.add_used(index, 0).unwrap_or_else(|err| {
230+
error!("Failed to add available descriptor {}: {}", index, err);
231+
});
235232
}
233+
queue.advance_used_ring_idx();
236234

237235
Ok(have_used)
238236
}
@@ -244,19 +242,19 @@ where
244242
// This is safe since we checked in the caller function that the device is activated.
245243
let mem = self.device_state.mem().unwrap();
246244

247-
let head = self.queues[EVQ_INDEX].pop()?.ok_or_else(|| {
245+
let queue = &mut self.queues[EVQ_INDEX];
246+
let head = queue.pop()?.ok_or_else(|| {
248247
METRICS.ev_queue_event_fails.inc();
249248
DeviceError::VsockError(VsockError::EmptyQueue)
250249
})?;
251250

252251
mem.write_obj::<u32>(VIRTIO_VSOCK_EVENT_TRANSPORT_RESET, head.addr)
253252
.unwrap_or_else(|err| error!("Failed to write virtio vsock reset event: {:?}", err));
254253

255-
self.queues[EVQ_INDEX]
256-
.add_used(head.index, head.len)
257-
.unwrap_or_else(|err| {
258-
error!("Failed to add used descriptor {}: {}", head.index, err);
259-
});
254+
queue.add_used(head.index, head.len).unwrap_or_else(|err| {
255+
error!("Failed to add used descriptor {}: {}", head.index, err);
256+
});
257+
queue.advance_used_ring_idx();
260258

261259
self.signal_used_queue()?;
262260

0 commit comments

Comments
 (0)