Skip to content

Commit e2ed128

Browse files
committed
allow persistent bounce buffers in MaybeBounce
This is particularly useful for virtio devices, as we know that a virtio descriptor chain is at most u16::MAX bytes long, meaning we can easily just allocate a persistent bounce buffer of that size on device creation and then never reallocate on the hotpath. Signed-off-by: Patrick Roy <[email protected]>
1 parent d286c46 commit e2ed128

File tree

3 files changed

+114
-30
lines changed

3 files changed

+114
-30
lines changed

src/vmm/src/builder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ pub fn build_microvm_for_boot(
274274
}
275275

276276
let entry_point = load_kernel(
277-
MaybeBounce(&boot_config.kernel_file, secret_free),
277+
MaybeBounce::new(&boot_config.kernel_file, secret_free),
278278
vmm.vm.guest_memory(),
279279
)?;
280280
let initrd = match &boot_config.initrd_file {
@@ -286,7 +286,7 @@ pub fn build_microvm_for_boot(
286286

287287
Some(InitrdConfig::from_reader(
288288
vmm.vm.guest_memory(),
289-
MaybeBounce(initrd_file, secret_free),
289+
MaybeBounce::new(initrd_file, secret_free),
290290
u64_to_usize(size),
291291
)?)
292292
}

src/vmm/src/vstate/memory.rs

Lines changed: 111 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -64,54 +64,123 @@ pub enum MemoryError {
6464
/// `Write` respectively, by reading/writing using a bounce buffer, and memcpy-ing into the
6565
/// [`VolatileSlice`].
6666
#[derive(Debug)]
67-
pub struct MaybeBounce<T>(pub T, pub bool);
67+
pub struct MaybeBounce<T, const N: usize = 0> {
68+
pub(crate) target: T,
69+
pub(crate) should_bounce: bool,
70+
persistent_buffer: [u8; N],
71+
}
72+
73+
impl<T> MaybeBounce<T, 0> {
74+
/// Creates a new `MaybeBounce` that always allocates a bounce
75+
/// buffer on-demand
76+
pub fn new(target: T, should_bounce: bool) -> Self {
77+
MaybeBounce::new_persistent(target, should_bounce)
78+
}
79+
}
80+
81+
impl<T, const N: usize> MaybeBounce<T, N> {
82+
/// Creates a new `MaybeBounce` that uses a persistent, fixed size bounce buffer
83+
/// of size `N`. If a read/write request exceeds the size of this bounce buffer, it
84+
/// is split into multiple, `<= N`-size read/writes.
85+
pub fn new_persistent(target: T, should_bounce: bool) -> Self {
86+
MaybeBounce {
87+
target,
88+
should_bounce,
89+
persistent_buffer: [0u8; N],
90+
}
91+
}
92+
}
6893

6994
// FIXME: replace AsFd with ReadVolatile once &File: ReadVolatile in vm-memory.
70-
impl<T: Read + AsFd> ReadVolatile for MaybeBounce<T> {
95+
impl<T: Read + AsFd, const N: usize> ReadVolatile for MaybeBounce<T, N> {
7196
fn read_volatile<B: BitmapSlice>(
7297
&mut self,
7398
buf: &mut VolatileSlice<B>,
7499
) -> Result<usize, VolatileMemoryError> {
75-
if self.1 {
76-
let mut bbuf = vec![0; buf.len()];
77-
let n = self
78-
.0
79-
.read(bbuf.as_mut_slice())
80-
.map_err(VolatileMemoryError::IOError)?;
81-
buf.copy_from(&bbuf[..n]);
82-
Ok(n)
100+
if self.should_bounce {
101+
let mut bbuf = (N == 0).then(|| vec![0u8; buf.len()]);
102+
let bbuf = bbuf
103+
.as_deref_mut()
104+
.unwrap_or(self.persistent_buffer.as_mut_slice());
105+
106+
let mut buf = buf.offset(0)?;
107+
let mut total = 0;
108+
while !buf.is_empty() {
109+
let how_much = buf.len().min(bbuf.len());
110+
let n = self
111+
.target
112+
.read(&mut bbuf[..how_much])
113+
.map_err(VolatileMemoryError::IOError)?;
114+
buf.copy_from(&bbuf[..n]);
115+
116+
buf = buf.offset(n)?;
117+
total += n;
118+
119+
if n < how_much {
120+
break;
121+
}
122+
}
123+
124+
Ok(total)
83125
} else {
84-
self.0.as_fd().read_volatile(buf)
126+
self.target.as_fd().read_volatile(buf)
85127
}
86128
}
87129
}
88130

89-
impl<T: Write + AsFd> WriteVolatile for MaybeBounce<T> {
131+
impl<T: Write + AsFd, const N: usize> WriteVolatile for MaybeBounce<T, N> {
90132
fn write_volatile<B: BitmapSlice>(
91133
&mut self,
92134
buf: &VolatileSlice<B>,
93135
) -> Result<usize, VolatileMemoryError> {
94-
if self.1 {
95-
let mut bbuf = vec![0; buf.len()];
96-
buf.copy_to(bbuf.as_mut_slice());
97-
self.0
98-
.write(bbuf.as_slice())
99-
.map_err(VolatileMemoryError::IOError)
136+
if self.should_bounce {
137+
let mut bbuf = (N == 0).then(|| vec![0u8; buf.len()]);
138+
let bbuf = bbuf
139+
.as_deref_mut()
140+
.unwrap_or(self.persistent_buffer.as_mut_slice());
141+
142+
let mut buf = buf.offset(0)?;
143+
let mut total = 0;
144+
while !buf.is_empty() {
145+
let how_much = buf.copy_to(bbuf);
146+
let n = self
147+
.target
148+
.write(&bbuf[..how_much])
149+
.map_err(VolatileMemoryError::IOError)?;
150+
buf = buf.offset(n)?;
151+
total += n;
152+
153+
if n < how_much {
154+
break;
155+
}
156+
}
157+
158+
Ok(total)
100159
} else {
101-
self.0.as_fd().write_volatile(buf)
160+
self.target.as_fd().write_volatile(buf)
102161
}
103162
}
104163
}
105164

106-
impl<R: Read> Read for MaybeBounce<R> {
165+
impl<R: Read, const N: usize> Read for MaybeBounce<R, N> {
107166
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
108-
self.0.read(buf)
167+
self.target.read(buf)
168+
}
169+
}
170+
171+
impl<W: Write, const N: usize> Write for MaybeBounce<W, N> {
172+
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
173+
self.target.write(buf)
174+
}
175+
176+
fn flush(&mut self) -> std::io::Result<()> {
177+
self.target.flush()
109178
}
110179
}
111180

112-
impl<S: Seek> Seek for MaybeBounce<S> {
181+
impl<S: Seek, const N: usize> Seek for MaybeBounce<S, N> {
113182
fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
114-
self.0.seek(pos)
183+
self.target.seek(pos)
115184
}
116185
}
117186

@@ -947,30 +1016,45 @@ mod tests {
9471016
fn test_bounce() {
9481017
let file_direct = TempFile::new().unwrap();
9491018
let file_bounced = TempFile::new().unwrap();
1019+
let file_persistent_bounced = TempFile::new().unwrap();
9501020

9511021
let mut data = (0..=255).collect_vec();
9521022

953-
MaybeBounce(file_direct.as_file(), false)
1023+
MaybeBounce::new(file_direct.as_file(), false)
1024+
.write_all_volatile(&VolatileSlice::from(data.as_mut_slice()))
1025+
.unwrap();
1026+
MaybeBounce::new(file_bounced.as_file(), true)
9541027
.write_all_volatile(&VolatileSlice::from(data.as_mut_slice()))
9551028
.unwrap();
956-
MaybeBounce(file_bounced.as_file(), true)
1029+
MaybeBounce::<_, 7>::new_persistent(file_persistent_bounced.as_file(), true)
9571030
.write_all_volatile(&VolatileSlice::from(data.as_mut_slice()))
9581031
.unwrap();
9591032

9601033
let mut data_direct = vec![0u8; 256];
9611034
let mut data_bounced = vec![0u8; 256];
1035+
let mut data_persistent_bounced = vec![0u8; 256];
9621036

9631037
file_direct.as_file().seek(SeekFrom::Start(0)).unwrap();
9641038
file_bounced.as_file().seek(SeekFrom::Start(0)).unwrap();
1039+
file_persistent_bounced
1040+
.as_file()
1041+
.seek(SeekFrom::Start(0))
1042+
.unwrap();
9651043

966-
MaybeBounce(file_direct.as_file(), false)
1044+
MaybeBounce::new(file_direct.as_file(), false)
9671045
.read_exact_volatile(&mut VolatileSlice::from(data_direct.as_mut_slice()))
9681046
.unwrap();
969-
MaybeBounce(file_bounced.as_file(), true)
1047+
MaybeBounce::new(file_bounced.as_file(), true)
9701048
.read_exact_volatile(&mut VolatileSlice::from(data_bounced.as_mut_slice()))
9711049
.unwrap();
1050+
MaybeBounce::<_, 7>::new_persistent(file_persistent_bounced.as_file(), true)
1051+
.read_exact_volatile(&mut VolatileSlice::from(
1052+
data_persistent_bounced.as_mut_slice(),
1053+
))
1054+
.unwrap();
9721055

9731056
assert_eq!(data_direct, data_bounced);
9741057
assert_eq!(data_direct, data);
1058+
assert_eq!(data_persistent_bounced, data);
9751059
}
9761060
}

src/vmm/src/vstate/vm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ impl Vm {
466466
.iter()
467467
.any(|r| r.inner().guest_memfd != 0);
468468
self.guest_memory()
469-
.dump(&mut MaybeBounce(&file, secret_hidden))
469+
.dump(&mut MaybeBounce::new(&file, secret_hidden))
470470
.and_then(|_| self.swiotlb_regions().dump(&mut file))?;
471471
self.reset_dirty_bitmap();
472472
self.guest_memory().reset_dirty();

0 commit comments

Comments
 (0)