virtio_queue_ser/
state.rs

1// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2//
3// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
4use serde::{Deserialize, Serialize};
5use versionize::{VersionMap, Versionize, VersionizeResult};
6use versionize_derive::Versionize;
7use virtio_queue::QueueState;
8
9/// Wrapper over a `QueueState` that has serialization capabilities.
10#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, Versionize)]
11pub struct QueueStateSer {
12    /// The maximum size in elements offered by the device.
13    pub max_size: u16,
14    /// Tail position of the available ring.
15    pub next_avail: u16,
16    /// Head position of the used ring.
17    pub next_used: u16,
18    /// VIRTIO_F_RING_EVENT_IDX negotiated.
19    pub event_idx_enabled: bool,
20    /// The queue size in elements the driver selected.
21    pub size: u16,
22    /// Indicates if the queue finished with the configuration.
23    pub ready: bool,
24    /// Guest physical address of the descriptor table.
25    pub desc_table: u64,
26    /// Guest physical address of the available ring.
27    pub avail_ring: u64,
28    /// Guest physical address of the used ring.
29    pub used_ring: u64,
30}
31
32// The following `From` implementations can be used to convert from a `QueueStateSer` to the
33// `QueueState` from the base crate and vice versa.
34impl From<&QueueStateSer> for QueueState {
35    fn from(state: &QueueStateSer) -> Self {
36        QueueState {
37            max_size: state.max_size,
38            next_avail: state.next_avail,
39            next_used: state.next_used,
40            event_idx_enabled: state.event_idx_enabled,
41            size: state.size,
42            ready: state.ready,
43            desc_table: state.desc_table,
44            avail_ring: state.avail_ring,
45            used_ring: state.used_ring,
46        }
47    }
48}
49
50impl From<&QueueState> for QueueStateSer {
51    fn from(state: &QueueState) -> Self {
52        QueueStateSer {
53            max_size: state.max_size,
54            next_avail: state.next_avail,
55            next_used: state.next_used,
56            event_idx_enabled: state.event_idx_enabled,
57            size: state.size,
58            ready: state.ready,
59            desc_table: state.desc_table,
60            avail_ring: state.avail_ring,
61            used_ring: state.used_ring,
62        }
63    }
64}
65
66impl Default for QueueStateSer {
67    fn default() -> Self {
68        QueueStateSer::from(&QueueState::default())
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use virtio_queue::{Error, Queue};
76
77    #[test]
78    fn test_state_ser() {
79        const SOME_VALUE: u16 = 16;
80
81        let state = QueueState {
82            max_size: SOME_VALUE * 2,
83            next_avail: SOME_VALUE - 1,
84            next_used: SOME_VALUE + 1,
85            event_idx_enabled: false,
86            size: SOME_VALUE,
87            ready: true,
88            desc_table: SOME_VALUE as u64,
89            avail_ring: SOME_VALUE as u64 * 2,
90            used_ring: SOME_VALUE as u64 * 4,
91        };
92
93        let ser_state = QueueStateSer::from(&state);
94        let state_from_ser = QueueState::from(&ser_state);
95
96        // Check that the old and the new state are identical when using the intermediate
97        // `QueueStateSer` object.
98        assert_eq!(state, state_from_ser);
99
100        // Test the `Default` implementation of `QueueStateSer`.
101        let default_queue_state_ser = QueueStateSer::default();
102        assert_eq!(
103            QueueState::from(&default_queue_state_ser),
104            QueueState::default()
105        );
106    }
107
108    #[test]
109    fn test_ser_with_len_zero() {
110        // This is a regression test that tests that a queue where the size is set to 0 does not
111        // cause any problems when poping the descriptor chain.
112        let queue_ser = QueueStateSer {
113            max_size: 16,
114            next_avail: 0,
115            next_used: 0,
116            event_idx_enabled: false,
117            size: 0,
118            ready: true,
119            desc_table: 0,
120            avail_ring: 256,
121            used_ring: 276,
122        };
123
124        let queue_state = QueueState::from(&queue_ser);
125        let err = Queue::try_from(queue_state).unwrap_err();
126        assert_eq!(err, Error::InvalidSize);
127    }
128}