Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/queue/split_queue.rs
5394 views
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
5
use std::num::Wrapping;
6
use std::sync::atomic::fence;
7
use std::sync::atomic::AtomicU16;
8
use std::sync::atomic::Ordering;
9
10
use anyhow::bail;
11
use anyhow::Context;
12
use anyhow::Result;
13
use base::error;
14
use base::Event;
15
use data_model::Le32;
16
use serde::Deserialize;
17
use serde::Serialize;
18
use snapshot::AnySnapshot;
19
use virtio_sys::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
20
use vm_memory::GuestAddress;
21
use vm_memory::GuestMemory;
22
use zerocopy::FromBytes;
23
use zerocopy::Immutable;
24
use zerocopy::IntoBytes;
25
use zerocopy::KnownLayout;
26
27
use crate::virtio::DescriptorChain;
28
use crate::virtio::Interrupt;
29
use crate::virtio::QueueConfig;
30
use crate::virtio::SplitDescriptorChain;
31
32
#[allow(dead_code)]
33
const VIRTQ_USED_F_NO_NOTIFY: u16 = 0x1;
34
#[allow(dead_code)]
35
const VIRTQ_AVAIL_F_NO_INTERRUPT: u16 = 0x1;
36
37
/// An activated virtio queue with split queue layout.
38
#[derive(Debug)]
39
pub 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)]
70
pub 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)]
84
struct virtq_used_elem {
85
id: Le32,
86
len: Le32,
87
}
88
89
impl 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)]
622
mod 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
}
894
895