From 030b1b43c1e71b453a3151e2a301faf7fae28108 Mon Sep 17 00:00:00 2001 From: Changyuan Lyu Date: Mon, 9 Nov 2026 23:57:45 -0800 Subject: [PATCH] refactor(mem): turn RamBus into an RCU cell of Ram snapshots A virtio worker holds RamBus::lock_layout() for as long as its device runs, while any change of the guest RAM layout takes the write lock. A change while a device runs therefore blocks until that device resets. For example, a vCPU hangs when the guest enables the DAX window BAR of a virtio-fs device while a virtio-blk device serves the root filesystem, and so does hot-plugging a device with such a BAR. Borrowing the read-copy-update idea of Linux RCU, - a reader calls RamBus::load() to get an Arc snapshot, and holds the read lock only to clone the Arc; - a writer calls RamBus::update(), which copies the layout, changes the copy and publishes it. A failed update publishes nothing, and Memory changes all RAM ranges of a region in one update. A snapshot holds the ArcMemPages it maps, so the host mappings stay valid as long as anyone uses the snapshot, even after they are removed from the bus. A running worker keeps using the snapshot taken when its device was activated, so it does not see later layout changes yet. Signed-off-by: Changyuan Lyu --- alioth/src/cpu/cpu_amd64/sev.rs | 2 +- alioth/src/mem/addressable.rs | 4 +- alioth/src/mem/mapped.rs | 78 +++++++++++-------- alioth/src/mem/mapped_test.rs | 48 +++++++++--- alioth/src/mem/mem.rs | 53 +++++++++---- alioth/src/virtio/dev/dev.rs | 5 +- alioth/src/virtio/dev/entropy_test.rs | 17 +++- alioth/src/virtio/dev/vsock/uds_vsock_test.rs | 14 ++-- alioth/src/virtio/queue/packed_test.rs | 18 ++--- alioth/src/virtio/queue/queue_test.rs | 6 +- alioth/src/virtio/queue/split_test.rs | 20 ++--- alioth/src/virtio/virtio_test.rs | 2 +- alioth/src/virtio/vu/backend.rs | 4 +- alioth/src/virtio/worker/io_uring_test.rs | 4 +- 14 files changed, 174 insertions(+), 101 deletions(-) diff --git a/alioth/src/cpu/cpu_amd64/sev.rs b/alioth/src/cpu/cpu_amd64/sev.rs index ed88cafb..c15f97a6 100644 --- a/alioth/src/cpu/cpu_amd64/sev.rs +++ b/alioth/src/cpu/cpu_amd64/sev.rs @@ -57,7 +57,7 @@ where fn update_snp_desc(&self, desc: &SevMetadataDesc) -> Result<()> { let mut cpuid_table = SnpCpuidInfo::new_zeroed(); let ram_bus = self.ctx.board.memory.ram_bus(); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let page_type = match desc.type_ { SevDescType::SNP_DESC_MEM => SnpPageType::UNMEASURED, SevDescType::SNP_SECRETS => SnpPageType::SECRETS, diff --git a/alioth/src/mem/addressable.rs b/alioth/src/mem/addressable.rs index b77142f7..cd7314d1 100644 --- a/alioth/src/mem/addressable.rs +++ b/alioth/src/mem/addressable.rs @@ -20,7 +20,7 @@ pub trait SlotBackend { fn size(&self) -> u64; } -#[derive(Debug)] +#[derive(Debug, Clone)] struct Slot where B: SlotBackend, @@ -52,7 +52,7 @@ where } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Addressable where B: SlotBackend, diff --git a/alioth/src/mem/mapped.rs b/alioth/src/mem/mapped.rs index c6fea98b..9b1a77da 100644 --- a/alioth/src/mem/mapped.rs +++ b/alioth/src/mem/mapped.rs @@ -31,7 +31,7 @@ use libc::{ MAP_ANONYMOUS, MAP_FAILED, MAP_PRIVATE, MAP_SHARED, MS_ASYNC, PROT_READ, PROT_WRITE, c_void, madvise, mmap, msync, munmap, }; -use parking_lot::{RwLock, RwLockReadGuard}; +use parking_lot::RwLock; use snafu::ResultExt; use zerocopy::{FromBytes, Immutable, IntoBytes}; @@ -180,14 +180,24 @@ impl ArcMemPages { } } -#[derive(Debug)] +/// A snapshot of the guest RAM layout. +/// +/// A `Ram` does not change once a [`RamBus`] publishes it. Its slots hold +/// reference-counted [`ArcMemPages`], so a snapshot keeps the host mappings +/// it refers to alive, even after they are removed from the bus. +#[derive(Debug, Clone, Default)] pub struct Ram { inner: Addressable, } -#[derive(Debug)] +/// The guest RAM layout, updated in the read-copy-update (RCU) way. +/// +/// A reader takes a snapshot with [`RamBus::load`] and keeps it as long as +/// it needs, while a writer publishes a new layout with [`RamBus::update`] +/// without waiting for readers. +#[derive(Debug, Default)] pub struct RamBus { - ram: RwLock, + ram: RwLock>, } struct Iter<'m> { @@ -397,66 +407,66 @@ impl Ram { } Ok(()) } -} -impl Default for RamBus { - fn default() -> Self { - Self::new() + pub(crate) fn add(&mut self, gpa: u64, user_mem: ArcMemPages) -> Result<()> { + self.inner.add(gpa, user_mem)?; + Ok(()) } -} -impl RamBus { - pub fn lock_layout(&self) -> RwLockReadGuard<'_, Ram> { - self.ram.read() + pub(crate) fn remove(&mut self, gpa: u64) -> Result { + self.inner.remove(gpa) } +} +impl RamBus { pub fn new() -> Self { - Self { - ram: RwLock::new(Ram { - inner: Addressable::default(), - }), - } + Self::default() } - pub(crate) fn add(&self, gpa: u64, user_mem: ArcMemPages) -> Result<(), Error> { - let mut ram = self.ram.write(); - ram.inner.add(gpa, user_mem)?; - Ok(()) + /// Returns a snapshot of the current layout. + /// + /// The snapshot stays valid for as long as the caller holds it, but does + /// not see later updates. + pub fn load(&self) -> Arc { + self.ram.read().clone() } - pub(crate) fn remove(&self, gpa: u64) -> Result { - let mut ram = self.ram.write(); - ram.inner.remove(gpa) + /// Copies the current layout, changes the copy with `f` and publishes it. + /// + /// If `f` fails, nothing is published, so a change of several slots is + /// either fully visible or not visible at all. + pub(crate) fn update(&self, f: impl FnOnce(&mut Ram) -> Result) -> Result { + let mut current = self.ram.write(); + let mut ram = Ram::clone(¤t); + let ret = f(&mut ram)?; + *current = Arc::new(ram); + Ok(ret) } pub fn read(&self, gpa: u64, buf: &mut [u8]) -> Result<()> { - let ram = self.ram.read(); - ram.read(gpa, buf) + self.load().read(gpa, buf) } pub fn write(&self, gpa: u64, buf: &[u8]) -> Result<()> { - let ram = self.ram.read(); - ram.write(gpa, buf) + self.load().write(gpa, buf) } pub fn read_t(&self, gpa: u64) -> Result where T: FromBytes + IntoBytes, { - let ram = self.ram.read(); - ram.read_t(gpa) + self.load().read_t(gpa) } pub fn write_t(&self, gpa: u64, val: &T) -> Result<(), Error> where T: IntoBytes + Immutable, { - let ram = self.ram.read(); - ram.write_t(gpa, val) + self.load().write_t(gpa, val) } pub fn read_range(&self, gpa: u64, len: u64, dst: &mut impl Write) -> Result<()> { - let ram = self.ram.read(); + let ram = self.load(); for r in ram.slice_iter(gpa, len) { dst.write_all(r?).context(error::Write)?; } @@ -464,7 +474,7 @@ impl RamBus { } pub fn write_range(&self, gpa: u64, len: u64, mut src: impl Read) -> Result<()> { - let ram = self.ram.read(); + let ram = self.load(); for r in ram.slice_iter_mut(gpa, len) { src.read_exact(r?).context(error::Read)?; } diff --git a/alioth/src/mem/mapped_test.rs b/alioth/src/mem/mapped_test.rs index 5bc7f2b5..27c84e21 100644 --- a/alioth/src/mem/mapped_test.rs +++ b/alioth/src/mem/mapped_test.rs @@ -38,11 +38,11 @@ fn test_ram_bus_read() { let mem2 = ArcMemPages::from_anonymous(PAGE_SIZE as usize, Some(prot), None).unwrap(); if mem1.addr > mem2.addr { - bus.add(0x0, mem1).unwrap(); - bus.add(PAGE_SIZE, mem2).unwrap(); + bus.update(|ram| ram.add(0x0, mem1)).unwrap(); + bus.update(|ram| ram.add(PAGE_SIZE, mem2)).unwrap(); } else { - bus.add(0x0, mem2).unwrap(); - bus.add(PAGE_SIZE, mem1).unwrap(); + bus.update(|ram| ram.add(0x0, mem2)).unwrap(); + bus.update(|ram| ram.add(PAGE_SIZE, mem1)).unwrap(); } let data = MyStruct { @@ -75,19 +75,45 @@ fn test_ram_bus_read() { let guest_iov = [(0, 16), (PAGE_SIZE - 16, 32), (2 * PAGE_SIZE - 16, 16)]; - let locked_bus = bus.lock_layout(); - let mut iov = locked_bus.translate_iov_mut(&guest_iov).unwrap(); + let ram = bus.load(); + let mut iov = ram.translate_iov_mut(&guest_iov).unwrap(); assert_eq!(iov.len(), 4); assert_matches!((&*data).read_vectored(&mut iov), Ok(64)); drop(iov); - let iov = locked_bus.translate_iov(&guest_iov).unwrap(); + let iov = ram.translate_iov(&guest_iov).unwrap(); assert_eq!(iov.len(), 4); let mut buf_read = Vec::new(); assert_matches!(buf_read.write_vectored(&iov), Ok(64)); assert_eq!(buf_read, data); drop(iov); - drop(locked_bus); - bus.remove(0x0).unwrap(); + drop(ram); + bus.update(|ram| ram.remove(0x0)).unwrap(); +} + +#[test] +fn test_ram_bus_snapshot() { + let bus = RamBus::new(); + let empty = bus.load(); + + // Holding a snapshot does not block updates. + let mem = ArcMemPages::from_anonymous(PAGE_SIZE as usize, None, None).unwrap(); + bus.update(|ram| ram.add(0x0, mem.clone())).unwrap(); + assert_matches!(empty.read_t::(0x0), Err(_)); + let snapshot = bus.load(); + snapshot.write_t(0x0, &0xdeadbeef_u32).unwrap(); + + // A failed update publishes nothing. + let ret = bus.update(|ram| { + ram.add(PAGE_SIZE, mem.clone())?; + ram.add(0x0, mem.clone()) + }); + assert_matches!(ret, Err(_)); + assert_matches!(bus.read_t::(PAGE_SIZE), Err(_)); + + // A removed slot stays accessible through older snapshots. + bus.update(|ram| ram.remove(0x0)).unwrap(); + assert_matches!(bus.read_t::(0x0), Err(_)); + assert_matches!(snapshot.read_t::(0x0), Ok(0xdeadbeef)); } #[test] @@ -97,9 +123,9 @@ fn test_ram_get_array_ptr() { // Two adjacent guest pages, backed by separate host mappings. for gpa in [0, PAGE_SIZE] { let pages = ArcMemPages::from_anonymous(PAGE_SIZE as usize, Some(prot), None).unwrap(); - bus.add(gpa, pages).unwrap(); + bus.update(|ram| ram.add(gpa, pages)).unwrap(); } - let ram = bus.lock_layout(); + let ram = bus.load(); let ptr = ram.get_array_ptr::(PAGE_SIZE - 16, 2).unwrap(); assert_eq!(ptr, ram.get_ptr::(PAGE_SIZE - 16).unwrap()); diff --git a/alioth/src/mem/mem.rs b/alioth/src/mem/mem.rs index 3add4685..b86f3747 100644 --- a/alioth/src/mem/mem.rs +++ b/alioth/src/mem/mem.rs @@ -266,6 +266,19 @@ struct LayoutCallbacks { updated: Vec>, } +/// Iterates over the RAM-backed ranges of `region` placed at `addr`. +fn ram_ranges(addr: u64, region: &MemRegion) -> impl Iterator { + let ranges = region.ranges.iter().scan(addr, |gpa, range| { + let start = *gpa; + *gpa += range.size(); + Some((start, range)) + }); + ranges.filter_map(|(gpa, range)| match range { + MemRange::Ram(pages) | MemRange::DevMem { pages, .. } => Some((gpa, pages)), + MemRange::Emulated(_) | MemRange::Span(_) => None, + }) +} + // lock order: region -> callbacks -> bus #[derive(Debug, Default)] pub struct Memory { @@ -308,7 +321,7 @@ impl Memory { pub fn register_update_callback(&self, callback: Box) -> Result<()> { let _regions = self.regions.lock(); let mut callbacks = self.callbacks.lock(); - let ram = self.ram_bus.lock_layout(); + let ram = self.ram_bus.load(); callback.ram_updated(&ram)?; callbacks.updated.push(callback); Ok(()) @@ -332,15 +345,20 @@ impl Memory { region.validate()?; let mut regions = self.regions.lock(); regions.add(addr, region.clone())?; - let mut offset = 0; let callbacks = self.callbacks.lock(); - let mut ram_updated = false; + let ram_ranges: Vec<_> = ram_ranges(addr, ®ion).collect(); + let ram_updated = !ram_ranges.is_empty(); + if ram_updated { + self.ram_bus.update(|ram| { + for (gpa, pages) in ram_ranges { + ram.add(gpa, pages.clone())?; + } + Ok(()) + })?; + } + let mut offset = 0; for range in ®ion.ranges { let gpa = addr + offset; - if let MemRange::Ram(pages) | MemRange::DevMem { pages, .. } = range { - self.ram_bus.add(gpa, pages.clone())?; - ram_updated = true; - } match range { MemRange::Emulated(r) => { let mut mmio_bus = self.mmio_bus.write(); @@ -361,7 +379,7 @@ impl Memory { offset += range.size(); } if ram_updated { - let ram = self.ram_bus.lock_layout(); + let ram = self.ram_bus.load(); for update_callback in &callbacks.updated { update_callback.ram_updated(&ram)?; } @@ -374,15 +392,20 @@ impl Memory { } fn unmap_region(&self, addr: u64, region: &MemRegion) -> Result<()> { - let mut offset = 0; let callbacks = self.callbacks.lock(); - let mut ram_updated = false; + let ram_ranges: Vec<_> = ram_ranges(addr, region).collect(); + let ram_updated = !ram_ranges.is_empty(); + if ram_updated { + self.ram_bus.update(|ram| { + for (gpa, _) in ram_ranges { + ram.remove(gpa)?; + } + Ok(()) + })?; + } + let mut offset = 0; for range in ®ion.ranges { let gpa = addr + offset; - if let MemRange::Ram(_) | MemRange::DevMem { .. } = range { - self.ram_bus.remove(gpa)?; - ram_updated = true; - } match range { MemRange::Emulated(_) => { let mut mmio_bus = self.mmio_bus.write(); @@ -403,7 +426,7 @@ impl Memory { offset += range.size(); } if ram_updated { - let ram = self.ram_bus.lock_layout(); + let ram = self.ram_bus.load(); for callback in &callbacks.updated { callback.ram_updated(&ram)?; } diff --git a/alioth/src/virtio/dev/dev.rs b/alioth/src/virtio/dev/dev.rs index df8e0d9b..be2771b3 100644 --- a/alioth/src/virtio/dev/dev.rs +++ b/alioth/src/virtio/dev/dev.rs @@ -606,7 +606,7 @@ where fn run(&mut self, param: &StartParam) -> Result<()> { let memory = self.context.param.memory.clone(); - let ram = memory.lock_layout(); + let ram = memory.load(); let feature = param.feature & !VirtioFeature::ACCESS_PLATFORM.bits(); let queue_regs = self.context.param.queue_regs.clone(); let feature = VirtioFeature::from_bits_retain(feature); @@ -656,7 +656,8 @@ where if let Err(e) = self.loop_until_reset() { log::error!("worker {}: {e:?}", self.context.dev.name()); } - // The memory layout lock is released by now. + // The worker has dropped the queues and its guest memory + // snapshot by now. if let Some(seq) = self.context.reset_seq.take() { self.context.ack_reset(seq); } diff --git a/alioth/src/virtio/dev/entropy_test.rs b/alioth/src/virtio/dev/entropy_test.rs index 5f809c8d..89e7d047 100644 --- a/alioth/src/virtio/dev/entropy_test.rs +++ b/alioth/src/virtio/dev/entropy_test.rs @@ -17,6 +17,7 @@ use std::fs::OpenOptions; use std::io::Write; use std::os::unix::fs::OpenOptionsExt; use std::sync::Arc; +use std::thread; use std::time::Duration; use assert_matches::assert_matches; @@ -25,13 +26,14 @@ use tempfile::TempDir; use crate::ffi; use crate::mem::emulated::{Action, Mmio}; +use crate::mem::mapped::ArcMemPages; use crate::sync::notifier::Notifier; use crate::virtio::dev::entropy::{EntropyConfig, EntropySpec}; use crate::virtio::dev::{DevSpec, StartParam, Virtio, WakeEvent, WorkerParam}; use crate::virtio::queue::QueueReg; use crate::virtio::queue::split::SplitQueue; use crate::virtio::queue::tests::GuestQueue; -use crate::virtio::tests::{DATA_ADDR, FakeIrqSender, fixture_queues, fixture_ram_bus}; +use crate::virtio::tests::{DATA_ADDR, FakeIrqSender, MEM_SIZE, fixture_queues, fixture_ram_bus}; use crate::virtio::{DeviceId, FEATURE_BUILT_IN, VirtioFeature}; #[test] @@ -46,7 +48,7 @@ fn entry_config_test() { #[test] fn entropy_test() { let ram_bus = Arc::new(fixture_ram_bus()); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let regs: Arc<[QueueReg]> = Arc::from(fixture_queues(1)); let mut guest_q = GuestQueue::new( @@ -114,6 +116,17 @@ fn entropy_test() { assert_eq!(used0.id, id0); assert_eq!(used0.len, s0.len() as u32); + // The guest memory layout can change while the device is running. + let pages = ArcMemPages::from_anonymous(4 << 10, None, None).unwrap(); + let (done_tx, done_rx) = flume::bounded(1); + let bus = ram_bus.clone(); + thread::spawn(move || { + let ret = bus.update(|ram| ram.add(MEM_SIZE as u64, pages)); + done_tx.send(ret).unwrap(); + }); + let ret = done_rx.recv_timeout(Duration::from_secs(5)); + assert_matches!(ret, Ok(Ok(()))); + writer.write_all(s1.as_bytes()).unwrap(); writer.flush().unwrap(); let id1 = guest_q.add_desc(&[], &[(buf1_addr, 4 << 10)]); diff --git a/alioth/src/virtio/dev/vsock/uds_vsock_test.rs b/alioth/src/virtio/dev/vsock/uds_vsock_test.rs index d6a904f9..c7fa309c 100644 --- a/alioth/src/virtio/dev/vsock/uds_vsock_test.rs +++ b/alioth/src/virtio/dev/vsock/uds_vsock_test.rs @@ -296,7 +296,7 @@ impl<'m> VsockTest<'m> { #[test] fn vsock_conn_test() { let ram_bus = Arc::new(fixture_ram_bus()); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let mut t = VsockTest::new(&ram_bus, &ram); // 0. Setup connection @@ -387,7 +387,7 @@ fn vsock_conn_test() { #[test] fn vsock_host_close_test() { let ram_bus = Arc::new(fixture_ram_bus()); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let mut t = VsockTest::new(&ram_bus, &ram); // Establish a host-initiated connection @@ -415,7 +415,7 @@ fn vsock_host_close_test() { #[test] fn vsock_host_close_no_desc_test() { let ram_bus = Arc::new(fixture_ram_bus()); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let mut t = VsockTest::new(&ram_bus, &ram); // Establish a host-initiated connection @@ -458,7 +458,7 @@ fn vsock_host_close_no_desc_test() { #[test] fn vsock_partial_conn_request_test() { let ram_bus = Arc::new(fixture_ram_bus()); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let mut t = VsockTest::new(&ram_bus, &ram); let mut h2g_stream = t.connect(); @@ -487,7 +487,7 @@ fn vsock_partial_conn_request_test() { #[test] fn vsock_simultaneous_conn_test() { let ram_bus = Arc::new(fixture_ram_bus()); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let mut t = VsockTest::new(&ram_bus, &ram); let buf_addrs = [RX_ADDR, RX_ADDR + 2048]; @@ -523,7 +523,7 @@ fn vsock_simultaneous_conn_test() { #[test] fn vsock_conn_request_eof_test() { let ram_bus = Arc::new(fixture_ram_bus()); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let mut t = VsockTest::new(&ram_bus, &ram); // A client that connects and disconnects without saying anything. @@ -547,7 +547,7 @@ fn vsock_conn_request_eof_test() { #[test] fn vsock_conn_request_close_test() { let ram_bus = Arc::new(fixture_ram_bus()); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let mut t = VsockTest::new(&ram_bus, &ram); // A client that sends a complete request and hangs up before the guest diff --git a/alioth/src/virtio/queue/packed_test.rs b/alioth/src/virtio/queue/packed_test.rs index 8f15a3d3..9581f448 100644 --- a/alioth/src/virtio/queue/packed_test.rs +++ b/alioth/src/virtio/queue/packed_test.rs @@ -143,7 +143,7 @@ impl<'m> VirtQueueGuest<'m> for PackedQueue<'m> { fn disabled_queue() { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; reg.enabled.store(false, Ordering::Relaxed); let split_queue = PackedQueue::new(reg, &ram, false); @@ -158,7 +158,7 @@ fn disabled_queue() { fn queue_size(#[case] size: u16, #[case] valid: bool) { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; reg.size.store(size, Ordering::Relaxed); @@ -177,7 +177,7 @@ fn queue_size(#[case] size: u16, #[case] valid: bool) { fn ring_at_end_of_memory(#[case] entries: u16, #[case] valid: bool) { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; // Place the ring so that only its first `entries` descriptors are in @@ -201,7 +201,7 @@ fn ring_at_end_of_memory(#[case] entries: u16, #[case] valid: bool) { fn chain_longer_than_ring() { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; reg.size.store(4, Ordering::Relaxed); let q = PackedQueue::new(reg, &ram, false).unwrap().unwrap(); @@ -249,7 +249,7 @@ fn write_indirect_table(ram: &Ram, num: u16) { fn unexpected_indirect_desc(#[case] flag_0: DescFlag, #[case] flag_1: DescFlag) { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let q = PackedQueue::new(&queues[0], &ram, false).unwrap().unwrap(); write_indirect_table(&ram, 1); @@ -275,7 +275,7 @@ fn unexpected_indirect_desc(#[case] flag_0: DescFlag, #[case] flag_1: DescFlag) fn indirect_table_len(#[case] num: u32, #[case] valid: bool) { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; reg.size.store(4, Ordering::Relaxed); let q = PackedQueue::new(reg, &ram, false).unwrap().unwrap(); @@ -295,7 +295,7 @@ fn indirect_table_len(#[case] num: u32, #[case] valid: bool) { fn enabled_queue() { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; let q = PackedQueue::new(reg, &ram, false).unwrap().unwrap(); let mut guest_q = GuestQueue::new(PackedQueue::new(reg, &ram, false).unwrap().unwrap(), reg); @@ -356,7 +356,7 @@ fn enabled_queue() { fn enable_notification() { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; let q = PackedQueue::new(reg, &ram, false).unwrap().unwrap(); @@ -396,7 +396,7 @@ fn is_interrupt_enabled( ) { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; let q = PackedQueue::new(reg, &ram, enable_event_idx) .unwrap() diff --git a/alioth/src/virtio/queue/queue_test.rs b/alioth/src/virtio/queue/queue_test.rs index d14d0793..ff4f46b0 100644 --- a/alioth/src/virtio/queue/queue_test.rs +++ b/alioth/src/virtio/queue/queue_test.rs @@ -184,7 +184,7 @@ impl<'a> Read for Reader<'a> { fn test_copy_from_reader() { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; let mut host_q = Queue::new( SplitQueue::new(reg, &ram, false).unwrap().unwrap(), @@ -343,7 +343,7 @@ impl<'a> Write for Writer<'a> { fn test_copy_to_writer() { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; let mut host_q = Queue::new( SplitQueue::new(reg, &ram, false).unwrap().unwrap(), @@ -562,7 +562,7 @@ fn check_handle_deferred<'m, Q>( fn test_handle_deferred(#[case] packed: bool, #[case] event_idx: bool) { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; if packed { let host_q = Queue::new( diff --git a/alioth/src/virtio/queue/split_test.rs b/alioth/src/virtio/queue/split_test.rs index ce181246..372c85f8 100644 --- a/alioth/src/virtio/queue/split_test.rs +++ b/alioth/src/virtio/queue/split_test.rs @@ -104,7 +104,7 @@ impl<'m> VirtQueueGuest<'m> for SplitQueue<'m> { fn disabled_queue() { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; reg.enabled.store(false, Ordering::Relaxed); let split_queue = SplitQueue::new(reg, &ram, false); @@ -115,7 +115,7 @@ fn disabled_queue() { fn invalid_queue_size() { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; reg.size.store(0, Ordering::Relaxed); @@ -147,7 +147,7 @@ enum Ring { fn ring_at_end_of_memory(#[case] ring: Ring, #[case] entries: u16, #[case] valid: bool) { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; // Place the ring so that only its first `entries` entries are in guest @@ -188,7 +188,7 @@ fn ring_at_end_of_memory(#[case] ring: Ring, #[case] entries: u16, #[case] valid fn enabled_queue() { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; let q = SplitQueue::new(reg, &ram, false).unwrap().unwrap(); let mut guest_q = GuestQueue::new(SplitQueue::new(reg, &ram, false).unwrap().unwrap(), reg); @@ -243,7 +243,7 @@ fn enabled_queue() { fn event_idx_enabled() { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; let q = SplitQueue::new(reg, &ram, true).unwrap().unwrap(); unsafe { &*q.used_event.unwrap() }.store(1, Ordering::Release); @@ -283,7 +283,7 @@ fn add_chain(q: &SplitQueue, descs: &[Desc]) { fn unexpected_indirect_desc(#[case] flag: DescFlag, #[case] table_flag: DescFlag) { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let q = SplitQueue::new(&queues[0], &ram, false).unwrap().unwrap(); let table = [desc(DATA_ADDR, 1, table_flag, 0)]; @@ -319,7 +319,7 @@ fn linked_descs(n: u16) -> Vec { fn indirect_chain() { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let q = SplitQueue::new(&queues[0], &ram, false).unwrap().unwrap(); ram.write(DATA_ADDR, b"abcdefg").unwrap(); @@ -360,7 +360,7 @@ fn indirect_chain() { fn chain_len(#[case] direct: u16, #[case] indirect: u16, #[case] valid: bool) { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let reg = &queues[0]; reg.size.store(4, Ordering::Relaxed); let q = SplitQueue::new(reg, &ram, false).unwrap().unwrap(); @@ -391,7 +391,7 @@ fn chain_len(#[case] direct: u16, #[case] indirect: u16, #[case] valid: bool) { fn chain_with_loop(#[case] indirect: bool) { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let q = SplitQueue::new(&queues[0], &ram, false).unwrap().unwrap(); // Descriptors 0 and 1 link to each other, so the chain never ends. @@ -419,7 +419,7 @@ fn chain_with_loop(#[case] indirect: bool) { fn desc_out_of_indirect_table() { let ram_bus = fixture_ram_bus(); let queues = fixture_queues(1); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let q = SplitQueue::new(&queues[0], &ram, false).unwrap().unwrap(); // The indirect table has only descriptor 0, which links to descriptor 1. diff --git a/alioth/src/virtio/virtio_test.rs b/alioth/src/virtio/virtio_test.rs index fd2a34fa..f1d46c20 100644 --- a/alioth/src/virtio/virtio_test.rs +++ b/alioth/src/virtio/virtio_test.rs @@ -30,7 +30,7 @@ const QUEUE_START: u64 = 1 << 20; pub fn fixture_ram_bus() -> RamBus { let host_pages = ArcMemPages::from_anonymous(MEM_SIZE, None, None).unwrap(); let ram_bus = RamBus::new(); - ram_bus.add(0, host_pages).unwrap(); + ram_bus.update(|ram| ram.add(0, host_pages)).unwrap(); ram_bus } diff --git a/alioth/src/virtio/vu/backend.rs b/alioth/src/virtio/vu/backend.rs index 76cdaba2..6239de5b 100644 --- a/alioth/src/virtio/vu/backend.rs +++ b/alioth/src/virtio/vu/backend.rs @@ -420,7 +420,7 @@ impl VuBackend { region.size as usize, libc::PROT_READ | libc::PROT_WRITE, )?; - self.memory.add(region.gpa, user_mem)?; + self.memory.update(|ram| ram.add(region.gpa, user_mem))?; self.init.regions.push(single.region); } (VuFrontMsg::REM_MEM_REG, 40) => { @@ -433,7 +433,7 @@ impl VuBackend { if r.gpa == region.gpa && r.hva == region.hva && r.size == region.size { log::info!("{name}: remove mem: {r:x?}"); self.init.regions.remove(index); - let _ = self.memory.remove(region.gpa); + let _ = self.memory.update(|ram| ram.remove(region.gpa)); break; } } diff --git a/alioth/src/virtio/worker/io_uring_test.rs b/alioth/src/virtio/worker/io_uring_test.rs index 694626e5..b76eec6b 100644 --- a/alioth/src/virtio/worker/io_uring_test.rs +++ b/alioth/src/virtio/worker/io_uring_test.rs @@ -154,7 +154,7 @@ fn io_uring_drain_test() { // The pipe is empty, so the read stays in flight. { - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let mut guest_q = GuestQueue::new( SplitQueue::new(®s[0], &ram, false).unwrap().unwrap(), ®s[0], @@ -192,7 +192,7 @@ fn io_uring_drain_test() { #[test] fn io_uring_drain_cancels_pending_reads() { let ram_bus = fixture_ram_bus(); - let ram = ram_bus.lock_layout(); + let ram = ram_bus.load(); let regs = fixture_queues(1); let mut guest_q = GuestQueue::new( SplitQueue::new(®s[0], &ram, false).unwrap().unwrap(),