xref: /aosp_15_r20/external/crosvm/devices/src/virtio/queue/packed_queue.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2023 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 #![deny(missing_docs)]
6 
7 use std::num::Wrapping;
8 use std::sync::atomic::fence;
9 use std::sync::atomic::Ordering;
10 
11 use anyhow::bail;
12 use anyhow::Result;
13 use base::error;
14 use base::warn;
15 use base::Event;
16 use serde::Deserialize;
17 use serde::Serialize;
18 use virtio_sys::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
19 use vm_memory::GuestAddress;
20 use vm_memory::GuestMemory;
21 
22 use crate::virtio::descriptor_chain::DescriptorChain;
23 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_AVAIL;
24 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_USED;
25 use crate::virtio::descriptor_chain::VIRTQ_DESC_F_WRITE;
26 use crate::virtio::queue::packed_descriptor_chain::PackedDesc;
27 use crate::virtio::queue::packed_descriptor_chain::PackedDescEvent;
28 use crate::virtio::queue::packed_descriptor_chain::PackedDescriptorChain;
29 use crate::virtio::queue::packed_descriptor_chain::PackedNotificationType;
30 use crate::virtio::queue::packed_descriptor_chain::RING_EVENT_FLAGS_DESC;
31 use crate::virtio::Interrupt;
32 use crate::virtio::QueueConfig;
33 
34 #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
35 struct PackedQueueIndex {
36     wrap_counter: bool,
37     index: Wrapping<u16>,
38 }
39 impl PackedQueueIndex {
new(wrap_counter: bool, index: u16) -> Self40     pub fn new(wrap_counter: bool, index: u16) -> Self {
41         Self {
42             wrap_counter,
43             index: Wrapping(index),
44         }
45     }
46 
new_from_desc(desc: u16) -> Self47     pub fn new_from_desc(desc: u16) -> Self {
48         let wrap_counter: bool = (desc >> 15) == 1;
49         let mask: u16 = 0x7fff;
50         let index = desc & mask;
51         Self::new(wrap_counter, index)
52     }
53 
to_desc(self) -> PackedDescEvent54     pub fn to_desc(self) -> PackedDescEvent {
55         let flag = RING_EVENT_FLAGS_DESC;
56         let mut desc = self.index.0;
57         if self.wrap_counter {
58             desc |= 1 << 15;
59         }
60         PackedDescEvent {
61             desc: desc.into(),
62             flag: flag.into(),
63         }
64     }
65 
add_index(&mut self, index_value: u16, size: u16)66     fn add_index(&mut self, index_value: u16, size: u16) {
67         let new_index = self.index.0 + index_value;
68         if new_index < size {
69             self.index = Wrapping(new_index);
70         } else {
71             self.index = Wrapping(new_index - size);
72             self.wrap_counter = !self.wrap_counter;
73         }
74     }
75 }
76 
77 impl Default for PackedQueueIndex {
default() -> Self78     fn default() -> Self {
79         Self::new(true, 0)
80     }
81 }
82 
83 #[derive(Debug)]
84 pub struct PackedQueue {
85     mem: GuestMemory,
86 
87     event: Event,
88     interrupt: Interrupt,
89 
90     // The queue size in elements the driver selected
91     size: u16,
92 
93     // MSI-X vector for the queue. Don't care for INTx
94     vector: u16,
95 
96     // Internal index counter to keep track of where to poll
97     avail_index: PackedQueueIndex,
98     use_index: PackedQueueIndex,
99     signalled_used_index: PackedQueueIndex,
100 
101     // Device feature bits accepted by the driver
102     features: u64,
103 
104     // Guest physical address of the descriptor table
105     desc_table: GuestAddress,
106 
107     // Write-only by the device, Including information for reducing the number of device events
108     device_event_suppression: GuestAddress,
109 
110     // Read-only by the device, Includes information for reducing the number of driver events
111     driver_event_suppression: GuestAddress,
112 }
113 
114 #[derive(Serialize, Deserialize)]
115 pub struct PackedQueueSnapshot {
116     size: u16,
117     vector: u16,
118     avail_index: PackedQueueIndex,
119     use_index: PackedQueueIndex,
120     signalled_used_index: PackedQueueIndex,
121     features: u64,
122     desc_table: GuestAddress,
123     device_event_suppression: GuestAddress,
124     driver_event_suppression: GuestAddress,
125 }
126 
127 impl PackedQueue {
128     /// Constructs an empty virtio queue with the given `max_size`.
new( config: &QueueConfig, mem: &GuestMemory, event: Event, interrupt: Interrupt, ) -> Result<Self>129     pub fn new(
130         config: &QueueConfig,
131         mem: &GuestMemory,
132         event: Event,
133         interrupt: Interrupt,
134     ) -> Result<Self> {
135         let size = config.size();
136 
137         let desc_table = config.desc_table();
138         let driver_area = config.avail_ring();
139         let device_area = config.used_ring();
140 
141         // Validate addresses and queue size to ensure that address calculation won't overflow.
142         let ring_sizes = Self::area_sizes(size, desc_table, driver_area, device_area);
143         let rings = ring_sizes.iter().zip(vec![
144             "descriptor table",
145             "driver_event_suppression",
146             "device_event_suppression",
147         ]);
148 
149         for ((addr, size), name) in rings {
150             if addr.checked_add(*size as u64).is_none() {
151                 bail!(
152                     "virtio queue {} goes out of bounds: start:0x{:08x} size:0x{:08x}",
153                     name,
154                     addr.offset(),
155                     size,
156                 );
157             }
158         }
159 
160         Ok(PackedQueue {
161             mem: mem.clone(),
162             event,
163             interrupt,
164             size,
165             vector: config.vector(),
166             desc_table: config.desc_table(),
167             driver_event_suppression: config.avail_ring(),
168             device_event_suppression: config.used_ring(),
169             features: config.acked_features(),
170             avail_index: PackedQueueIndex::default(),
171             use_index: PackedQueueIndex::default(),
172             signalled_used_index: PackedQueueIndex::default(),
173         })
174     }
175 
vhost_user_reclaim(&mut self, _vring_base: u16)176     pub fn vhost_user_reclaim(&mut self, _vring_base: u16) {
177         // TODO: b/331466964 - Need more than `vring_base` to reclaim a packed virtqueue.
178         unimplemented!()
179     }
180 
next_avail_to_process(&self) -> u16181     pub fn next_avail_to_process(&self) -> u16 {
182         self.avail_index.index.0
183     }
184 
185     /// Return the actual size of the queue, as the driver may not set up a
186     /// queue as big as the device allows.
size(&self) -> u16187     pub fn size(&self) -> u16 {
188         self.size
189     }
190 
191     /// Getter for vector field
vector(&self) -> u16192     pub fn vector(&self) -> u16 {
193         self.vector
194     }
195 
196     /// Getter for descriptor area
desc_table(&self) -> GuestAddress197     pub fn desc_table(&self) -> GuestAddress {
198         self.desc_table
199     }
200 
201     /// Getter for driver area
avail_ring(&self) -> GuestAddress202     pub fn avail_ring(&self) -> GuestAddress {
203         self.driver_event_suppression
204     }
205 
206     /// Getter for device area
used_ring(&self) -> GuestAddress207     pub fn used_ring(&self) -> GuestAddress {
208         self.device_event_suppression
209     }
210 
211     /// Get a reference to the queue's "kick event"
event(&self) -> &Event212     pub fn event(&self) -> &Event {
213         &self.event
214     }
215 
216     /// Get a reference to the queue's interrupt
interrupt(&self) -> &Interrupt217     pub fn interrupt(&self) -> &Interrupt {
218         &self.interrupt
219     }
220 
area_sizes( queue_size: u16, desc_table: GuestAddress, driver_area: GuestAddress, device_area: GuestAddress, ) -> Vec<(GuestAddress, usize)>221     fn area_sizes(
222         queue_size: u16,
223         desc_table: GuestAddress,
224         driver_area: GuestAddress,
225         device_area: GuestAddress,
226     ) -> Vec<(GuestAddress, usize)> {
227         vec![
228             (desc_table, 16 * queue_size as usize),
229             (driver_area, 4),
230             (device_area, 4),
231         ]
232     }
233 
234     /// Set the device event suppression
235     ///
236     /// This field is used to specify the timing of when the driver notifies the
237     /// device that the descriptor table is ready to be processed.
set_avail_event(&mut self, event: PackedDescEvent)238     fn set_avail_event(&mut self, event: PackedDescEvent) {
239         fence(Ordering::SeqCst);
240         self.mem
241             .write_obj_at_addr_volatile(event, self.device_event_suppression)
242             .unwrap();
243     }
244 
245     // Get the driver event suppression.
246     // This field is used to specify the timing of when the device notifies the
247     // driver that the descriptor table is ready to be processed.
get_driver_event(&self) -> PackedDescEvent248     fn get_driver_event(&self) -> PackedDescEvent {
249         fence(Ordering::SeqCst);
250 
251         let desc: PackedDescEvent = self
252             .mem
253             .read_obj_from_addr_volatile(self.driver_event_suppression)
254             .unwrap();
255         desc
256     }
257 
258     /// Get the first available descriptor chain without removing it from the queue.
259     /// Call `pop_peeked` to remove the returned descriptor chain from the queue.
peek(&mut self) -> Option<DescriptorChain>260     pub fn peek(&mut self) -> Option<DescriptorChain> {
261         let desc_addr = self
262             .desc_table
263             .checked_add((self.avail_index.index.0 as u64) * 16)
264             .expect("peeked address will not overflow");
265 
266         let desc = self
267             .mem
268             .read_obj_from_addr::<PackedDesc>(desc_addr)
269             .inspect_err(|_e| {
270                 error!("failed to read desc {:#x}", desc_addr.offset());
271             })
272             .ok()?;
273 
274         if !desc.is_available(self.avail_index.wrap_counter as u16) {
275             return None;
276         }
277 
278         // This fence ensures that subsequent reads from the descriptor do not
279         // get reordered and happen only after verifying the descriptor table is
280         // available.
281         fence(Ordering::SeqCst);
282 
283         let chain = PackedDescriptorChain::new(
284             &self.mem,
285             self.desc_table,
286             self.size,
287             self.avail_index.wrap_counter,
288             self.avail_index.index.0,
289         );
290 
291         match DescriptorChain::new(chain, &self.mem, self.avail_index.index.0) {
292             Ok(descriptor_chain) => Some(descriptor_chain),
293             Err(e) => {
294                 error!("{:#}", e);
295                 None
296             }
297         }
298     }
299 
300     /// Remove the first available descriptor chain from the queue.
301     /// This function should only be called immediately following `peek` and must be passed a
302     /// reference to the same `DescriptorChain` returned by the most recent `peek`.
pop_peeked(&mut self, descriptor_chain: &DescriptorChain)303     pub(super) fn pop_peeked(&mut self, descriptor_chain: &DescriptorChain) {
304         self.avail_index
305             .add_index(descriptor_chain.count, self.size());
306         if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) != 0 {
307             self.set_avail_event(self.avail_index.to_desc());
308         }
309     }
310 
311     /// Write to first descriptor in descriptor chain to mark descriptor chain as used
add_used(&mut self, desc_chain: DescriptorChain, len: u32)312     pub fn add_used(&mut self, desc_chain: DescriptorChain, len: u32) {
313         let desc_index = desc_chain.index();
314         if desc_index >= self.size {
315             error!(
316                 "attempted to add out of bounds descriptor to used ring: {}",
317                 desc_index
318             );
319             return;
320         }
321 
322         let chain_id = desc_chain
323             .id
324             .expect("Packed descriptor chain should have id");
325 
326         let desc_addr = self
327             .desc_table
328             .checked_add(self.use_index.index.0 as u64 * 16)
329             .expect("Descriptor address should not overflow.");
330 
331         // Write to len field
332         self.mem
333             .write_obj_at_addr(
334                 len,
335                 desc_addr
336                     .checked_add(8)
337                     .expect("Descriptor address should not overflow."),
338             )
339             .unwrap();
340 
341         // Write to id field
342         self.mem
343             .write_obj_at_addr(
344                 chain_id,
345                 desc_addr
346                     .checked_add(12)
347                     .expect("Descriptor address should not overflow."),
348             )
349             .unwrap();
350 
351         let wrap_counter = self.use_index.wrap_counter;
352 
353         let mut flags: u16 = 0;
354         if wrap_counter {
355             flags = flags | VIRTQ_DESC_F_USED | VIRTQ_DESC_F_AVAIL;
356         }
357         if len > 0 {
358             flags |= VIRTQ_DESC_F_WRITE;
359         }
360 
361         // Writing to flags should come at the very end to avoid showing the
362         // driver fragmented descriptor data
363         fence(Ordering::SeqCst);
364 
365         self.mem
366             .write_obj_at_addr_volatile(flags, desc_addr.unchecked_add(14))
367             .unwrap();
368 
369         self.use_index.add_index(desc_chain.count, self.size());
370     }
371 
372     /// Returns if the queue should have an interrupt sent based on its state.
queue_wants_interrupt(&mut self) -> bool373     fn queue_wants_interrupt(&mut self) -> bool {
374         let driver_event = self.get_driver_event();
375         match driver_event.notification_type() {
376             PackedNotificationType::Enable => true,
377             PackedNotificationType::Disable => false,
378             PackedNotificationType::Desc(desc) => {
379                 if self.features & ((1u64) << VIRTIO_RING_F_EVENT_IDX) == 0 {
380                     warn!("This is undefined behavior. We should actually send error in this case");
381                     return true;
382                 }
383 
384                 // Reserved current use_index for next notify
385                 let old = self.signalled_used_index;
386                 self.signalled_used_index = self.use_index;
387 
388                 // Get desc_event_off and desc_event_wrap from driver event suppress area
389                 let event_index: PackedQueueIndex = PackedQueueIndex::new_from_desc(desc);
390 
391                 let event_idx = event_index.index;
392                 let old_idx = old.index;
393                 let new_idx = self.use_index.index;
394 
395                 // In qemu's implementation, there's an additional calculation,
396                 // need to verify its correctness.
397                 // if event_index.wrap_counter != self.use_index.wrap_counter {
398                 //     event_idx -= self.size() as u16;
399                 // }
400 
401                 (new_idx - event_idx - Wrapping(1)) < (new_idx - old_idx)
402             }
403         };
404         true
405     }
406 
407     /// inject interrupt into guest on this queue
408     /// return true: interrupt is injected into guest for this queue
409     ///        false: interrupt isn't injected
trigger_interrupt(&mut self) -> bool410     pub fn trigger_interrupt(&mut self) -> bool {
411         if self.queue_wants_interrupt() {
412             self.interrupt.signal_used_queue(self.vector);
413             true
414         } else {
415             false
416         }
417     }
418 
419     /// Acknowledges that this set of features should be enabled on this queue.
ack_features(&mut self, features: u64)420     pub fn ack_features(&mut self, features: u64) {
421         self.features |= features;
422     }
423 
424     /// TODO: b/290307056 - Implement snapshot for packed virtqueue,
425     /// add tests to validate.
snapshot(&self) -> Result<serde_json::Value>426     pub fn snapshot(&self) -> Result<serde_json::Value> {
427         bail!("Snapshot for packed virtqueue not implemented.");
428     }
429 
430     /// TODO: b/290307056 - Implement restore for packed virtqueue,
431     /// add tests to validate.
restore( _queue_value: serde_json::Value, _mem: &GuestMemory, _event: Event, _interrupt: Interrupt, ) -> Result<PackedQueue>432     pub fn restore(
433         _queue_value: serde_json::Value,
434         _mem: &GuestMemory,
435         _event: Event,
436         _interrupt: Interrupt,
437     ) -> Result<PackedQueue> {
438         bail!("Restore for packed virtqueue not implemented.");
439     }
440 }
441