Skip to content

Commit 5f623fb

Browse files
authored
avoid excessive Arc<Mapping> contention (#1012)
Conceptually it wasn't wrong for submappings to `Arc::clone` the pointed-to base mapping, but in practice all the accessors off of MemCtx return SubMapping with lifetimes constrained by that MemCtx. The sheer number of submappings being created and destroyed during I/O operations makes the refcount very hot under load. Lean into the de facto lifetime constraint on SubMapping and just take a borrow of the backing Mapping instead of Arc::clone (and implicit ref decrement in later drop).
1 parent 2dc6437 commit 5f623fb

File tree

4 files changed

+14
-28
lines changed

4 files changed

+14
-28
lines changed

lib/propolis/src/common.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -527,22 +527,18 @@ mod test {
527527
let mut buf = [0u8; 8];
528528
let mut ro8 = ReadOp::from_buf(0, &mut buf[0..1]);
529529
ro8.write_u8(1);
530-
drop(ro8);
531530
assert_eq!(buf, [1, 0, 0, 0, 0, 0, 0, 0]);
532531

533532
let mut ro16 = ReadOp::from_buf(0, &mut buf[0..2]);
534533
ro16.write_u16(0x2000);
535-
drop(ro16);
536534
assert_eq!(buf, [0, 0x20, 0, 0, 0, 0, 0, 0]);
537535

538536
let mut ro32 = ReadOp::from_buf(0, &mut buf[0..4]);
539537
ro32.write_u32(0x4000_0000);
540-
drop(ro32);
541538
assert_eq!(buf, [0, 0, 0, 0x40, 0, 0, 0, 0]);
542539

543540
let mut ro64 = ReadOp::from_buf(0, &mut buf);
544541
ro64.write_u64(0x8000_0000_0000_0000);
545-
drop(ro64);
546542
assert_eq!(buf, [0, 0, 0, 0, 0, 0, 0, 0x80]);
547543
}
548544

@@ -551,19 +547,15 @@ mod test {
551547
let buf = [0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80];
552548
let mut wo8 = WriteOp::from_buf(0, &buf[0..1]);
553549
assert_eq!(wo8.read_u8(), 0x10);
554-
drop(wo8);
555550

556551
let mut wo16 = WriteOp::from_buf(0, &buf[0..2]);
557552
assert_eq!(wo16.read_u16(), 0x2010);
558-
drop(wo16);
559553

560554
let mut wo32 = WriteOp::from_buf(0, &buf[0..4]);
561555
assert_eq!(wo32.read_u32(), 0x40302010);
562-
drop(wo32);
563556

564557
let mut wo64 = WriteOp::from_buf(0, &buf);
565558
assert_eq!(wo64.read_u64(), 0x8070605040302010);
566-
drop(wo64);
567559
}
568560

569561
#[test]
@@ -588,7 +580,6 @@ mod test {
588580
let mut ro = ReadOp::from_buf(0, &mut buf);
589581
ro.write_u8(0x10);
590582
ro.write_u8(0x20);
591-
drop(ro);
592583
assert_eq!(buf, [0x10, 0x20]);
593584
}
594585

lib/propolis/src/hw/qemu/fwcfg.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -898,7 +898,6 @@ mod test {
898898
let mut val = T::default();
899899
let mut ro = ReadOp::from_buf(0, val.as_mut_bytes());
900900
dev.pio_read(port, &mut ro);
901-
drop(ro);
902901
val
903902
}
904903
fn pio_read_data<T: IntoBytes + FromBytes + Copy + Default>(

lib/propolis/src/util/regmap.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ impl<ID> RegMap<ID> {
130130
let mut sro = ReadOp::from_buf(0, &mut scratch);
131131

132132
f(&reg.id, RWOp::Read(&mut sro));
133-
drop(sro);
134133
copy_op.write_bytes(
135134
&scratch[copy_op.offset()..(copy_op.offset() + copy_op.len())],
136135
);

lib/propolis/src/vmm/mem.rs

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ unsafe impl Sync for Mapping {}
378378
// not reference them directly as a field.
379379
#[allow(dead_code)]
380380
enum Backing<'a> {
381-
Base(Arc<Mapping>),
381+
Base(&'a Mapping),
382382
Sub(&'a SubMapping<'a>),
383383
}
384384

@@ -401,12 +401,9 @@ pub struct SubMapping<'a> {
401401
impl SubMapping<'_> {
402402
/// Create `SubMapping` using the entire region offered by an underlying
403403
/// `Mapping` object.
404-
fn new_base<'a>(
405-
_mem: &'a MemCtx,
406-
base: &'_ Arc<Mapping>,
407-
) -> SubMapping<'a> {
404+
fn new_base<'a>(base: &'a Mapping) -> SubMapping<'a> {
408405
SubMapping {
409-
backing: Backing::Base(base.clone()),
406+
backing: Backing::Base(base),
410407

411408
ptr: base.ptr,
412409
len: base.len,
@@ -427,7 +424,7 @@ impl SubMapping<'_> {
427424
}
428425

429426
#[cfg(test)]
430-
fn new_base_test<'a>(base: Arc<Mapping>) -> SubMapping<'a> {
427+
fn new_base_test(base: &Mapping) -> SubMapping<'_> {
431428
let ptr = base.ptr;
432429
let len = base.len;
433430
let prot = base.prot;
@@ -1121,7 +1118,7 @@ impl MemCtx {
11211118
format!("memory region {} not found", name),
11221119
)
11231120
})?;
1124-
Ok(SubMapping::new_base(self, ent).constrain_access(Prot::WRITE))
1121+
Ok(SubMapping::new_base(ent).constrain_access(Prot::WRITE))
11251122
}
11261123

