Skip to main content

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
184impl From<Queue> for QueueSync {
185    fn from(queue: Queue) -> Self {
186        QueueSync {
187            state: Arc::new(Mutex::new(queue)),
188        }
189    }
190}
191
192impl From<Arc<Mutex<Queue>>> for QueueSync {
193    fn from(state: Arc<Mutex<Queue>>) -> Self {
194        QueueSync { state }
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201    use crate::defs::{DEFAULT_AVAIL_RING_ADDR, DEFAULT_DESC_TABLE_ADDR, DEFAULT_USED_RING_ADDR};
202    use std::sync::Barrier;
203    use virtio_bindings::bindings::virtio_ring::VRING_USED_F_NO_NOTIFY;
204    use vm_memory::{Address, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap};
205
206    #[test]
207    fn test_queue_state_sync() {
208        let mut q = QueueSync::new(0x1000).unwrap();
209        let mut q2 = q.clone();
210        let q3 = q.clone();
211        let barrier = Arc::new(Barrier::new(3));
212        let b2 = barrier.clone();
213        let b3 = barrier.clone();
214
215        let t1 = std::thread::spawn(move || {
216            {
217                let guard = q2.lock();
218                assert!(!guard.ready());
219            }
220            b2.wait();
221            b2.wait();
222            {
223                let guard = q2.lock();
224                assert!(guard.ready());
225            }
226        });
227
228        let t2 = std::thread::spawn(move || {
229            assert!(!q3.ready());
230            b3.wait();
231            b3.wait();
232            assert!(q3.ready());
233        });
234
235        barrier.wait();
236        q.set_ready(true);
237        barrier.wait();
238
239        t1.join().unwrap();
240        t2.join().unwrap();
241    }
242
243    #[test]
244    fn test_state_sync_add_used() {
245        let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
246        let mut q = QueueSync::new(0x100).unwrap();
247
248        q.set_desc_table_address(Some(0x1000), None);
249        q.set_avail_ring_address(Some(0x2000), None);
250        q.set_used_ring_address(Some(0x3000), None);
251        q.set_event_idx(true);
252        q.set_ready(true);
253        assert!(q.is_valid(m.memory()));
254        assert_eq!(q.lock().size(), 0x100);
255
256        assert_eq!(q.max_size(), 0x100);
257        assert_eq!(q.size(), 0x100);
258        q.set_size(0x80);
259        assert_eq!(q.size(), 0x80);
260        assert_eq!(q.max_size(), 0x100);
261        q.set_next_avail(5);
262        assert_eq!(q.next_avail(), 5);
263        q.set_next_used(3);
264        assert_eq!(q.next_used(), 3);
265        assert_eq!(
266            q.avail_idx(m.memory(), Ordering::Acquire).unwrap(),
267            Wrapping(0)
268        );
269        assert_eq!(
270            q.used_idx(m.memory(), Ordering::Acquire).unwrap(),
271            Wrapping(0)
272        );
273
274        assert_eq!(q.next_used(), 3);
275
276        // index too large
277        assert!(q.add_used(m.memory(), 0x200, 0x1000).is_err());
278        assert_eq!(q.next_used(), 3);
279
280        // should be ok
281        q.add_used(m.memory(), 1, 0x1000).unwrap();
282        assert_eq!(q.next_used(), 4);
283        assert_eq!(
284            q.used_idx(m.memory(), Ordering::Acquire).unwrap(),
285            Wrapping(4)
286        );
287    }
288
289    #[test]
290    fn test_sync_state_reset_queue() {
291        let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
292        let mut q = QueueSync::new(0x100).unwrap();
293
294        q.set_desc_table_address(Some(0x1000), None);
295        q.set_avail_ring_address(Some(0x2000), None);
296        q.set_used_ring_address(Some(0x3000), None);
297        q.set_event_idx(true);
298        q.set_next_avail(2);
299        q.set_next_used(2);
300        q.set_size(0x8);
301        q.set_ready(true);
302        assert!(q.is_valid(m.memory()));
303
304        q.needs_notification(m.memory()).unwrap();
305
306        assert_eq!(q.lock_state().size(), 0x8);
307        assert!(q.lock_state().ready());
308        assert_ne!(q.lock_state().desc_table(), DEFAULT_DESC_TABLE_ADDR);
309        assert_ne!(q.lock_state().avail_ring(), DEFAULT_AVAIL_RING_ADDR);
310        assert_ne!(q.lock_state().used_ring(), DEFAULT_USED_RING_ADDR);
311        assert_ne!(q.lock_state().next_avail(), 0);
312        assert_ne!(q.lock_state().next_used(), 0);
313        assert!(q.lock_state().event_idx_enabled());
314
315        q.reset();
316        assert_eq!(q.lock_state().size(), 0x100);
317        assert!(!q.lock_state().ready());
318        assert_eq!(q.lock_state().desc_table(), DEFAULT_DESC_TABLE_ADDR);
319        assert_eq!(q.lock_state().avail_ring(), DEFAULT_AVAIL_RING_ADDR);
320        assert_eq!(q.lock_state().used_ring(), DEFAULT_USED_RING_ADDR);
321        assert_eq!(q.lock_state().next_avail(), 0);
322        assert_eq!(q.lock_state().next_used(), 0);
323        assert!(!q.lock_state().event_idx_enabled());
324    }
325
326    #[test]
327    fn test_enable_disable_notification() {
328        let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap();
329        let mem = m.memory();
330        let mut q = QueueSync::new(0x100).unwrap();
331
332        q.set_desc_table_address(Some(0x1000), None);
333        assert_eq!(q.desc_table(), 0x1000);
334        q.set_avail_ring_address(Some(0x2000), None);
335        assert_eq!(q.avail_ring(), 0x2000);
336        q.set_used_ring_address(Some(0x3000), None);
337        assert_eq!(q.used_ring(), 0x3000);
338        q.set_ready(true);
339        assert!(q.is_valid(mem));
340
341        let used_addr = GuestAddress(q.lock_state().used_ring());
342
343        assert!(!q.event_idx_enabled());
344        q.enable_notification(mem).unwrap();
345        let v = m.read_obj::<u16>(used_addr).map(u16::from_le).unwrap();
346        assert_eq!(v, 0);
347
348        q.disable_notification(m.memory()).unwrap();
349        let v = m.read_obj::<u16>(used_addr).map(u16::from_le).unwrap();
350        assert_eq!(v, VRING_USED_F_NO_NOTIFY as u16);
351
352        q.enable_notification(mem).unwrap();
353        let v = m.read_obj::<u16>(used_addr).map(u16::from_le).unwrap();
354        assert_eq!(v, 0);
355
356        q.set_event_idx(true);
357        let avail_addr = GuestAddress(q.lock_state().avail_ring());
358        m.write_obj::<u16>(u16::to_le(2), avail_addr.unchecked_add(2))
359            .unwrap();
360
361        assert!(q.enable_notification(mem).unwrap());
362        q.lock_state().set_next_avail(2);
363        assert!(!q.enable_notification(mem).unwrap());
364
365        m.write_obj::<u16>(u16::to_le(8), avail_addr.unchecked_add(2))
366            .unwrap();
367
368        assert!(q.enable_notification(mem).unwrap());
369        q.lock_state().set_next_avail(8);
370        assert!(!q.enable_notification(mem).unwrap());
371    }
372
373    #[test]
374    fn test_queue_sync_from() {
375        let q = Queue::new(0x100).unwrap();
376        let q_sync = QueueSync::from(q);
377        assert_eq!(q_sync.max_size(), 0x100);
378
379        let q2 = Queue::new(0x200).unwrap();
380        let state = Arc::new(Mutex::new(q2));
381        let q_sync2 = QueueSync::from(state);
382        assert_eq!(q_sync2.max_size(), 0x200);
383    }
384}