Skip to content

Commit 5313daa

Browse files
alexandruagandreeaflorescu
authored andcommitted
bitmap/backend: add AtomicBitmap implementation
Signed-off-by: Alexandru Agache <[email protected]>
1 parent 198ded2 commit 5313daa

File tree

3 files changed

+190
-1
lines changed

3 files changed

+190
-1
lines changed

src/bitmap/backend/atomic_bitmap.rs

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
3+
4+
//! Bitmap backend implementation based on atomic integers.
5+
6+
use std::sync::atomic::{AtomicU64, Ordering};
7+
8+
use crate::bitmap::{Bitmap, RefSlice, WithBitmapSlice};
9+
10+
#[cfg(feature = "backend-mmap")]
11+
use crate::mmap::NewBitmap;
12+
13+
/// `AtomicBitmap` implements a simple bit map on the page level with test and set operations.
14+
/// It is page-size aware, so it converts addresses to page numbers before setting or clearing
15+
/// the bits.
16+
#[derive(Debug)]
17+
pub struct AtomicBitmap {
18+
map: Vec<AtomicU64>,
19+
size: usize,
20+
page_size: usize,
21+
}
22+
23+
#[allow(clippy::len_without_is_empty)]
24+
impl AtomicBitmap {
25+
/// Create a new bitmap of `byte_size`, with one bit per page. This is effectively
26+
/// rounded up, and we get a new vector of the next multiple of 64 bigger than `bit_size`.
27+
pub fn new(byte_size: usize, page_size: usize) -> Self {
28+
let mut num_pages = byte_size / page_size;
29+
if byte_size % page_size > 0 {
30+
num_pages += 1;
31+
}
32+
33+
// Adding one entry element more just in case `num_pages` is not a multiple of `64`.
34+
let map_size = num_pages / 64 + 1;
35+
let map: Vec<AtomicU64> = (0..map_size).map(|_| AtomicU64::new(0)).collect();
36+
37+
AtomicBitmap {
38+
map,
39+
size: num_pages,
40+
page_size,
41+
}
42+
}
43+
44+
/// Is bit `n` set? Bits outside the range of the bitmap are always unset.
45+
pub fn is_bit_set(&self, index: usize) -> bool {
46+
if index < self.size {
47+
(self.map[index >> 6].load(Ordering::Acquire) & (1 << (index & 63))) != 0
48+
} else {
49+
// Out-of-range bits are always unset.
50+
false
51+
}
52+
}
53+
54+
/// Is the bit corresponding to address `addr` set?
55+
pub fn is_addr_set(&self, addr: usize) -> bool {
56+
self.is_bit_set(addr / self.page_size)
57+
}
58+
59+
/// Set a range of `len` bytes starting at `start_addr`. The first bit set in the bitmap
60+
/// is for the page corresponding to `start_addr`, and the last bit that we set corresponds
61+
/// to address `start_addr + len - 1`.
62+
pub fn set_addr_range(&self, start_addr: usize, len: usize) {
63+
// Return early in the unlikely event that `len == 0` so the `len - 1` computation
64+
// below does not underflow.
65+
if len == 0 {
66+
return;
67+
}
68+
69+
let first_bit = start_addr / self.page_size;
70+
// Handle input ranges where `start_addr + len - 1` would otherwise overflow an `usize`
71+
// by ignoring pages at invalid addresses.
72+
let last_bit = start_addr.saturating_add(len - 1) / self.page_size;
73+
for n in first_bit..=last_bit {
74+
if n >= self.size {
75+
// Attempts to set bits beyond the end of the bitmap are simply ignored.
76+
break;
77+
}
78+
self.map[n >> 6].fetch_or(1 << (n & 63), Ordering::SeqCst);
79+
}
80+
}
81+
82+
/// Get the length of the bitmap in bits (i.e. in how many pages it can represent).
83+
pub fn len(&self) -> usize {
84+
self.size
85+
}
86+
87+
/// Reset all bitmap bits to 0.
88+
pub fn reset(&self) {
89+
for it in self.map.iter() {
90+
it.store(0, Ordering::Release);
91+
}
92+
}
93+
}
94+
95+
impl Clone for AtomicBitmap {
96+
fn clone(&self) -> Self {
97+
let map = self
98+
.map
99+
.iter()
100+
.map(|i| i.load(Ordering::Acquire))
101+
.map(AtomicU64::new)
102+
.collect();
103+
AtomicBitmap {
104+
map,
105+
size: self.size,
106+
page_size: self.page_size,
107+
}
108+
}
109+
}
110+
111+
impl<'a> WithBitmapSlice<'a> for AtomicBitmap {
112+
type S = RefSlice<'a, Self>;
113+
}
114+
115+
impl Bitmap for AtomicBitmap {
116+
fn mark_dirty(&self, offset: usize, len: usize) {
117+
self.set_addr_range(offset, len)
118+
}
119+
120+
fn dirty_at(&self, offset: usize) -> bool {
121+
self.is_addr_set(offset)
122+
}
123+
124+
fn slice_at(&self, offset: usize) -> <Self as WithBitmapSlice>::S {
125+
RefSlice::new(self, offset)
126+
}
127+
}
128+
129+
impl Default for AtomicBitmap {
130+
fn default() -> Self {
131+
AtomicBitmap::new(0, 0x1000)
132+
}
133+
}
134+
135+
#[cfg(feature = "backend-mmap")]
136+
impl NewBitmap for AtomicBitmap {
137+
fn with_len(len: usize) -> Self {
138+
use std::convert::TryFrom;
139+
140+
// There's no unsafe potential in calling this function.
141+
let page_size = unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) };
142+
// The `unwrap` is safe to use because the above call should always succeed on the
143+
// supported platforms, and the size of a page will always fit within a `usize`.
144+
AtomicBitmap::new(len, usize::try_from(page_size).unwrap())
145+
}
146+
}
147+
148+
#[cfg(test)]
149+
mod tests {
150+
#[test]
151+
fn test_bitmap_basic() {
152+
use super::AtomicBitmap;
153+
154+
// Test that bitmap size is properly rounded up.
155+
let a = AtomicBitmap::new(1025, 128);
156+
assert_eq!(a.len(), 9);
157+
158+
let b = AtomicBitmap::new(1024, 128);
159+
assert_eq!(b.len(), 8);
160+
b.set_addr_range(128, 129);
161+
assert!(!b.is_addr_set(0));
162+
assert!(b.is_addr_set(128));
163+
assert!(b.is_addr_set(256));
164+
assert!(!b.is_addr_set(384));
165+
166+
let copy_b = b.clone();
167+
assert!(copy_b.is_addr_set(256));
168+
assert!(!copy_b.is_addr_set(384));
169+
170+
b.reset();
171+
assert!(!b.is_addr_set(128));
172+
assert!(!b.is_addr_set(256));
173+
assert!(!b.is_addr_set(384));
174+
}
175+
176+
#[test]
177+
fn test_bitmap_out_of_range() {
178+
use super::AtomicBitmap;
179+
let b = AtomicBitmap::new(1024, 1);
180+
// Set a partial range that goes beyond the end of the bitmap
181+
b.set_addr_range(768, 512);
182+
assert!(b.is_addr_set(768));
183+
// The bitmap is never set beyond its end.
184+
assert!(!b.is_addr_set(1024));
185+
assert!(!b.is_addr_set(1152));
186+
}
187+
}

src/bitmap/backend/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
33

4+
mod atomic_bitmap;
45
mod ref_slice;
56

7+
pub use atomic_bitmap::AtomicBitmap;
68
pub use ref_slice::RefSlice;

src/bitmap/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::fmt::Debug;
1414
use crate::{GuestMemory, GuestMemoryRegion};
1515

1616
#[cfg(any(test, feature = "backend-bitmap"))]
17-
pub use backend::RefSlice;
17+
pub use backend::{AtomicBitmap, RefSlice};
1818

1919
/// Trait implemented by types that support creating `BitmapSlice` objects.
2020
pub trait WithBitmapSlice<'a> {

0 commit comments

Comments
 (0)