Skip to content

Commit ad262ce

Browse files
amphiphip1611
authored andcommitted
vm-migration: speed up ReadVolatile and WriteVolatile
The ReadVolatile and WriteVolatile implementations of TlsStream were very slow, mainly because they allocated a large buffer on each invocation. The TlsStreamWrapper carries a buffer that it uses for ReadVolatile and WriteVolatile and that is allocated once on creation. On-behalf-of: SAP sebastian.eydam@sap.com Signed-off-by: Sebastian Eydam <sebastian.eydam@cyberus-technology.de>
1 parent e74da3a commit ad262ce

File tree

2 files changed

+92
-16
lines changed

2 files changed

+92
-16
lines changed

vm-migration/src/tls.rs

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,28 @@ pub enum TlsStream {
4646
Server(StreamOwned<ServerConnection, TcpStream>),
4747
}
4848

49+
// The TLS-Stream objects cannot read or write volatile, thus we need a buffer
50+
// between the VolatileSlice and the TLS stream (see ReadVolatile and
51+
// WriteVolatile implementations below). Allocating this buffer in these
52+
// function calls would make it very slow, thus we tie the buffer to the stream
53+
// with this wrapper.
54+
pub struct TlsStreamWrapper {
55+
stream: TlsStream,
56+
// Used only in ReadVolatile and WriteVolatile
57+
buf: Vec<u8>,
58+
}
59+
60+
static MAX_CHUNK: usize = 1024 * 64;
61+
62+
impl TlsStreamWrapper {
63+
pub fn new(stream: TlsStream) -> Self {
64+
Self {
65+
stream,
66+
buf: Vec::new(),
67+
}
68+
}
69+
}
70+
4971
impl Read for TlsStream {
5072
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
5173
match self {
@@ -55,6 +77,12 @@ impl Read for TlsStream {
5577
}
5678
}
5779

80+
impl Read for TlsStreamWrapper {
81+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
82+
Read::read(&mut self.stream, buf)
83+
}
84+
}
85+
5886
impl Write for TlsStream {
5987
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
6088
match self {
@@ -70,38 +98,81 @@ impl Write for TlsStream {
7098
}
7199
}
72100

101+
impl Write for TlsStreamWrapper {
102+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
103+
Write::write(&mut self.stream, buf)
104+
}
105+
fn flush(&mut self) -> io::Result<()> {
106+
Write::flush(&mut self.stream)
107+
}
108+
}
109+
73110
// Reading from or writing to these FDs would break the connection, because
74111
// those reads or writes wouldn't go through rustls. But the FD is used to wait
75112
// until it becomes readable.
76-
impl AsFd for TlsStream {
113+
impl AsFd for TlsStreamWrapper {
77114
fn as_fd(&self) -> BorrowedFd<'_> {
78-
match self {
115+
match &self.stream {
79116
TlsStream::Client(s) => s.get_ref().as_fd(),
80117
TlsStream::Server(s) => s.get_ref().as_fd(),
81118
}
82119
}
83120
}
84121

85-
impl ReadVolatile for TlsStream {
122+
impl ReadVolatile for TlsStreamWrapper {
86123
fn read_volatile<B: BitmapSlice>(
87124
&mut self,
88125
vs: &mut VolatileSlice<B>,
89126
) -> std::result::Result<usize, VolatileMemoryError> {
90-
let mut tmp = vec![0u8; vs.len()];
91-
let n = Read::read(self, &mut tmp[..]).unwrap();
92-
vs.copy_from(&tmp[..n]);
127+
let len = vs.len().min(MAX_CHUNK);
128+
129+
if len == 0 {
130+
return Ok(0);
131+
}
132+
133+
if self.buf.len() < len {
134+
self.buf.resize(len, 0);
135+
}
136+
137+
let buf = &mut self.buf[..len];
138+
let n =
139+
Read::read(&mut self.stream, &mut buf[..len]).map_err(VolatileMemoryError::IOError)?;
140+
141+
if n == 0 {
142+
return Ok(0);
143+
}
144+
145+
vs.copy_from(&buf[..n]);
146+
self.buf.clear();
147+
93148
Ok(n)
94149
}
95150
}
96151

97-
impl WriteVolatile for TlsStream {
152+
impl WriteVolatile for TlsStreamWrapper {
98153
fn write_volatile<B: BitmapSlice>(
99154
&mut self,
100155
vs: &VolatileSlice<B>,
101156
) -> std::result::Result<usize, VolatileMemoryError> {
102-
let mut tmp = vec![0u8; vs.len()];
103-
let n = vs.copy_to(&mut tmp[..]);
104-
let n = Write::write(self, &tmp[..n]).unwrap();
157+
let len = vs.len().min(MAX_CHUNK);
158+
if len == 0 {
159+
return Ok(0);
160+
}
161+
162+
if self.buf.len() < len {
163+
self.buf.resize(len, 0);
164+
}
165+
166+
let buf = &mut self.buf[..len];
167+
let n = vs.copy_to(&mut buf[..len]);
168+
169+
if n == 0 {
170+
return Ok(0);
171+
}
172+
173+
let n = Write::write(&mut self.stream, &buf[..n]).map_err(VolatileMemoryError::IOError)?;
174+
self.buf.clear();
175+
105176
Ok(n)
106177
}
107178
}
@@ -130,7 +201,10 @@ impl TlsConnectionWrapper {
130201
Self { config }
131202
}
132203

133-
pub fn wrap(&self, socket: TcpStream) -> std::result::Result<TlsStream, MigratableError> {
204+
pub fn wrap(
205+
&self,
206+
socket: TcpStream,
207+
) -> std::result::Result<TlsStreamWrapper, MigratableError> {
134208
let conn = ServerConnection::new(self.config.clone()).map_err(TlsError::RustlsError)?;
135209

136210
let mut tls = StreamOwned::new(conn, socket);
@@ -146,7 +220,7 @@ impl TlsConnectionWrapper {
146220
}
147221
}
148222

149-
Ok(TlsStream::Server(tls))
223+
Ok(TlsStreamWrapper::new(TlsStream::Server(tls)))
150224
}
151225
}
152226

vmm/src/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ use vm_memory::{
5858
VolatileMemoryError, VolatileSlice, WriteVolatile,
5959
};
6060
use vm_migration::protocol::*;
61-
use vm_migration::tls::{TlsConnectionWrapper, TlsStream};
61+
use vm_migration::tls::{TlsConnectionWrapper, TlsStream, TlsStreamWrapper};
6262
use vm_migration::{
6363
Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable, tls,
6464
};
@@ -268,7 +268,7 @@ impl From<u64> for EpollDispatch {
268268
enum SocketStream {
269269
Unix(UnixStream),
270270
Tcp(TcpStream),
271-
Tls(TlsStream),
271+
Tls(Box<TlsStreamWrapper>),
272272
}
273273

274274
impl Read for SocketStream {
@@ -924,7 +924,7 @@ impl ReceiveListener {
924924
}
925925
ReceiveListener::Tls(listener, conn) => listener.accept().map(|(socket, _)| {
926926
conn.wrap(socket)
927-
.map(SocketStream::Tls)
927+
.map(|s| SocketStream::Tls(Box::new(s)))
928928
.map_err(std::io::Error::other)
929929
})?,
930930
}
@@ -1407,7 +1407,9 @@ fn send_migration_socket(
14071407
.map(|(host, _)| host)
14081408
.unwrap_or(address),
14091409
)?;
1410-
Ok(SocketStream::Tls(TlsStream::Client(tls_stream)))
1410+
Ok(SocketStream::Tls(Box::new(TlsStreamWrapper::new(
1411+
TlsStream::Client(tls_stream),
1412+
))))
14111413
}
14121414
} else if let Some(path) = &send_data_migration.destination_url.strip_prefix("unix:") {
14131415
info!("Connecting to UNIX socket at {:?}", path);

0 commit comments

Comments
 (0)