vmm/devices/virtio/net/
persist.rs

1// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Defines the structures needed for saving/restoring net devices.
5
6use std::io;
7use std::sync::{Arc, Mutex};
8
9use serde::{Deserialize, Serialize};
10
11use super::device::{Net, RxBuffers};
12use super::{NET_NUM_QUEUES, NET_QUEUE_MAX_SIZE, RX_INDEX, TapError};
13use crate::devices::virtio::device::{ActiveState, DeviceState};
14use crate::devices::virtio::generated::virtio_ids::VIRTIO_ID_NET;
15use crate::devices::virtio::persist::{PersistError as VirtioStateError, VirtioDeviceState};
16use crate::devices::virtio::transport::VirtioInterrupt;
17use crate::mmds::data_store::Mmds;
18use crate::mmds::ns::MmdsNetworkStack;
19use crate::mmds::persist::MmdsNetworkStackState;
20use crate::rate_limiter::RateLimiter;
21use crate::rate_limiter::persist::RateLimiterState;
22use crate::snapshot::Persist;
23use crate::utils::net::mac::MacAddr;
24use crate::vstate::memory::GuestMemoryMmap;
25
26/// Information about the network config's that are saved
27/// at snapshot.
28#[derive(Debug, Default, Clone, Serialize, Deserialize)]
29pub struct NetConfigSpaceState {
30    guest_mac: Option<MacAddr>,
31}
32
33/// Information about the network device that are saved
34/// at snapshot.
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct NetState {
37    pub id: String,
38    pub tap_if_name: String,
39    rx_rate_limiter_state: RateLimiterState,
40    tx_rate_limiter_state: RateLimiterState,
41    /// The associated MMDS network stack.
42    pub mmds_ns: Option<MmdsNetworkStackState>,
43    config_space: NetConfigSpaceState,
44    pub virtio_state: VirtioDeviceState,
45}
46
47/// Auxiliary structure for creating a device when resuming from a snapshot.
48#[derive(Debug)]
49pub struct NetConstructorArgs {
50    /// Pointer to guest memory.
51    pub mem: GuestMemoryMmap,
52    /// Pointer to the MMDS data store.
53    pub mmds: Option<Arc<Mutex<Mmds>>>,
54}
55
56/// Errors triggered when trying to construct a network device at resume time.
57#[derive(Debug, thiserror::Error, displaydoc::Display)]
58pub enum NetPersistError {
59    /// Failed to create a network device: {0}
60    CreateNet(#[from] super::NetError),
61    /// Failed to create a rate limiter: {0}
62    CreateRateLimiter(#[from] io::Error),
63    /// Failed to re-create the virtio state (i.e queues etc): {0}
64    VirtioState(#[from] VirtioStateError),
65    /// Indicator that no MMDS is associated with this device.
66    NoMmdsDataStore,
67    /// Setting tap interface offload flags failed: {0}
68    TapSetOffload(TapError),
69}
70
71impl Persist<'_> for Net {
72    type State = NetState;
73    type ConstructorArgs = NetConstructorArgs;
74    type Error = NetPersistError;
75
76    fn save(&self) -> Self::State {
77        NetState {
78            id: self.id().clone(),
79            tap_if_name: self.iface_name(),
80            rx_rate_limiter_state: self.rx_rate_limiter.save(),
81            tx_rate_limiter_state: self.tx_rate_limiter.save(),
82            mmds_ns: self.mmds_ns.as_ref().map(|mmds| mmds.save()),
83            config_space: NetConfigSpaceState {
84                guest_mac: self.guest_mac,
85            },
86            virtio_state: VirtioDeviceState::from_device(self),
87        }
88    }
89
90    fn restore(
91        constructor_args: Self::ConstructorArgs,
92        state: &Self::State,
93    ) -> Result<Self, Self::Error> {
94        // RateLimiter::restore() can fail at creating a timerfd.
95        let rx_rate_limiter = RateLimiter::restore((), &state.rx_rate_limiter_state)?;
96        let tx_rate_limiter = RateLimiter::restore((), &state.tx_rate_limiter_state)?;
97        let mut net = Net::new(
98            state.id.clone(),
99            &state.tap_if_name,
100            state.config_space.guest_mac,
101            rx_rate_limiter,
102            tx_rate_limiter,
103        )?;
104
105        // We trust the MMIODeviceManager::restore to pass us an MMDS data store reference if
106        // there is at least one net device having the MMDS NS present and/or the mmds version was
107        // persisted in the snapshot.
108        if let Some(mmds_ns) = &state.mmds_ns {
109            // We're safe calling unwrap() to discard the error, as MmdsNetworkStack::restore()
110            // always returns Ok.
111            net.mmds_ns = Some(
112                MmdsNetworkStack::restore(
113                    constructor_args
114                        .mmds
115                        .map_or_else(|| Err(NetPersistError::NoMmdsDataStore), Ok)?,
116                    mmds_ns,
117                )
118                .unwrap(),
119            );
120        }
121
122        net.queues = state.virtio_state.build_queues_checked(
123            &constructor_args.mem,
124            VIRTIO_ID_NET,
125            NET_NUM_QUEUES,
126            NET_QUEUE_MAX_SIZE,
127        )?;
128        net.avail_features = state.virtio_state.avail_features;
129        net.acked_features = state.virtio_state.acked_features;
130
131        Ok(net)
132    }
133}
134
135#[cfg(test)]
136mod tests {
137
138    use super::*;
139    use crate::devices::virtio::device::VirtioDevice;
140    use crate::devices::virtio::net::test_utils::{default_net, default_net_no_mmds};
141    use crate::devices::virtio::test_utils::{default_interrupt, default_mem};
142    use crate::snapshot::Snapshot;
143
144    fn validate_save_and_restore(net: Net, mmds_ds: Option<Arc<Mutex<Mmds>>>) {
145        let guest_mem = default_mem();
146        let mut mem = vec![0; 4096];
147
148        let id;
149        let tap_if_name;
150        let has_mmds_ns;
151        let allow_mmds_requests;
152        let virtio_state;
153
154        // Create and save the net device.
155        {
156            Snapshot::new(net.save())
157                .save(&mut mem.as_mut_slice())
158                .unwrap();
159
160            // Save some fields that we want to check later.
161            id = net.id.clone();
162            tap_if_name = net.iface_name();
163            has_mmds_ns = net.mmds_ns.is_some();
164            allow_mmds_requests = has_mmds_ns && mmds_ds.is_some();
165            virtio_state = VirtioDeviceState::from_device(&net);
166        }
167
168        // Drop the initial net device so that we don't get an error when trying to recreate the
169        // TAP device.
170        drop(net);
171        {
172            // Deserialize and restore the net device.
173            match Net::restore(
174                NetConstructorArgs {
175                    mem: guest_mem,
176                    mmds: mmds_ds,
177                },
178                &Snapshot::load_without_crc_check(mem.as_slice())
179                    .unwrap()
180                    .data,
181            ) {
182                Ok(restored_net) => {
183                    // Test that virtio specific fields are the same.
184                    assert_eq!(restored_net.device_type(), VIRTIO_ID_NET);
185                    assert_eq!(restored_net.avail_features(), virtio_state.avail_features);
186                    assert_eq!(restored_net.acked_features(), virtio_state.acked_features);
187                    assert_eq!(restored_net.is_activated(), virtio_state.activated);
188
189                    // Test that net specific fields are the same.
190                    assert_eq!(&restored_net.id, &id);
191                    assert_eq!(&restored_net.iface_name(), &tap_if_name);
192                    assert_eq!(restored_net.mmds_ns.is_some(), allow_mmds_requests);
193                    assert_eq!(restored_net.rx_rate_limiter, RateLimiter::default());
194                    assert_eq!(restored_net.tx_rate_limiter, RateLimiter::default());
195                }
196                Err(NetPersistError::NoMmdsDataStore) => {
197                    assert!(has_mmds_ns && !allow_mmds_requests)
198                }
199                _ => unreachable!(),
200            }
201        }
202    }
203
204    #[test]
205    fn test_persistence() {
206        let mmds = Some(Arc::new(Mutex::new(Mmds::default())));
207        validate_save_and_restore(default_net(), mmds.as_ref().cloned());
208        validate_save_and_restore(default_net_no_mmds(), None);
209
210        // Check what happens if the MMIODeviceManager gives us the reference to the MMDS
211        // data store even if this device does not have mmds ns configured.
212        // The restore should be conservative and not configure the mmds ns.
213        validate_save_and_restore(default_net_no_mmds(), mmds);
214
215        // Check what happens if the MMIODeviceManager does not give us the reference to the MMDS
216        // data store. This will return an error.
217        validate_save_and_restore(default_net(), None);
218    }
219}