Skip to content

Commit c00d5ed

Browse files
committed
feat(rng): reuse buffer for processing descriptor chains
Add a buffer to the Entropy struct which will be reused for descriptor chain processing. This will remove runtime overhead of recreating this buffer for each request. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent 32332d9 commit c00d5ed

File tree

1 file changed

+24
-22
lines changed

1 file changed

+24
-22
lines changed

src/vmm/src/devices/virtio/rng/device.rs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ pub struct Entropy {
4848

4949
// Device specific fields
5050
rate_limiter: RateLimiter,
51+
52+
buffer: IoVecBufferMut,
5153
}
5254

5355
impl Entropy {
@@ -75,6 +77,7 @@ impl Entropy {
7577
queue_events,
7678
irq_trigger,
7779
rate_limiter,
80+
buffer: IoVecBufferMut::default(),
7881
})
7982
}
8083

@@ -88,13 +91,13 @@ impl Entropy {
8891
.map_err(DeviceError::FailedSignalingIrq)
8992
}
9093

91-
fn rate_limit_request(rate_limiter: &mut RateLimiter, bytes: u64) -> bool {
92-
if !rate_limiter.consume(1, TokenType::Ops) {
94+
fn rate_limit_request(&mut self, bytes: u64) -> bool {
95+
if !self.rate_limiter.consume(1, TokenType::Ops) {
9396
return false;
9497
}
9598

96-
if !rate_limiter.consume(bytes, TokenType::Bytes) {
97-
rate_limiter.manual_replenish(1, TokenType::Ops);
99+
if !self.rate_limiter.consume(bytes, TokenType::Bytes) {
100+
self.rate_limiter.manual_replenish(1, TokenType::Ops);
98101
return false;
99102
}
100103

@@ -106,53 +109,52 @@ impl Entropy {
106109
rate_limiter.manual_replenish(bytes, TokenType::Bytes);
107110
}
108111

109-
fn handle_one(&self, iovec: &mut IoVecBufferMut) -> Result<u32, EntropyError> {
112+
fn handle_one(&mut self) -> Result<u32, EntropyError> {
110113
// If guest provided us with an empty buffer just return directly
111-
if iovec.len() == 0 {
114+
if self.buffer.len() == 0 {
112115
return Ok(0);
113116
}
114117

115-
let mut rand_bytes = vec![0; iovec.len() as usize];
118+
let mut rand_bytes = vec![0; self.buffer.len() as usize];
116119
rand::fill(&mut rand_bytes).map_err(|err| {
117120
METRICS.host_rng_fails.inc();
118121
err
119122
})?;
120123

121124
// It is ok to unwrap here. We are writing `iovec.len()` bytes at offset 0.
122-
iovec.write_all_volatile_at(&rand_bytes, 0).unwrap();
123-
Ok(iovec.len())
125+
self.buffer.write_all_volatile_at(&rand_bytes, 0).unwrap();
126+
Ok(self.buffer.len())
124127
}
125128

126129
fn process_entropy_queue(&mut self) {
127-
// This is safe since we checked in the event handler that the device is activated.
128-
let mem = self.device_state.mem().unwrap();
129-
130130
let mut used_any = false;
131131
while let Some(desc) = self.queues[RNG_QUEUE].pop() {
132+
// This is safe since we checked in the event handler that the device is activated.
133+
let mem = self.device_state.mem().unwrap();
132134
let index = desc.index;
133135
METRICS.entropy_event_count.inc();
134136

135-
// SAFETY: This descriptor chain is only loaded once
136-
// virtio requests are handled sequentially so no two IoVecBuffers
137-
// are live at the same time, meaning this has exclusive ownership over the memory
138-
let bytes = match unsafe { IoVecBufferMut::from_descriptor_chain(mem, desc) } {
139-
Ok(mut iovec) => {
137+
// SAFETY: This descriptor chain points to a single `DescriptorChain` memory buffer,
138+
// no other `IoVecBufferMut` object points to the same `DescriptorChain` at the same
139+
// time and we clear the `iovec` after we process the request.
140+
let bytes = match unsafe { self.buffer.load_descriptor_chain(mem, desc) } {
141+
Ok(()) => {
140142
debug!(
141143
"entropy: guest request for {} bytes of entropy",
142-
iovec.len()
144+
self.buffer.len()
143145
);
144146

145147
// Check for available rate limiting budget.
146148
// If not enough budget is available, leave the request descriptor in the queue
147149
// to handle once we do have budget.
148-
if !Self::rate_limit_request(&mut self.rate_limiter, u64::from(iovec.len())) {
150+
if !self.rate_limit_request(u64::from(self.buffer.len())) {
149151
debug!("entropy: throttling entropy queue");
150152
METRICS.entropy_rate_limiter_throttled.inc();
151153
self.queues[RNG_QUEUE].undo_pop();
152154
break;
153155
}
154156

155-
self.handle_one(&mut iovec).unwrap_or_else(|err| {
157+
self.handle_one().unwrap_or_else(|err| {
156158
error!("entropy: {err}");
157159
METRICS.entropy_event_fails.inc();
158160
0
@@ -444,8 +446,8 @@ mod tests {
444446
// This should succeed, we should have one more descriptor
445447
let desc = entropy_dev.queues_mut()[RNG_QUEUE].pop().unwrap();
446448
// SAFETY: This descriptor chain is only loaded into one buffer
447-
let mut iovec = unsafe { IoVecBufferMut::from_descriptor_chain(&mem, desc).unwrap() };
448-
entropy_dev.handle_one(&mut iovec).unwrap();
449+
entropy_dev.buffer = unsafe { IoVecBufferMut::from_descriptor_chain(&mem, desc).unwrap() };
450+
entropy_dev.handle_one().unwrap();
449451
}
450452

451453
#[test]

0 commit comments

Comments
 (0)