devices/virtio/queue/split_queue.rs
1// Copyright 2017 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::num::Wrapping;
6use std::sync::atomic::fence;
7use std::sync::atomic::AtomicU16;
8use std::sync::atomic::Ordering;
9
10use anyhow::bail;
11use anyhow::Context;
12use anyhow::Result;
13use base::error;
14use base::Event;
15use data_model::Le32;
16use serde::Deserialize;
17use serde::Serialize;
18use snapshot::AnySnapshot;
19use virtio_sys::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
20use vm_memory::GuestAddress;
21use vm_memory::GuestMemory;
22use zerocopy::FromBytes;
23use zerocopy::Immutable;
24use zerocopy::IntoBytes;
25use zerocopy::KnownLayout;
26
27use crate::virtio::DescriptorChain;
28use crate::virtio::Interrupt;
29use crate::virtio::QueueConfig;
30use crate::virtio::SplitDescriptorChain;
31
32#[allow(dead_code)]
33const VIRTQ_USED_F_NO_NOTIFY: u16 = 0x1;
34#[allow(dead_code)]
35const VIRTQ_AVAIL_F_NO_INTERRUPT: u16 = 0x1;
36
37/// An activated virtio queue with split queue layout.
38#[derive(Debug)]
39pub struct SplitQueue {
40 mem: GuestMemory,
41
42 event: Event,
43 interrupt: Interrupt,
44
45 /// The queue size in elements the driver selected. This is always guaranteed to be a power of
46 /// two, as required for split virtqueues.
47 size: u16,
48
49 /// MSI-X vector for the queue. Don't care for INTx
50 vector: u16,
51
52 /// Guest physical address of the descriptor table
53 desc_table: GuestAddress,
54
55 /// Guest physical address of the available ring
56 avail_ring: GuestAddress,
57
58 /// Guest physical address of the used ring
59 used_ring: GuestAddress,
60
61 next_avail: Wrapping<u16>,
62 next_used: Wrapping<u16>,
63
64 // Device feature bits accepted by the driver
65 features: u64,
66 last_used: Wrapping<u16>,
67}
68
69#[derive(Serialize, Deserialize)]
70pub struct SplitQueueSnapshot {
71 size: u16,
72 vector: u16,
73 desc_table: GuestAddress,
74 avail_ring: GuestAddress,
75 used_ring: GuestAddress,
76 next_avail: Wrapping<u16>,
77 next_used: Wrapping<u16>,
78 features: u64,
79 last_used: Wrapping<u16>,
80}
81
82#[repr(C)]
83#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
84struct virtq_used_elem {
85 id: Le32,
86 len: Le32,
87}
88
89impl SplitQueue {
90 /// Constructs an activated split virtio queue with the given configuration.
91 pub fn new(
92 config: &QueueConfig,
93 mem: &GuestMemory,
94 event: Event,
95 interrupt: Interrupt,
96 ) -> Result<SplitQueue> {
97 let size = config.size();
98 if !size.is_power_of_two() {
99 bail!("split queue size {size} is not a power of 2");
100 }
101
102 let desc_table = config.desc_table();
103 let avail_ring = config.avail_ring();
104 let used_ring = config.used_ring();
105
106 // Validate addresses and queue size to ensure that address calculation won't overflow.
107 let ring_sizes = Self::ring_sizes(size, desc_table, avail_ring, used_ring);
108 let rings = ring_sizes
109 .iter()
110 .zip(vec!["descriptor table", "available ring", "used ring"]);
111
112 for ((addr, size), name) in rings {
113 if addr.checked_add(*size as u64).is_none() {
114 bail!(
115 "virtio queue {} goes out of bounds: start:0x{:08x} size:0x{:08x}",
116 name,
117 addr.offset(),
118 size,
119 );
120 }
121 }
122
123 Ok(SplitQueue {
124 mem: mem.clone(),
125 event,
126 interrupt,
127 size,
128 vector: config.vector(),
129 desc_table: config.desc_table(),
130 avail_ring: config.avail_ring(),
131 used_ring: config.used_ring(),
132 features: config.acked_features(),
133 next_avail: config.next_avail(),
134 next_used: config.next_used(),
135
136 // WARNING: last_used controls interrupt suppression
137 // (VIRTIO_RING_F_EVENT_IDX). The only safe value initial value is
138 // zero (unless restoring a snapshot and the value that was stored
139 // on the device is known; however we do not bother with that in our
140 // snapshot system since it is much simpler to just use the zero
141 // value and send a potentially spurious interrupt on restore).
142 last_used: Wrapping(0),
143 })
144 }
145
146 pub fn vhost_user_reclaim(&mut self, vring_base: u16) {
147 self.next_avail = Wrapping(vring_base);
148 // The vhost-user spec says:
149 //
150 // For the Used Ring, the device only needs the next descriptor index at which to put
151 // new descriptors, which is the value in the vring structure in memory, so this value
152 // is not covered by this message.
153 //
154 // So, we read the value from guest memory.
155 let used_index_addr = self.used_ring.unchecked_add(2);
156 self.next_used = self
157 .mem
158 .read_obj_from_addr_volatile(used_index_addr)
159 .unwrap();
160
161 // Since the backend has not told us what its actual last_used value
162 // was, we have to assume that an interrupt must be sent when next
163 // available descriptor is used, so we set this to zero.
164 //
165 // But wait, one might ask, why can't we just assume the vhost-user
166 // backend has already sent interrupts for any descriptors it marked
167 // used before it stopped processing the queue? Then we could just
168 // initialize last_used as `last_used == next_used`, which would skip
169 // spurious interrupts and be more efficient. Right?
170 //
171 // If VIRTIO_RING_F_EVENT_IDX is enabled, then no. The reason is the
172 // device could be in an interrupt suppressed state and so it may indeed
173 // have marked some descriptors used, but not yet sent an interrupt for
174 // them. Once we set last_used = next_used, no interrupts will be sent
175 // to the driver until the driver updates next_used (see
176 // queue_wants_interrupt for details), but the driver will
177 // never wake up the device isn't sending any interrupts. Thus, the
178 // device stalls.
179 //
180 // NOTE: this value is not used by the snapshot/restore process, but we
181 // still want to pick a reasonable value here in case it is used in the
182 // future.
183 self.last_used = Wrapping(0);
184 }
185
186 pub fn next_avail_to_process(&self) -> u16 {
187 self.next_avail.0
188 }
189
190 /// Return the actual size of the queue, as the driver may not set up a
191 /// queue as big as the device allows.
192 pub fn size(&self) -> u16 {
193 self.size
194 }
195
196 /// Getter for vector field
197 pub fn vector(&self) -> u16 {
198 self.vector
199 }
200
201 /// Getter for descriptor area
202 pub fn desc_table(&self) -> GuestAddress {
203 self.desc_table
204 }
205
206 /// Getter for driver area
207 pub fn avail_ring(&self) -> GuestAddress {
208 self.avail_ring
209 }
210
211 /// Getter for device area
212 pub fn used_ring(&self) -> GuestAddress {
213 self.used_ring
214 }
215
216 /// Get a reference to the queue's "kick event"
217 pub fn event(&self) -> &Event {
218 &self.event
219 }
220
221 /// Get a reference to the queue's interrupt
222 pub fn interrupt(&self) -> &Interrupt {
223 &self.interrupt
224 }
225
226 // Return `index` modulo the currently configured queue size.
227 fn wrap_queue_index(&self, index: Wrapping<u16>) -> u16 {
228 // We know that `self.size` is a power of two (enforced by `new()`), so the modulus can
229 // be calculated with a bitmask rather than actual division.
230 debug_assert!(self.size.is_power_of_two());
231 index.0 & self.size.wrapping_sub(1)
232 }
233
234 fn ring_sizes(
235 queue_size: u16,
236 desc_table: GuestAddress,
237 avail_ring: GuestAddress,
238 used_ring: GuestAddress,
239 ) -> Vec<(GuestAddress, usize)> {
240 let queue_size = queue_size as usize;
241 vec![
242 (desc_table, 16 * queue_size),
243 (avail_ring, 6 + 2 * queue_size),
244 (used_ring, 6 + 8 * queue_size),
245 ]
246 }
247
248 // Set the `avail_event` field in the used ring.
249 //
250 // This allows the device to inform the driver that driver-to-device notification
251 // (kicking the ring) is not necessary until the driver reaches the `avail_index` descriptor.
252 //
253 // This value is only used if the `VIRTIO_F_EVENT_IDX` feature has been negotiated.
254 fn set_avail_event(&mut self, avail_index: Wrapping<u16>) {
255 fence(Ordering::SeqCst);
256
257 let avail_event_addr = self.used_ring.unchecked_add(4 + 8 * u64::from(self.size));
258 self.mem
259 .write_obj_at_addr_volatile(avail_index.0, avail_event_addr)
260 .unwrap();
261 }
262
263 // Query the value of a single-bit flag in the available ring.
264 //
265 // Returns `true` if `flag` is currently set (by the driver) in the available ring flags.
266 fn get_avail_flag(&self, flag: u16) -> bool {
267 fence(Ordering::SeqCst);
268
269 let avail_flags: u16 = self
270 .mem
271 .read_obj_from_addr_volatile(self.avail_ring)
272 .unwrap();
273
274 avail_flags & flag == flag
275 }
276
277 // Get the `used_event` field in the available ring.
278 //
279 // The returned value is the index of the next descriptor chain entry for which the driver
280 // needs to be notified upon use. Entries before this index may be used without notifying
281 // the driver.
282 //
283 // This value is only valid if the `VIRTIO_F_EVENT_IDX` feature has been negotiated.
284 fn get_used_event(&self) -> Wrapping<u16> {
285 fence(Ordering::SeqCst);
286
287 let used_event_addr = self.avail_ring.unchecked_add(4 + 2 * u64::from(self.size));
288 let used_event: u16 = self
289 .mem
290 .read_obj_from_addr_volatile(used_event_addr)
291 .unwrap();
292
293 Wrapping(used_event)
294 }
295
296 /// Get the first available descriptor chain without removing it from the queue.
297 /// Call `pop_peeked` to remove the returned descriptor chain from the queue.
298 pub fn peek(&mut self) -> Option<DescriptorChain> {
299 // Get a `VolatileSlice` covering the `struct virtq_avail` fixed header (`flags` and `idx`)
300 // and variable-length `ring`. This ensures that the raw pointers generated below point into
301 // valid `GuestMemory` regions.
302 let avail_ring_size = 2 * size_of::<u16>() + (size_of::<u16>() * usize::from(self.size));
303 let avail_ring_vslice = self
304 .mem
305 .get_slice_at_addr(self.avail_ring, avail_ring_size)
306 .unwrap();
307
308 // SAFETY: offset of `virtq_avail.idx` (2) is always within the `VolatileSlice` bounds.
309 let avail_index_ptr = unsafe { avail_ring_vslice.as_mut_ptr().add(2) } as *mut u16;
310 // SAFETY: `GuestMemory::get_slice_at_addr()` returns a valid `VolatileSlice`, and
311 // `avail_index_ptr` is a valid `*mut u16` contained within that slice.
312 let avail_index_atomic = unsafe { AtomicU16::from_ptr(avail_index_ptr) };
313
314 // Check if the driver has published any new descriptors beyond `self.next_avail`. This uses
315 // a `Relaxed` load because we do not need a memory barrier if there are no new descriptors.
316 // If the ring is not empty, the `fence()` below will provide the necessary ordering,
317 // pairing with the write memory barrier in the driver.
318 let avail_index: u16 = avail_index_atomic.load(Ordering::Relaxed);
319 let next_avail = self.next_avail;
320 if next_avail.0 == avail_index {
321 return None;
322 }
323
324 // This fence ensures that subsequent reads from the descriptor do not
325 // get reordered and happen only after fetching the available_index and
326 // checking that there is a slot available.
327 fence(Ordering::Acquire);
328
329 // Calculate the offset of `ring[next_avail % size]` within `struct virtq_avail`.
330 let ring_offset = 4 + (usize::from(self.wrap_queue_index(next_avail)) * 2);
331 debug_assert!(ring_offset + size_of::<u16>() <= avail_ring_size);
332 // SAFETY: The available ring index was wrapped to fall within the queue size above, so
333 // `ring_offset` is always in bounds.
334 let ring_ptr = unsafe { avail_ring_vslice.as_ptr().add(ring_offset) } as *const u16;
335 // SAFETY: `ring_ptr` is a valid `*const u16` within `avail_ring_vslice`.
336 let descriptor_index: u16 = unsafe { std::ptr::read_volatile(ring_ptr) };
337
338 let chain =
339 SplitDescriptorChain::new(&self.mem, self.desc_table, self.size, descriptor_index);
340 DescriptorChain::new(chain, &self.mem, descriptor_index)
341 .map_err(|e| {
342 error!("{:#}", e);
343 e
344 })
345 .ok()
346 }
347
348 /// Remove the first available descriptor chain from the queue.
349 /// This function should only be called immediately following `peek` and must be passed a
350 /// reference to the same `DescriptorChain` returned by the most recent `peek`.
351 pub(super) fn pop_peeked(&mut self, _descriptor_chain: &DescriptorChain) {
352 self.next_avail += Wrapping(1);
353 if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) != 0 {
354 self.set_avail_event(self.next_avail);
355 }
356 }
357
358 pub(super) fn try_pop_length(&mut self, length: usize) -> Option<Vec<DescriptorChain>> {
359 let mut remain_len = length;
360 let mut descriptors = vec![];
361 while remain_len > 0 {
362 match self.peek() {
363 Some(desc) => {
364 let available_bytes = desc.writer.available_bytes();
365 descriptors.push(desc);
366 self.next_avail += Wrapping(1);
367 if available_bytes >= remain_len {
368 if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) != 0 {
369 self.set_avail_event(self.next_avail);
370 }
371 return Some(descriptors);
372 } else {
373 remain_len -= available_bytes;
374 }
375 }
376 None => {
377 if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) != 0 {
378 self.set_avail_event(self.next_avail);
379 }
380 // Reverse the effect of pop
381 self.next_avail -= Wrapping(descriptors.len() as u16);
382 return None;
383 }
384 }
385 }
386 None
387 }
388
389 /// Puts multiple available descriptor heads into the used ring for use by the guest.
390 pub fn add_used_with_bytes_written_batch(
391 &mut self,
392 desc_chains: impl IntoIterator<Item = (DescriptorChain, u32)>,
393 ) {
394 // Get a `VolatileSlice` covering the `struct virtq_used` fixed header (`flags` and `idx`)
395 // and variable-length `ring`. This ensures that the raw pointers generated below point into
396 // valid `GuestMemory` regions.
397 let used_ring_size =
398 2 * size_of::<u16>() + (size_of::<virtq_used_elem>() * usize::from(self.size));
399 let used_ring_vslice = self
400 .mem
401 .get_slice_at_addr(self.used_ring, used_ring_size)
402 .unwrap();
403
404 // SAFETY: `elems_ptr` is always a valid pointer due to `used_ring_vslice()`.
405 let elems_ptr = unsafe { used_ring_vslice.as_mut_ptr().add(4) } as *mut virtq_used_elem;
406
407 // SAFETY: `used_index_ptr` is always a valid pointer due to `used_ring_vslice()`.
408 let used_index_ptr = unsafe { used_ring_vslice.as_mut_ptr().add(2) } as *mut u16;
409
410 // SAFETY: `used_index_ptr` is always a valid pointer
411 let used_index_atomic = unsafe { AtomicU16::from_ptr(used_index_ptr) };
412
413 let mut next_used = self.next_used;
414 for (desc_chain, len) in desc_chains {
415 let desc_index = desc_chain.index();
416 debug_assert!(desc_index < self.size);
417 let id = Le32::from(u32::from(desc_index));
418 let len = Le32::from(len);
419
420 let wrapped_index = usize::from(self.wrap_queue_index(next_used));
421 // SAFETY: `wrapped_index` is always in bounds due to `wrap_queue_index()`.
422 let elem_ptr = unsafe { elems_ptr.add(wrapped_index) };
423
424 // SAFETY: `elem_ptr` is always a valid pointer
425 unsafe {
426 std::ptr::write_volatile(std::ptr::addr_of_mut!((*elem_ptr).id), id);
427 std::ptr::write_volatile(std::ptr::addr_of_mut!((*elem_ptr).len), len);
428 };
429
430 next_used += Wrapping(1);
431 }
432
433 if next_used != self.next_used {
434 fence(Ordering::Release);
435 used_index_atomic.store(next_used.0, Ordering::Relaxed);
436 self.next_used = next_used;
437 }
438 }
439
440 /// Returns if the queue should have an interrupt sent based on its state.
441 ///
442 /// This function implements `VIRTIO_RING_F_EVENT_IDX`, otherwise known as
443 /// interrupt suppression. The virtio spec provides the driver with a field,
444 /// `used_event`, which says that once we write that descriptor (or several
445 /// in the case of a flurry of `add_used` calls), we should send a
446 /// notification. Because the values involved wrap around `u16::MAX`, and to
447 /// avoid checking the condition on every `add_used` call, the math is a
448 /// little complicated.
449 ///
450 /// The critical inequality is:
451 /// ```text
452 /// (next_used - 1) - used_event < next_used - last_used
453 /// ```
454 ///
455 /// For illustration purposes, we label it as `A < B`, where
456 /// `A = (next_used -1) - used_event`, and `B = next_used - last_used`.
457 ///
458 /// `A` and `B` represent two distances, measured in a wrapping ring of size
459 /// `u16::MAX`. In the "send intr" case, the inequality is true. In the
460 /// "don't send intr" case, the inequality is false. We must be very careful
461 /// in assigning a direction to the ring, so that when we
462 /// graph the subtraction operations, we are measuring the right distance
463 /// (similar to how DC circuits are analyzed).
464 ///
465 /// The two distances are as follows:
466 /// * `A` is the distance between the driver's requested notification point, and the current
467 /// position in the ring.
468 ///
469 /// * `B` is the distance between the last time we notified the guest, and the current position
470 /// in the ring.
471 ///
472 /// If we graph these distances for the situation where we want to notify
473 /// the guest, and when we don't want to notify the guest, we see that
474 /// `A < B` becomes true the moment `next_used - 1` passes `used_event`. See
475 /// the graphs at the bottom of this comment block for a more visual
476 /// explanation.
477 ///
478 /// Once an interrupt is sent, we have a final useful property: last_used
479 /// moves up next_used, which causes the inequality to be false. Thus, we
480 /// won't send notifications again until `used_event` is moved forward by
481 /// the driver.
482 ///
483 /// Finally, let's talk about a couple of ways to write this inequality
484 /// that don't work, and critically, explain *why*.
485 ///
486 /// First, a naive reading of the virtio spec might lead us to ask: why not
487 /// just use the following inequality:
488 /// ```text
489 /// next_used - 1 >= used_event
490 /// ```
491 ///
492 /// because that's much simpler, right? The trouble is that the ring wraps,
493 /// so it could be that a smaller index is actually ahead of a larger one.
494 /// That's why we have to use distances in the ring instead.
495 ///
496 /// Second, one might look at the correct inequality:
497 /// ```text
498 /// (next_used - 1) - used_event < next_used - last_used
499 /// ```
500 ///
501 /// And try to simplify it to:
502 /// ```text
503 /// last_used - 1 < used_event
504 /// ```
505 ///
506 /// Functionally, this won't work because next_used isn't present at all
507 /// anymore. (Notifications will never be sent.) But why is that? The algebra
508 /// here *appears* to work out, but all semantic meaning is lost. There are
509 /// two explanations for why this happens:
510 /// * The intuitive one: the terms in the inequality are not actually separable; in other words,
511 /// (next_used - last_used) is an inseparable term, so subtracting next_used from both sides
512 /// of the original inequality and zeroing them out is semantically invalid. But why aren't
513 /// they separable? See below.
514 /// * The theoretical one: canceling like terms relies a vector space law: a + x = b + x => a =
515 /// b (cancellation law). For congruences / equality under modulo, this law is satisfied, but
516 /// for inequalities under mod, it is not; therefore, we cannot cancel like terms.
517 ///
518 /// ```text
519 /// ┌──────────────────────────────────┐
520 /// │ │
521 /// │ │
522 /// │ │
523 /// │ ┌──────────── next_used - 1
524 /// │ │A x
525 /// │ │ ┌────────────x────────────┐
526 /// │ │ │ x │
527 /// │ │ │ │
528 /// │ │ │ │ │
529 /// │ │ │ │ │
530 /// │ used_event xxxx + ◄───┘ xxxxx last_used
531 /// │ │ │ │
532 /// │ │ Send intr │ │
533 /// │ │ │ │
534 /// │ └─────────────────────────┘ │
535 /// │ │
536 /// │ B │
537 /// └────────────────────────────────────────────────────┘
538 ///
539 /// ┌───────────────────────────────────────────────────┐
540 /// │ A │
541 /// │ ┌────────────────────────┐ │
542 /// │ │ │ │
543 /// │ │ │ │
544 /// │ │ │ │ │
545 /// │ │ │ │ │
546 /// used_event xxxx │ xxxxx last_used │
547 /// │ + ◄───┘ │ │ │
548 /// │ │ │ │
549 /// │ Don't send intr │ │ │
550 /// │ │ │ │
551 /// └───────────x────────────┘ │ │
552 /// x │ │
553 /// next_used - 1 │ │
554 /// │ │ B │ │
555 /// │ └────────────────────┘ │
556 /// │ │
557 /// └──────────────────────────────────┘
558 /// ```
559 fn queue_wants_interrupt(&self) -> bool {
560 if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) != 0 {
561 let used_event = self.get_used_event();
562 self.next_used - used_event - Wrapping(1) < self.next_used - self.last_used
563 } else {
564 !self.get_avail_flag(VIRTQ_AVAIL_F_NO_INTERRUPT)
565 }
566 }
567
568 /// inject interrupt into guest on this queue
569 /// return true: interrupt is injected into guest for this queue
570 /// false: interrupt isn't injected
571 pub fn trigger_interrupt(&mut self) -> bool {
572 if self.queue_wants_interrupt() {
573 self.last_used = self.next_used;
574 self.interrupt.signal_used_queue(self.vector);
575 true
576 } else {
577 false
578 }
579 }
580
581 pub fn snapshot(&self) -> anyhow::Result<AnySnapshot> {
582 AnySnapshot::to_any(SplitQueueSnapshot {
583 size: self.size,
584 vector: self.vector,
585 desc_table: self.desc_table,
586 avail_ring: self.avail_ring,
587 used_ring: self.used_ring,
588 next_avail: self.next_avail,
589 next_used: self.next_used,
590 features: self.features,
591 last_used: self.last_used,
592 })
593 .context("failed to serialize MsixConfigSnapshot")
594 }
595
596 pub fn restore(
597 queue_value: AnySnapshot,
598 mem: &GuestMemory,
599 event: Event,
600 interrupt: Interrupt,
601 ) -> anyhow::Result<SplitQueue> {
602 let s: SplitQueueSnapshot = AnySnapshot::from_any(queue_value)?;
603 let queue = SplitQueue {
604 mem: mem.clone(),
605 event,
606 interrupt,
607 size: s.size,
608 vector: s.vector,
609 desc_table: s.desc_table,
610 avail_ring: s.avail_ring,
611 used_ring: s.used_ring,
612 next_avail: s.next_avail,
613 next_used: s.next_used,
614 features: s.features,
615 last_used: s.last_used,
616 };
617 Ok(queue)
618 }
619}
620
621#[cfg(test)]
622mod tests {
623 use std::convert::TryInto;
624 use std::mem::offset_of;
625
626 use data_model::Le16;
627 use data_model::Le32;
628 use data_model::Le64;
629 use zerocopy::FromBytes;
630 use zerocopy::Immutable;
631 use zerocopy::IntoBytes;
632 use zerocopy::KnownLayout;
633
634 use super::*;
635 use crate::virtio::create_descriptor_chain;
636 use crate::virtio::Desc;
637 use crate::virtio::Interrupt;
638 use crate::virtio::Queue;
639
640 const GUEST_MEMORY_SIZE: u64 = 0x10000;
641 const DESC_OFFSET: u64 = 0;
642 const AVAIL_OFFSET: u64 = 0x200;
643 const USED_OFFSET: u64 = 0x400;
644 const QUEUE_SIZE: usize = 0x10;
645 const BUFFER_OFFSET: u64 = 0x8000;
646 const BUFFER_LEN: u32 = 0x400;
647
648 #[derive(Copy, Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
649 #[repr(C)]
650 struct Avail {
651 flags: Le16,
652 idx: Le16,
653 ring: [Le16; QUEUE_SIZE],
654 used_event: Le16,
655 }
656
657 impl Default for Avail {
658 fn default() -> Self {
659 Avail {
660 flags: Le16::from(0u16),
661 idx: Le16::from(0u16),
662 ring: [Le16::from(0u16); QUEUE_SIZE],
663 used_event: Le16::from(0u16),
664 }
665 }
666 }
667
668 #[derive(Copy, Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
669 #[repr(C)]
670 struct UsedElem {
671 id: Le32,
672 len: Le32,
673 }
674
675 impl Default for UsedElem {
676 fn default() -> Self {
677 UsedElem {
678 id: Le32::from(0u32),
679 len: Le32::from(0u32),
680 }
681 }
682 }
683
684 #[derive(Copy, Clone, Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
685 #[repr(C, packed)]
686 struct Used {
687 flags: Le16,
688 idx: Le16,
689 used_elem_ring: [UsedElem; QUEUE_SIZE],
690 avail_event: Le16,
691 }
692
693 impl Default for Used {
694 fn default() -> Self {
695 Used {
696 flags: Le16::from(0u16),
697 idx: Le16::from(0u16),
698 used_elem_ring: [UsedElem::default(); QUEUE_SIZE],
699 avail_event: Le16::from(0u16),
700 }
701 }
702 }
703
704 fn setup_vq(queue: &mut QueueConfig, mem: &GuestMemory) -> Queue {
705 let desc = Desc {
706 addr: Le64::from(BUFFER_OFFSET),
707 len: Le32::from(BUFFER_LEN),
708 flags: Le16::from(0u16),
709 next: Le16::from(1u16),
710 };
711 let _ = mem.write_obj_at_addr(desc, GuestAddress(DESC_OFFSET));
712
713 let avail = Avail::default();
714 let _ = mem.write_obj_at_addr(avail, GuestAddress(AVAIL_OFFSET));
715
716 let used = Used::default();
717 let _ = mem.write_obj_at_addr(used, GuestAddress(USED_OFFSET));
718
719 queue.set_desc_table(GuestAddress(DESC_OFFSET));
720 queue.set_avail_ring(GuestAddress(AVAIL_OFFSET));
721 queue.set_used_ring(GuestAddress(USED_OFFSET));
722 queue.ack_features((1u64) << VIRTIO_RING_F_EVENT_IDX);
723 queue.set_ready(true);
724
725 queue
726 .activate(mem, Event::new().unwrap(), Interrupt::new_for_test())
727 .expect("QueueConfig::activate failed")
728 }
729
730 fn fake_desc_chain(mem: &GuestMemory) -> DescriptorChain {
731 create_descriptor_chain(mem, GuestAddress(0), GuestAddress(0), Vec::new(), 0)
732 .expect("failed to create descriptor chain")
733 }
734
735 #[test]
736 fn queue_event_id_guest_fast() {
737 let mut queue =
738 QueueConfig::new(QUEUE_SIZE.try_into().unwrap(), 1 << VIRTIO_RING_F_EVENT_IDX);
739 let memory_start_addr = GuestAddress(0x0);
740 let mem = GuestMemory::new(&[(memory_start_addr, GUEST_MEMORY_SIZE)]).unwrap();
741 let mut queue = setup_vq(&mut queue, &mem);
742
743 // Offset of used_event within Avail structure
744 let used_event_offset = offset_of!(Avail, used_event) as u64;
745 let used_event_address = GuestAddress(AVAIL_OFFSET + used_event_offset);
746
747 // Assume driver submit 0x100 req to device,
748 // device has handled them, so increase self.next_used to 0x100
749 let mut device_generate: Wrapping<u16> = Wrapping(0x100);
750 for _ in 0..device_generate.0 {
751 queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);
752 }
753
754 // At this moment driver hasn't handled any interrupts yet, so it
755 // should inject interrupt.
756 assert_eq!(queue.trigger_interrupt(), true);
757
758 // Driver handle all the interrupts and update avail.used_event to 0x100
759 let mut driver_handled = device_generate;
760 let _ = mem.write_obj_at_addr(Le16::from(driver_handled.0), used_event_address);
761
762 // At this moment driver have handled all the interrupts, and
763 // device doesn't generate more data, so interrupt isn't needed.
764 assert_eq!(queue.trigger_interrupt(), false);
765
766 // Assume driver submit another u16::MAX - 0x100 req to device,
767 // Device has handled all of them, so increase self.next_used to u16::MAX
768 for _ in device_generate.0..u16::MAX {
769 queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);
770 }
771 device_generate = Wrapping(u16::MAX);
772
773 // At this moment driver just handled 0x100 interrupts, so it
774 // should inject interrupt.
775 assert_eq!(queue.trigger_interrupt(), true);
776
777 // driver handle all the interrupts and update avail.used_event to u16::MAX
778 driver_handled = device_generate;
779 let _ = mem.write_obj_at_addr(Le16::from(driver_handled.0), used_event_address);
780
781 // At this moment driver have handled all the interrupts, and
782 // device doesn't generate more data, so interrupt isn't needed.
783 assert_eq!(queue.trigger_interrupt(), false);
784
785 // Assume driver submit another 1 request,
786 // device has handled it, so wrap self.next_used to 0
787 queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);
788 device_generate += Wrapping(1);
789
790 // At this moment driver has handled all the previous interrupts, so it
791 // should inject interrupt again.
792 assert_eq!(queue.trigger_interrupt(), true);
793
794 // driver handle that interrupts and update avail.used_event to 0
795 driver_handled = device_generate;
796 let _ = mem.write_obj_at_addr(Le16::from(driver_handled.0), used_event_address);
797
798 // At this moment driver have handled all the interrupts, and
799 // device doesn't generate more data, so interrupt isn't needed.
800 assert_eq!(queue.trigger_interrupt(), false);
801 }
802
803 #[test]
804 fn queue_event_id_guest_slow() {
805 let mut queue =
806 QueueConfig::new(QUEUE_SIZE.try_into().unwrap(), 1 << VIRTIO_RING_F_EVENT_IDX);
807 let memory_start_addr = GuestAddress(0x0);
808 let mem = GuestMemory::new(&[(memory_start_addr, GUEST_MEMORY_SIZE)]).unwrap();
809 let mut queue = setup_vq(&mut queue, &mem);
810
811 // Offset of used_event within Avail structure
812 let used_event_offset = offset_of!(Avail, used_event) as u64;
813 let used_event_address = GuestAddress(AVAIL_OFFSET + used_event_offset);
814
815 // Assume driver submit 0x100 req to device,
816 // device have handled 0x100 req, so increase self.next_used to 0x100
817 let mut device_generate: Wrapping<u16> = Wrapping(0x100);
818 for _ in 0..device_generate.0 {
819 queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);
820 }
821
822 // At this moment driver hasn't handled any interrupts yet, so it
823 // should inject interrupt.
824 assert_eq!(queue.trigger_interrupt(), true);
825
826 // Driver handle part of the interrupts and update avail.used_event to 0x80
827 let mut driver_handled = Wrapping(0x80);
828 let _ = mem.write_obj_at_addr(Le16::from(driver_handled.0), used_event_address);
829
830 // At this moment driver hasn't finished last interrupt yet,
831 // so interrupt isn't needed.
832 assert_eq!(queue.trigger_interrupt(), false);
833
834 // Assume driver submit another 1 request,
835 // device has handled it, so increment self.next_used.
836 queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);
837 device_generate += Wrapping(1);
838
839 // At this moment driver hasn't finished last interrupt yet,
840 // so interrupt isn't needed.
841 assert_eq!(queue.trigger_interrupt(), false);
842
843 // Assume driver submit another u16::MAX - 0x101 req to device,
844 // Device has handled all of them, so increase self.next_used to u16::MAX
845 for _ in device_generate.0..u16::MAX {
846 queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);
847 }
848 device_generate = Wrapping(u16::MAX);
849
850 // At this moment driver hasn't finished last interrupt yet,
851 // so interrupt isn't needed.
852 assert_eq!(queue.trigger_interrupt(), false);
853
854 // driver handle most of the interrupts and update avail.used_event to u16::MAX - 1,
855 driver_handled = device_generate - Wrapping(1);
856 let _ = mem.write_obj_at_addr(Le16::from(driver_handled.0), used_event_address);
857
858 // Assume driver submit another 1 request,
859 // device has handled it, so wrap self.next_used to 0
860 queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);
861 device_generate += Wrapping(1);
862
863 // At this moment driver has already finished the last interrupt(0x100),
864 // and device service other request, so new interrupt is needed.
865 assert_eq!(queue.trigger_interrupt(), true);
866
867 // Assume driver submit another 1 request,
868 // device has handled it, so increment self.next_used to 1
869 queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);
870 device_generate += Wrapping(1);
871
872 // At this moment driver hasn't finished last interrupt((Wrapping(0)) yet,
873 // so interrupt isn't needed.
874 assert_eq!(queue.trigger_interrupt(), false);
875
876 // driver handle all the remain interrupts and wrap avail.used_event to 0x1.
877 driver_handled = device_generate;
878 let _ = mem.write_obj_at_addr(Le16::from(driver_handled.0), used_event_address);
879
880 // At this moment driver has handled all the interrupts, and
881 // device doesn't generate more data, so interrupt isn't needed.
882 assert_eq!(queue.trigger_interrupt(), false);
883
884 // Assume driver submit another 1 request,
885 // device has handled it, so increase self.next_used.
886 queue.add_used_with_bytes_written(fake_desc_chain(&mem), BUFFER_LEN);
887 device_generate += Wrapping(1);
888
889 // At this moment driver has finished all the previous interrupts, so it
890 // should inject interrupt again.
891 assert_eq!(queue.trigger_interrupt(), true);
892 }
893}