virtio_queue/
queue_sync.rs

1// Copyright (C) 2021 Alibaba Cloud. All rights reserved.
2//
3// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
4
5use std::num::Wrapping;
6use std::ops::Deref;
7use std::sync::atomic::Ordering;
8use std::sync::{Arc, Mutex, MutexGuard};
9
10use vm_memory::GuestMemory;
11
12use crate::{DescriptorChain, Error, Queue, QueueGuard, QueueT};
13
14/// Struct to maintain information and manipulate state of a virtio queue for multi-threaded
15/// context.
16///
17/// # Example
18///
19/// ```rust
20/// use virtio_queue::{Queue, QueueSync, QueueT};
21/// use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap};
22///
23/// let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
24/// let mut queue = QueueSync::new(1024).unwrap();
25///
26/// // First, the driver sets up the queue; this set up is done via writes on the bus (PCI, MMIO).
27/// queue.set_size(8);
28/// queue.set_desc_table_address(Some(0x1000), None);
29/// queue.set_avail_ring_address(Some(0x2000), None);
30/// queue.set_used_ring_address(Some(0x3000), None);
31/// queue.set_ready(true);
32/// // The user should check if the queue is valid before starting to use it.
33/// assert!(queue.is_valid(m.memory()));
34///
35/// // The memory object is not embedded in the `QueueSync`, so we have to pass it as a
36/// // parameter to the methods that access the guest memory. Examples would be:
37/// queue.add_used(m.memory(), 1, 0x100).unwrap();
38/// queue.needs_notification(m.memory()).unwrap();
39/// ```
40#[derive(Clone, Debug)]
41pub struct QueueSync {
42    state: Arc<Mutex<Queue>>,
43}
44
45impl QueueSync {
46    fn lock_state(&self) -> MutexGuard<'_, Queue> {
47        // Do not expect poisoned lock.
48        self.state.lock().unwrap()
49    }
50}
51
52impl<'a> QueueGuard<'a> for QueueSync {
53    type G = MutexGuard<'a, Queue>;
54}
55
56impl QueueT for QueueSync {
57    fn new(max_size: u16) -> Result<Self, Error> {
58        Ok(QueueSync {
59            state: Arc::new(Mutex::new(Queue::new(max_size)?)),
60        })
61    }
62
63    fn is_valid<M: GuestMemory>(&self, mem: &M) -> bool {
64        self.lock_state().is_valid(mem)
65    }
66
67    fn reset(&mut self) {
68        self.lock_state().reset();
69    }
70
71    fn lock(&mut self) -> <Self as QueueGuard<'_>>::G {
72        self.lock_state()
73    }
74
75    fn max_size(&self) -> u16 {
76        self.lock_state().max_size()
77    }
78
79    fn size(&self) -> u16 {
80        self.lock_state().size()
81    }
82
83    fn set_size(&mut self, size: u16) {
84        self.lock_state().set_size(size);
85    }
86
87    fn ready(&self) -> bool {
88        self.lock_state().ready()
89    }
90
91    fn set_ready(&mut self, ready: bool) {
92        self.lock_state().set_ready(ready)
93    }
94
95    fn set_desc_table_address(&mut self, low: Option<u32>, high: Option<u32>) {
96        self.lock_state().set_desc_table_address(low, high);
97    }
98
99    fn set_avail_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
100        self.lock_state().set_avail_ring_address(low, high);
101    }
102
103    fn set_used_ring_address(&mut self, low: Option<u32>, high: Option<u32>) {
104        self.lock_state().set_used_ring_address(low, high);
105    }
106
107    fn set_event_idx(&mut self, enabled: bool) {
108        self.lock_state().set_event_idx(enabled);
109    }
110
111    fn avail_idx<M>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error>
112    where
113        M: GuestMemory + ?Sized,
114    {
115        self.lock_state().avail_idx(mem, order)
116    }
117
118    fn used_idx<M: GuestMemory>(&self, mem: &M, order: Ordering) -> Result<Wrapping<u16>, Error> {
119        self.lock_state().used_idx(mem, order)
120    }
121
122    fn add_used<M: GuestMemory>(
123        &mut self,
124        mem: &M,
125        head_index: u16,
126        len: u32,
127    ) -> Result<(), Error> {
128        self.lock_state().add_used(mem, head_index, len)
129    }
130
131    fn enable_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<bool, Error> {
132        self.lock_state().enable_notification(mem)
133    }
134
135    fn disable_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<(), Error> {
136        self.lock_state().disable_notification(mem)
137    }
138
139    fn needs_notification<M: GuestMemory>(&mut self, mem: &M) -> Result<bool, Error> {
140        self.lock_state().needs_notification(mem)
141    }
142
143    fn next_avail(&self) -> u16 {
144        self.lock_state().next_avail()
145    }
146
147    fn set_next_avail(&mut self, next_avail: u16) {
148        self.lock_state().set_next_avail(next_avail);
149    }
150
151    fn next_used(&self) -> u16 {
152        self.lock_state().next_used()
153    }
154
155    fn set_next_used(&mut self, next_used: u16) {
156        self.lock_state().set_next_used(next_used);
157    }
158
159    fn desc_table(&self) -> u64 {
160        self.lock_state().desc_table()
161    }
162
163    fn avail_ring(&self) -> u64 {
164        self.lock_state().avail_ring()
165    }
166
167    fn used_ring(&self) -> u64 {
168        self.lock_state().used_ring()
169    }
170
171    fn event_idx_enabled(&self) -> bool {
172        self.lock_state().event_idx_enabled()
173    }
174
175    fn pop_descriptor_chain<M>(&mut self, mem: M) -> Option<DescriptorChain<M>>
176    where
177        M: Clone + Deref,
178        M::Target: GuestMemory,
179    {
180        self.lock_state().pop_descriptor_chain(mem)
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    use crate::defs::{DEFAULT_AVAIL_RING_ADDR, DEFAULT_DESC_TABLE_ADDR, DEFAULT_USED_RING_ADDR};
188    use std::sync::Barrier;
189    use virtio_bindings::bindings::virtio_ring::VRING_USED_F_NO_NOTIFY;
190    use vm_memory::{Address, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap};
191
192    #[test]
193    fn test_queue_state_sync() {
194        let mut q = QueueSync::new(0x1000).unwrap();
195        let mut q2 = q.clone();
196        let q3 = q.clone();
197        let barrier = Arc::new(Barrier::new(3));
198        let b2 = barrier.clone();
199        let b3 = barrier.clone();
200
201        let t1 = std::thread::spawn(move || {
202            {
203                let guard = q2.lock();
204                assert!(!guard.ready());
205            }
206            b2.wait();
207            b2.wait();
208            {
209                let guard = q2.lock();
210                assert!(guard.ready());
211            }
212        });
213
214        let t2 = std::thread::spawn(move || {
215            assert!(!q3.ready());
216            b3.wait();
217            b3.wait();
218            assert!(q3.ready());
219        });
220
221        barrier.wait();
222        q.set_ready(true);
223        barrier.wait();
224
225        t1.join().unwrap();
226        t2.join().unwrap();
227    }
228
229    #[test]
230    fn test_state_sync_add_used() {
231        let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
232        let mut q = QueueSync::new(0x100).unwrap();
233
234        q.set_desc_table_address(Some(0x1000), None);
235        q.set_avail_ring_address(Some(0x2000), None);
236        q.set_used_ring_address(Some(0x3000), None);
237        q.set_event_idx(true);
238        q.set_ready(true);
239        assert!(q.is_valid(m.memory()));
240        assert_eq!(q.lock().size(), 0x100);
241
242        assert_eq!(q.max_size(), 0x100);
243        assert_eq!(q.size(), 0x100);
244        q.set_size(0x80);
245        assert_eq!(q.size(), 0x80);
246        assert_eq!(q.max_size(), 0x100);
247        q.set_next_avail(5);
248        assert_eq!(q.next_avail(), 5);
249        q.set_next_used(3);
250        assert_eq!(q.next_used(), 3);
251        assert_eq!(
252            q.avail_idx(m.memory(), Ordering::Acquire).unwrap(),
253            Wrapping(0)
254        );
255        assert_eq!(
256            q.used_idx(m.memory(), Ordering::Acquire).unwrap(),
257            Wrapping(0)
258        );
259
260        assert_eq!(q.next_used(), 3);
261
262        // index too large
263        assert!(q.add_used(m.memory(), 0x200, 0x1000).is_err());
264        assert_eq!(q.next_used(), 3);
265
266        // should be ok
267        q.add_used(m.memory(), 1, 0x1000).unwrap();
268        assert_eq!(q.next_used(), 4);
269        assert_eq!(
270            q.used_idx(m.memory(), Ordering::Acquire).unwrap(),
271            Wrapping(4)
272        );
273    }
274
275    #[test]
276    fn test_sync_state_reset_queue() {
277        let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
278        let mut q = QueueSync::new(0x100).unwrap();
279
280        q.set_desc_table_address(Some(0x1000), None);
281        q.set_avail_ring_address(Some(0x2000), None);
282        q.set_used_ring_address(Some(0x3000), None);
283        q.set_event_idx(true);
284        q.set_next_avail(2);
285        q.set_next_used(2);
286        q.set_size(0x8);
287        q.set_ready(true);
288        assert!(q.is_valid(m.memory()));
289
290        q.needs_notification(m.memory()).unwrap();
291
292        assert_eq!(q.lock_state().size(), 0x8);
293        assert!(q.lock_state().ready());
294        assert_ne!(q.lock_state().desc_table(), DEFAULT_DESC_TABLE_ADDR);
295        assert_ne!(q.lock_state().avail_ring(), DEFAULT_AVAIL_RING_ADDR);
296        assert_ne!(q.lock_state().used_ring(), DEFAULT_USED_RING_ADDR);
297        assert_ne!(q.lock_state().next_avail(), 0);
298        assert_ne!(q.lock_state().next_used(), 0);
299        assert!(q.lock_state().event_idx_enabled());
300
301        q.reset();
302        assert_eq!(q.lock_state().size(), 0x100);
303        assert!(!q.lock_state().ready());
304        assert_eq!(q.lock_state().desc_table(), DEFAULT_DESC_TABLE_ADDR);
305        assert_eq!(q.lock_state().avail_ring(), DEFAULT_AVAIL_RING_ADDR);
306        assert_eq!(q.lock_state().used_ring(), DEFAULT_USED_RING_ADDR);
307        assert_eq!(q.lock_state().next_avail(), 0);
308        assert_eq!(q.lock_state().next_used(), 0);
309        assert!(!q.lock_state().event_idx_enabled());
310    }
311
312    #[test]
313    fn test_enable_disable_notification() {
314        let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
315        let mem = m.memory();
316        let mut q = QueueSync::new(0x100).unwrap();
317
318        q.set_desc_table_address(Some(0x1000), None);
319        assert_eq!(q.desc_table(), 0x1000);
320        q.set_avail_ring_address(Some(0x2000), None);
321        assert_eq!(q.avail_ring(), 0x2000);
322        q.set_used_ring_address(Some(0x3000), None);
323        assert_eq!(q.used_ring(), 0x3000);
324        q.set_ready(true);
325        assert!(q.is_valid(mem));
326
327        let used_addr = GuestAddress(q.lock_state().used_ring());
328
329        assert!(!q.event_idx_enabled());
330        q.enable_notification(mem).unwrap();
331        let v = m.read_obj::<u16>(used_addr).map(u16::from_le).unwrap();
332        assert_eq!(v, 0);
333
334        q.disable_notification(m.memory()).unwrap();
335        let v = m.read_obj::<u16>(used_addr).map(u16::from_le).unwrap();
336        assert_eq!(v, VRING_USED_F_NO_NOTIFY as u16);
337
338        q.enable_notification(mem).unwrap();
339        let v = m.read_obj::<u16>(used_addr).map(u16::from_le).unwrap();
340        assert_eq!(v, 0);
341
342        q.set_event_idx(true);
343        let avail_addr = GuestAddress(q.lock_state().avail_ring());
344        m.write_obj::<u16>(u16::to_le(2), avail_addr.unchecked_add(2))
345            .unwrap();
346
347        assert!(q.enable_notification(mem).unwrap());
348        q.lock_state().set_next_avail(2);
349        assert!(!q.enable_notification(mem).unwrap());
350
351        m.write_obj::<u16>(u16::to_le(8), avail_addr.unchecked_add(2))
352            .unwrap();
353
354        assert!(q.enable_notification(mem).unwrap());
355        q.lock_state().set_next_avail(8);
356        assert!(!q.enable_notification(mem).unwrap());
357    }
358}