vm_memory/
atomic.rs

1// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
2// Copyright (C) 2020 Red Hat, Inc. All rights reserved.
3// SPDX-License-Identifier: Apache-2.0
4
5//! A wrapper over an `ArcSwap<GuestMemory>` struct to support RCU-style mutability.
6//!
7//! With the `backend-atomic` feature enabled, simply replacing `GuestMemoryMmap`
8//! with `GuestMemoryAtomic<GuestMemoryMmap>` will enable support for mutable memory maps.
9//! To support mutable memory maps, devices will also need to use
10//! `GuestAddressSpace::memory()` to gain temporary access to guest memory.
11
12extern crate arc_swap;
13
14use arc_swap::{ArcSwap, Guard};
15use std::ops::Deref;
16use std::sync::{Arc, LockResult, Mutex, MutexGuard, PoisonError};
17
18use crate::{GuestAddressSpace, GuestMemory};
19
20/// A fast implementation of a mutable collection of memory regions.
21///
22/// This implementation uses `ArcSwap` to provide RCU-like snapshotting of the memory map:
23/// every update of the memory map creates a completely new `GuestMemory` object, and
24/// readers will not be blocked because the copies they retrieved will be collected once
25/// no one can access them anymore.  Under the assumption that updates to the memory map
26/// are rare, this allows a very efficient implementation of the `memory()` method.
27#[derive(Clone, Debug)]
28pub struct GuestMemoryAtomic<M: GuestMemory> {
29    // GuestAddressSpace<M>, which we want to implement, is basically a drop-in
30    // replacement for &M.  Therefore, we need to pass to devices the `GuestMemoryAtomic`
31    // rather than a reference to it.  To obtain this effect we wrap the actual fields
32    // of GuestMemoryAtomic with an Arc, and derive the Clone trait.  See the
33    // documentation for GuestAddressSpace for an example.
34    inner: Arc<(ArcSwap<M>, Mutex<()>)>,
35}
36
37impl<M: GuestMemory> From<Arc<M>> for GuestMemoryAtomic<M> {
38    /// create a new `GuestMemoryAtomic` object whose initial contents come from
39    /// the `map` reference counted `GuestMemory`.
40    fn from(map: Arc<M>) -> Self {
41        let inner = (ArcSwap::new(map), Mutex::new(()));
42        GuestMemoryAtomic {
43            inner: Arc::new(inner),
44        }
45    }
46}
47
48impl<M: GuestMemory> GuestMemoryAtomic<M> {
49    /// create a new `GuestMemoryAtomic` object whose initial contents come from
50    /// the `map` `GuestMemory`.
51    pub fn new(map: M) -> Self {
52        Arc::new(map).into()
53    }
54
55    fn load(&self) -> Guard<Arc<M>> {
56        self.inner.0.load()
57    }
58
59    /// Acquires the update mutex for the `GuestMemoryAtomic`, blocking the current
60    /// thread until it is able to do so.  The returned RAII guard allows for
61    /// scoped unlock of the mutex (that is, the mutex will be unlocked when
62    /// the guard goes out of scope), and optionally also for replacing the
63    /// contents of the `GuestMemoryAtomic` when the lock is dropped.
64    pub fn lock(&self) -> LockResult<GuestMemoryExclusiveGuard<M>> {
65        match self.inner.1.lock() {
66            Ok(guard) => Ok(GuestMemoryExclusiveGuard {
67                parent: self,
68                _guard: guard,
69            }),
70            Err(err) => Err(PoisonError::new(GuestMemoryExclusiveGuard {
71                parent: self,
72                _guard: err.into_inner(),
73            })),
74        }
75    }
76}
77
78impl<M: GuestMemory> GuestAddressSpace for GuestMemoryAtomic<M> {
79    type T = GuestMemoryLoadGuard<M>;
80    type M = M;
81
82    fn memory(&self) -> Self::T {
83        GuestMemoryLoadGuard { guard: self.load() }
84    }
85}
86
87/// A guard that provides temporary access to a `GuestMemoryAtomic`.  This
88/// object is returned from the `memory()` method.  It dereference to
89/// a snapshot of the `GuestMemory`, so it can be used transparently to
90/// access memory.
91#[derive(Debug)]
92pub struct GuestMemoryLoadGuard<M: GuestMemory> {
93    guard: Guard<Arc<M>>,
94}
95
96impl<M: GuestMemory> GuestMemoryLoadGuard<M> {
97    /// Make a clone of the held pointer and returns it.  This is more
98    /// expensive than just using the snapshot, but it allows to hold on
99    /// to the snapshot outside the scope of the guard.  It also allows
100    /// writers to proceed, so it is recommended if the reference must
101    /// be held for a long time (including for caching purposes).
102    pub fn into_inner(self) -> Arc<M> {
103        Guard::into_inner(self.guard)
104    }
105}
106
107impl<M: GuestMemory> Clone for GuestMemoryLoadGuard<M> {
108    fn clone(&self) -> Self {
109        GuestMemoryLoadGuard {
110            guard: Guard::from_inner(Arc::clone(&*self.guard)),
111        }
112    }
113}
114
115impl<M: GuestMemory> Deref for GuestMemoryLoadGuard<M> {
116    type Target = M;
117
118    fn deref(&self) -> &Self::Target {
119        &self.guard
120    }
121}
122
123/// An RAII implementation of a "scoped lock" for `GuestMemoryAtomic`.  When
124/// this structure is dropped (falls out of scope) the lock will be unlocked,
125/// possibly after updating the memory map represented by the
126/// `GuestMemoryAtomic` that created the guard.
127#[derive(Debug)]
128pub struct GuestMemoryExclusiveGuard<'a, M: GuestMemory> {
129    parent: &'a GuestMemoryAtomic<M>,
130    _guard: MutexGuard<'a, ()>,
131}
132
133impl<M: GuestMemory> GuestMemoryExclusiveGuard<'_, M> {
134    /// Replace the memory map in the `GuestMemoryAtomic` that created the guard
135    /// with the new memory map, `map`.  The lock is then dropped since this
136    /// method consumes the guard.
137    pub fn replace(self, map: M) {
138        self.parent.inner.0.store(Arc::new(map))
139    }
140}
141
142#[cfg(test)]
143#[cfg(feature = "backend-mmap")]
144mod tests {
145    use super::*;
146    use crate::{GuestAddress, GuestMemory, GuestMemoryRegion, GuestUsize, MmapRegion};
147
148    type GuestMemoryMmap = crate::GuestMemoryMmap<()>;
149    type GuestRegionMmap = crate::GuestRegionMmap<()>;
150    type GuestMemoryMmapAtomic = GuestMemoryAtomic<GuestMemoryMmap>;
151
152    #[test]
153    fn test_atomic_memory() {
154        let region_size = 0x400;
155        let regions = vec![
156            (GuestAddress(0x0), region_size),
157            (GuestAddress(0x1000), region_size),
158        ];
159        let mut iterated_regions = Vec::new();
160        let gmm = GuestMemoryMmap::from_ranges(&regions).unwrap();
161        let gm = GuestMemoryMmapAtomic::new(gmm);
162        let mem = gm.memory();
163
164        for region in mem.iter() {
165            assert_eq!(region.len(), region_size as GuestUsize);
166        }
167
168        for region in mem.iter() {
169            iterated_regions.push((region.start_addr(), region.len() as usize));
170        }
171        assert_eq!(regions, iterated_regions);
172        assert_eq!(mem.num_regions(), 2);
173        assert!(mem.find_region(GuestAddress(0x1000)).is_some());
174        assert!(mem.find_region(GuestAddress(0x10000)).is_none());
175
176        assert!(regions
177            .iter()
178            .map(|x| (x.0, x.1))
179            .eq(iterated_regions.iter().copied()));
180
181        let mem2 = mem.into_inner();
182        for region in mem2.iter() {
183            assert_eq!(region.len(), region_size as GuestUsize);
184        }
185        assert_eq!(mem2.num_regions(), 2);
186        assert!(mem2.find_region(GuestAddress(0x1000)).is_some());
187        assert!(mem2.find_region(GuestAddress(0x10000)).is_none());
188
189        assert!(regions
190            .iter()
191            .map(|x| (x.0, x.1))
192            .eq(iterated_regions.iter().copied()));
193
194        let mem3 = mem2.memory();
195        for region in mem3.iter() {
196            assert_eq!(region.len(), region_size as GuestUsize);
197        }
198        assert_eq!(mem3.num_regions(), 2);
199        assert!(mem3.find_region(GuestAddress(0x1000)).is_some());
200        assert!(mem3.find_region(GuestAddress(0x10000)).is_none());
201    }
202
203    #[test]
204    fn test_clone_guard() {
205        let region_size = 0x400;
206        let regions = vec![
207            (GuestAddress(0x0), region_size),
208            (GuestAddress(0x1000), region_size),
209        ];
210        let gmm = GuestMemoryMmap::from_ranges(&regions).unwrap();
211        let gm = GuestMemoryMmapAtomic::new(gmm);
212        let mem = {
213            let guard1 = gm.memory();
214            Clone::clone(&guard1)
215        };
216        assert_eq!(mem.num_regions(), 2);
217    }
218
219    #[test]
220    fn test_atomic_hotplug() {
221        let region_size = 0x1000;
222        let regions = vec![
223            (GuestAddress(0x0), region_size),
224            (GuestAddress(0x10_0000), region_size),
225        ];
226        let mut gmm = Arc::new(GuestMemoryMmap::from_ranges(&regions).unwrap());
227        let gm: GuestMemoryAtomic<_> = gmm.clone().into();
228        let mem_orig = gm.memory();
229        assert_eq!(mem_orig.num_regions(), 2);
230
231        {
232            let guard = gm.lock().unwrap();
233            let new_gmm = Arc::make_mut(&mut gmm);
234            let mmap = Arc::new(
235                GuestRegionMmap::new(MmapRegion::new(0x1000).unwrap(), GuestAddress(0x8000))
236                    .unwrap(),
237            );
238            let new_gmm = new_gmm.insert_region(mmap).unwrap();
239            let mmap = Arc::new(
240                GuestRegionMmap::new(MmapRegion::new(0x1000).unwrap(), GuestAddress(0x4000))
241                    .unwrap(),
242            );
243            let new_gmm = new_gmm.insert_region(mmap).unwrap();
244            let mmap = Arc::new(
245                GuestRegionMmap::new(MmapRegion::new(0x1000).unwrap(), GuestAddress(0xc000))
246                    .unwrap(),
247            );
248            let new_gmm = new_gmm.insert_region(mmap).unwrap();
249            let mmap = Arc::new(
250                GuestRegionMmap::new(MmapRegion::new(0x1000).unwrap(), GuestAddress(0xc000))
251                    .unwrap(),
252            );
253            new_gmm.insert_region(mmap).unwrap_err();
254            guard.replace(new_gmm);
255        }
256
257        assert_eq!(mem_orig.num_regions(), 2);
258        let mem = gm.memory();
259        assert_eq!(mem.num_regions(), 5);
260    }
261}