11271124
/// Like `writable_region`, but accesses the underlying memory segment
@@ -1167,12 +1164,12 @@ impl MemCtx {
11671164
MapKind::MmioReserve => None,
11681165
}?;
11691166

1170-
let guest_map = SubMapping::new_base(self, &seg.map_guest)
1167+
let guest_map = SubMapping::new_base(&seg.map_guest)
11711168
.constrain_access(prot)
11721169
.constrain_region(req_offset, len)
11731170
.expect("mapping offset should be valid");
11741171

1175-
let seg_map = SubMapping::new_base(self, &seg.map_seg)
1172+
let seg_map = SubMapping::new_base(&seg.map_seg)
11761173
.constrain_region(req_offset, len)
11771174
.expect("mapping offset should be valid");
11781175

@@ -1342,15 +1339,15 @@ pub mod test {
13421339
#[test]
13431340
fn mapping_denies_read_beyond_end() {
13441341
let (_hdl, base) = test_setup(Prot::READ);
1345-
let mapping = SubMapping::new_base_test(base);
1342+
let mapping = SubMapping::new_base_test(&base);
13461343

13471344
assert!(mapping.read::<[u8; TEST_LEN + 1]>().is_err());
13481345
}
13491346

13501347
#[test]
13511348
fn mapping_shortens_read_bytes_beyond_end() {
13521349
let (_hdl, base) = test_setup(Prot::READ);
1353-
let mapping = SubMapping::new_base_test(base);
1350+
let mapping = SubMapping::new_base_test(&base);
13541351

13551352
let mut buf: [u8; TEST_LEN + 1] = [0; TEST_LEN + 1];
13561353
assert_eq!(TEST_LEN, mapping.read_bytes(&mut buf).unwrap());
@@ -1359,7 +1356,7 @@ pub mod test {
13591356
#[test]
13601357
fn mapping_shortens_write_bytes_beyond_end() {
13611358
let (_hdl, base) = test_setup(Prot::RW);
1362-
let mapping = SubMapping::new_base_test(base);
1359+
let mapping = SubMapping::new_base_test(&base);
13631360

13641361
let mut buf: [u8; TEST_LEN + 1] = [0; TEST_LEN + 1];
13651362
assert_eq!(TEST_LEN, mapping.write_bytes(&mut buf).unwrap());
@@ -1369,7 +1366,7 @@ pub mod test {
13691366
fn mapping_create_empty() {
13701367
let (_hdl, base) = test_setup(Prot::READ);
13711368
let mapping =
1372-
SubMapping::new_base_test(base).constrain_region(0, 0).unwrap();
1369+
SubMapping::new_base_test(&base).constrain_region(0, 0).unwrap();
13731370

13741371
assert_eq!(0, mapping.len());
13751372
assert!(mapping.is_empty());
@@ -1378,7 +1375,7 @@ pub mod test {
13781375
#[test]
13791376
fn mapping_valid_subregions() {
13801377
let (_hdl, base) = test_setup(Prot::READ);
1381-
let mapping = SubMapping::new_base_test(base);
1378+
let mapping = SubMapping::new_base_test(&base);
13821379

13831380
assert!(mapping.subregion(0, 0).is_some());
13841381
assert!(mapping.subregion(0, TEST_LEN / 2).is_some());
@@ -1388,7 +1385,7 @@ pub mod test {
13881385
#[test]
13891386
fn mapping_invalid_subregions() {
13901387
let (_hdl, base) = test_setup(Prot::READ);
1391-
let mapping = SubMapping::new_base_test(base);
1388+
let mapping = SubMapping::new_base_test(&base);
13921389

13931390
// Beyond the end of the mapping.
13941391
assert!(mapping.subregion(TEST_LEN + 1, 0).is_none());
@@ -1402,7 +1399,7 @@ pub mod test {
14021399
#[test]
14031400
fn subregion_protection() {
14041401
let (_hdl, base) = test_setup(Prot::RW);
1405-
let mapping = SubMapping::new_base_test(base);
1402+
let mapping = SubMapping::new_base_test(&base);
14061403

14071404
// Main region has full access
14081405
let mut buf = [0u8];

0 commit comments

Comments
 (0)