Skip to content

Commit e48ab2a

Browse files
committed
feat: pmem: cache the msync result
There is only one type of request virto-pmem accepts, but guest still can issue many of them and Firecracker will need to process it all. Instead of doing one `msync` per request, it is less resource intensive to do it once on the first valid descriptor and then duplicate the result to other descriptors. This is safe since the guest will only know the result of the execution after Firecracker will signal it, which will only happen after all descriptors are processed. Signed-off-by: Egor Lazarchuk <yegorlz@amazon.co.uk>
1 parent 14d48af commit e48ab2a

File tree

1 file changed

+59
-22
lines changed

1 file changed

+59
-22
lines changed

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

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,9 @@ impl Pmem {
254254
// This is safe since we checked in the event handler that the device is activated.
255255
let active_state = self.device_state.active_state().unwrap();
256256

257+
let mut cached_result = None;
257258
while let Some(head) = self.queues[0].pop()? {
258-
let add_result = match self.process_chain(head) {
259+
let add_result = match self.process_chain(head, &mut cached_result) {
259260
Ok(()) => self.queues[0].add_used(head.index, 4),
260261
Err(err) => {
261262
error!("pmem: {err}");
@@ -283,7 +284,11 @@ impl Pmem {
283284
Ok(())
284285
}
285286

286-
fn process_chain(&self, head: DescriptorChain) -> Result<(), PmemError> {
287+
fn process_chain(
288+
&self,
289+
head: DescriptorChain,
290+
cached_result: &mut Option<i32>,
291+
) -> Result<(), PmemError> {
287292
// This is safe since we checked in the event handler that the device is activated.
288293
let active_state = self.device_state.active_state().unwrap();
289294

@@ -312,21 +317,33 @@ impl Pmem {
312317
return Err(PmemError::Non4byteStatusDescriptor(status_descriptor.len));
313318
}
314319

315-
let mut result = SUCCESS;
316-
// SAFETY: We are calling the system call with valid arguments and checking the returned
317-
// value
318-
unsafe {
319-
let ret = libc::msync(
320-
self.mmap_ptr as *mut libc::c_void,
321-
u64_to_usize(self.file_len),
322-
libc::MS_SYNC,
323-
);
324-
if ret < 0 {
325-
error!("pmem: Unable to msync the file. Error: {}", ret);
326-
result = FAILURE;
320+
// Since there is only 1 type of request pmem device supports,
321+
// we treat single notification from the guest as a single request
322+
// and reuse cached result of `msync` from first valid descriptor
323+
// for all following descriptors.
324+
if let Some(result) = cached_result {
325+
active_state
326+
.mem
327+
.write_obj(*result, status_descriptor.addr)?;
328+
} else {
329+
let mut status = SUCCESS;
330+
// SAFETY: We are calling the system call with valid arguments and checking the returned
331+
// value
332+
unsafe {
333+
let ret = libc::msync(
334+
self.mmap_ptr as *mut libc::c_void,
335+
u64_to_usize(self.file_len),
336+
libc::MS_SYNC,
337+
);
338+
if ret < 0 {
339+
error!("pmem: Unable to msync the file. Error: {}", ret);
340+
status = FAILURE;
341+
}
327342
}
343+
*cached_result = Some(status);
344+
345+
active_state.mem.write_obj(status, status_descriptor.addr)?;
328346
}
329-
active_state.mem.write_obj(result, status_descriptor.addr)?;
330347
Ok(())
331348
}
332349

@@ -501,8 +518,28 @@ mod tests {
501518
vq.used.idx.set(0);
502519
vq.avail.idx.set(1);
503520
let head = pmem.queues[0].pop().unwrap().unwrap();
504-
pmem.process_chain(head).unwrap();
521+
let mut result = None;
522+
pmem.process_chain(head, &mut result).unwrap();
505523
assert_eq!(mem.read_obj::<u32>(GuestAddress(0x2000)).unwrap(), 0);
524+
assert!(result.is_some());
525+
}
526+
527+
// Valid request cached value reuse
528+
{
529+
vq.avail.ring[0].set(0);
530+
vq.dtable[0].set(0x1000, 4, VIRTQ_DESC_F_NEXT, 1);
531+
vq.avail.ring[1].set(1);
532+
vq.dtable[1].set(0x2000, 4, VIRTQ_DESC_F_WRITE, 0);
533+
mem.write_obj::<u32>(0, GuestAddress(0x1000)).unwrap();
534+
mem.write_obj::<u32>(0x69, GuestAddress(0x2000)).unwrap();
535+
536+
pmem.queues[0] = vq.create_queue();
537+
vq.used.idx.set(0);
538+
vq.avail.idx.set(1);
539+
let head = pmem.queues[0].pop().unwrap().unwrap();
540+
let mut result = Some(0x69);
541+
pmem.process_chain(head, &mut result).unwrap();
542+
assert_eq!(mem.read_obj::<u32>(GuestAddress(0x2000)).unwrap(), 0x69);
506543
}
507544

508545
// Invalid request type
@@ -516,7 +553,7 @@ mod tests {
516553
vq.avail.idx.set(1);
517554
let head = pmem.queues[0].pop().unwrap().unwrap();
518555
assert!(matches!(
519-
pmem.process_chain(head).unwrap_err(),
556+
pmem.process_chain(head, &mut None).unwrap_err(),
520557
PmemError::UnknownRequestType(0x69),
521558
));
522559
}
@@ -532,7 +569,7 @@ mod tests {
532569
vq.avail.idx.set(1);
533570
let head = pmem.queues[0].pop().unwrap().unwrap();
534571
assert!(matches!(
535-
pmem.process_chain(head).unwrap_err(),
572+
pmem.process_chain(head, &mut None).unwrap_err(),
536573
PmemError::DescriptorChainTooShort,
537574
));
538575
}
@@ -550,7 +587,7 @@ mod tests {
550587
vq.avail.idx.set(1);
551588
let head = pmem.queues[0].pop().unwrap().unwrap();
552589
assert!(matches!(
553-
pmem.process_chain(head).unwrap_err(),
590+
pmem.process_chain(head, &mut None).unwrap_err(),
554591
PmemError::WriteOnlyDescriptor,
555592
));
556593
}
@@ -568,7 +605,7 @@ mod tests {
568605
vq.avail.idx.set(1);
569606
let head = pmem.queues[0].pop().unwrap().unwrap();
570607
assert!(matches!(
571-
pmem.process_chain(head).unwrap_err(),
608+
pmem.process_chain(head, &mut None).unwrap_err(),
572609
PmemError::ReadOnlyDescriptor,
573610
));
574611
}
@@ -584,7 +621,7 @@ mod tests {
584621
vq.avail.idx.set(1);
585622
let head = pmem.queues[0].pop().unwrap().unwrap();
586623
assert!(matches!(
587-
pmem.process_chain(head).unwrap_err(),
624+
pmem.process_chain(head, &mut None).unwrap_err(),
588625
PmemError::Non4byteHeadDescriptor(0x69),
589626
));
590627
}
@@ -603,7 +640,7 @@ mod tests {
603640
vq.avail.idx.set(1);
604641
let head = pmem.queues[0].pop().unwrap().unwrap();
605642
assert!(matches!(
606-
pmem.process_chain(head).unwrap_err(),
643+
pmem.process_chain(head, &mut None).unwrap_err(),
607644
PmemError::Non4byteStatusDescriptor(0x69),
608645
));
609646
}

0 commit comments

Comments
 (0)