Skip to content

feat: Refine API for smp#76

Merged
junyu0312 merged 1 commit intomainfrom
smp
Feb 22, 2026
Merged

feat: Refine API for smp#76
junyu0312 merged 1 commit intomainfrom
smp

Conversation

@junyu0312
Copy link
Owner

@junyu0312 junyu0312 commented Feb 22, 2026

Summary by CodeRabbit

  • Refactor
    • Introduced thread-safe device manager access to allow safe concurrent device operations.
    • Centralized MMIO layout checks to improve consistency of memory-mapped I/O handling.
    • Updated device and peripheral interfaces to require thread-safe implementors, enabling safer multi-threaded use and improved stability.

@coderabbitai
Copy link

coderabbitai bot commented Feb 22, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Refactors device/MMIO ownership and concurrency: DeviceManager is wrapped in Arc<Mutex<...>>, DeviceVmExitHandler gains mmio_layout(), MMIO layout is exposed as Arc, Vcpu run paths now accept &MmioLayout, and several device-related traits gained a Send bound.

Changes

Cohort / File(s) Summary
VM exit & run callsite updates
crates/vm-core/src/arch/vm_exit/aarch64.rs, crates/vm-core/src/virt.rs, crates/vm-core/src/virt/hvp.rs, crates/vm-core/src/virt/kvm.rs, crates/vm-core/src/virt/hvp/vcpu.rs, crates/vm-core/src/virt/kvm/vcpu/arch/aarch64.rs, crates/vm-core/src/virt/kvm/vcpu/arch/x86_64.rs
Change signatures to use Arc<Mutex<dyn DeviceVmExitHandler>> at Virt/VM run boundaries and pass &MmioLayout into Vcpu::run; lock device manager to obtain mmio_layout and clone Arc for exit handling.
AArch64 VM exit handler locking
crates/vm-core/src/arch/vm_exit/aarch64.rs
Updated handle_vm_exit signature to accept Arc<Mutex<dyn DeviceVmExitHandler>> and acquire the mutex before calling device mmio_read/mmio_write.
MMIO layout & manager refactor
crates/vm-core/src/device/device_manager.rs, crates/vm-core/src/device/mmio.rs, crates/vm-core/src/device/mmio/mmio_as_manager.rs
Expose mmio_layout() -> Arc<MmioLayout>; store MmioLayout behind Arc in manager, add in_mmio_region(addr) alias, and refactor in_mmio_range to use the layout accessor.
DeviceVmExitHandler trait
crates/vm-core/src/device/vm_exit.rs
Added mmio_layout(&self) -> Arc<MmioLayout> to the trait.
Vcpu API change
crates/vm-core/src/vcpu.rs, crates/vm-core/src/virt/hvp/vcpu.rs
Vcpu::run now takes &MmioLayout instead of &dyn DeviceVmExitHandler; MMIO checks use in_mmio_region.
Vm struct & builder
crates/vm-machine/src/vm.rs
Wrap device_manager in Arc<Mutex<DeviceManager>>; update builder, load, and run callsites to lock/clone as needed.
Trait Send bounds added
crates/vm-core/src/device.rs, crates/vm-core/src/device/mmio/mmio_device.rs, crates/vm-pci/src/device/function.rs, crates/vm-pci/src/types/function.rs, crates/vm-virtio/src/device.rs
Added : Send supertrait to Device, MmioHandler, BarHandler, PciTypeFunctionCommon, PciFunction, and VirtIoDevice to allow cross-thread usage of trait objects.

Sequence Diagram(s)

sequenceDiagram
    participant Vm
    participant Virt
    participant Vcpu
    participant DeviceMgr as DeviceManager\n(Arc<Mutex>)
    participant MMIO as MmioLayout\n(Arc)

    Vm->>Virt: run(device_manager: Arc<Mutex<DeviceManager>>)
    Virt->>DeviceMgr: lock()
    DeviceMgr-->>Virt: guard
    Virt->>MMIO: mmio_layout() -> Arc<MmioLayout>
    MMIO-->>Virt: Arc<MmioLayout>
    Virt->>Vcpu: run(&MmioLayout)
    Vcpu->>MMIO: in_mmio_region(addr)
    MMIO-->>Vcpu: bool
    alt is_mmio
        Vcpu->>DeviceMgr: lock()
        Vcpu->>DeviceMgr: handle_vm_exit(Arc<Mutex<DeviceManager>>)
        DeviceMgr-->>Vcpu: HandleVmExitResult
    end
    Vcpu-->>Virt: VmExitReason
    Virt-->>Vm: Result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hop with Arcs and Mutex bright,
I guard the MMIO through the night,
Layouts shared and traits now Send,
Locks in place for every blend,
A tiny rabbit cheers the thread-safe flight!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too vague and generic. 'Refine API for smp' does not clearly convey the main changes, which involve introducing thread-safe synchronization (Arc/Mutex), adding Send trait bounds, and restructuring MMIO handling across multiple components. Consider a more specific title like 'Introduce thread-safe API design with Arc/Mutex and Send bounds for SMP support' or similar to better reflect the substantive changes made.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch smp

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
crates/vm-machine/src/vm.rs (1)

114-124: Extract irq_chip and mmio_devices before calling load, then drop the lock.
The device_manager lock is held for the entire boot_loader.load() call but is only needed to extract get_irq_chip() and mmio_devices(). Extract these handles before invoking load and drop the lock immediately to reduce contention and avoid potential future re-entrance issues (line 114).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm-machine/src/vm.rs` around lines 114 - 124, Hold the device_manager
lock only long enough to extract the needed handles: call
self.device_manager.lock().unwrap(), call get_irq_chip() (and convert to the
expected Result/error as done now) and collect mmio_devices() into local
variables, then immediately drop the lock before calling boot_loader.load; pass
the extracted irq_chip (use as_ref() if load expects a reference) and the
collected mmio_devices to boot_loader.load so the mutex is not held during the
load call (symbols: self.device_manager.lock().unwrap(),
device_manager.get_irq_chip(), device_manager.mmio_devices(), boot_loader.load).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/vm-core/src/arch/vm_exit/aarch64.rs`:
- Around line 57-58: The VM-exit handlers use device.lock().unwrap(), which will
panic on mutex poisoning; change both occurrences in the MMIORead and MMIOWrite
handlers to handle locking errors by replacing unwrap() with a fallible lock
(e.g., device.lock().map_err(|_| Error::MmioErr)?) and propagate
Err(Error::MmioErr) so the VM-exit returns an MmioErr instead of crashing;
update the code paths in the MMIORead and MMIOWrite functions that currently
call device.lock().unwrap() to map the poisoned error into Error::MmioErr and
use the resulting locked guard for subsequent operations.

In `@crates/vm-core/src/device/vm_exit.rs`:
- Around line 1-13: The DeviceVmExitHandler trait is used as a trait object
inside Arc<Mutex<dyn DeviceVmExitHandler>> across threads, so add a Send
supertrait to the trait definition: change the declaration of
DeviceVmExitHandler to require Send (i.e., pub trait DeviceVmExitHandler: Send {
... }) so that dyn DeviceVmExitHandler is eligible to be sent between threads;
update the trait declaration in vm_exit.rs accordingly.

---

Duplicate comments:
In `@crates/vm-core/src/virt/hvp.rs`:
- Around line 256-262: The code currently unwraps the device_manager mutex
(device_manager.lock().unwrap()) which can panic if the lock is poisoned; update
the lock acquisition to map the PoisonError into a proper error return instead
of unwraping, e.g., replace the unwrap with using lock().map_err(|e| /* convert
to appropriate error type */)? and propagate that error through the function so
mmio_layout is obtained safely before calling get_vcpu_mut(0), vcpu.run(...),
and handle_vm_exit; ensure the same approach is applied to any other
lock().unwrap() usages in this module for consistency (references:
device_manager, mmio_layout, get_vcpu_mut, handle_vm_exit).

---

Nitpick comments:
In `@crates/vm-machine/src/vm.rs`:
- Around line 114-124: Hold the device_manager lock only long enough to extract
the needed handles: call self.device_manager.lock().unwrap(), call
get_irq_chip() (and convert to the expected Result/error as done now) and
collect mmio_devices() into local variables, then immediately drop the lock
before calling boot_loader.load; pass the extracted irq_chip (use as_ref() if
load expects a reference) and the collected mmio_devices to boot_loader.load so
the mutex is not held during the load call (symbols:
self.device_manager.lock().unwrap(), device_manager.get_irq_chip(),
device_manager.mmio_devices(), boot_loader.load).

Comment on lines +57 to +58
let device = device.lock().unwrap();

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the file and the specific lines mentioned
cat -n crates/vm-core/src/arch/vm_exit/aarch64.rs | head -80 | tail -30

Repository: junyu0312/rust-vm

Length of output: 1509


🏁 Script executed:

# Check the Error type definition to understand MmioErr
rg -n 'enum Error|fn MmioErr' crates/vm-core/src/arch/vm_exit/aarch64.rs -A 3

Repository: junyu0312/rust-vm

Length of output: 160


🏁 Script executed:

# Search for all lock().unwrap() patterns in the file
rg -n 'lock\(\)\.unwrap\(\)' crates/vm-core/src/arch/vm_exit/aarch64.rs

Repository: junyu0312/rust-vm

Length of output: 164


🏁 Script executed:

# Check the return type and Error definition in the entire vm-core crate
rg -n 'enum Error' crates/vm-core/src/ -A 10 | head -50

Repository: junyu0312/rust-vm

Length of output: 2788


Handle mutex poisoning gracefully in VM-exit handlers.

lock().unwrap() will panic if the device handler mutex is poisoned. Replace with error mapping to Error::MmioErr so VM-exit handling fails gracefully instead of crashing (lines 57–58 and 68–69).

Suggested fix
-            let device = device.lock().unwrap();
+            let device = device
+                .lock()
+                .map_err(|_| Error::MmioErr("device handler lock poisoned".into()))?;

Apply the same change at both lines 57–58 (MMIORead) and 68–69 (MMIOWrite).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let device = device.lock().unwrap();
let device = device
.lock()
.map_err(|_| Error::MmioErr("device handler lock poisoned".into()))?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm-core/src/arch/vm_exit/aarch64.rs` around lines 57 - 58, The VM-exit
handlers use device.lock().unwrap(), which will panic on mutex poisoning; change
both occurrences in the MMIORead and MMIOWrite handlers to handle locking errors
by replacing unwrap() with a fallible lock (e.g., device.lock().map_err(|_|
Error::MmioErr)?) and propagate Err(Error::MmioErr) so the VM-exit returns an
MmioErr instead of crashing; update the code paths in the MMIORead and MMIOWrite
functions that currently call device.lock().unwrap() to map the poisoned error
into Error::MmioErr and use the resulting locked guard for subsequent
operations.

Comment on lines +1 to 13
use std::sync::Arc;

use crate::device::Result;
use crate::device::mmio::MmioLayout;

pub trait DeviceVmExitHandler {
fn io_in(&mut self, port: u16, data: &mut [u8]) -> Result<()>;
fn io_out(&mut self, port: u16, data: &[u8]) -> Result<()>;
fn mmio_read(&self, addr: u64, len: usize, data: &mut [u8]) -> Result<()>;
fn mmio_write(&self, addr: u64, len: usize, data: &[u8]) -> Result<()>;
fn in_mmio_range(&self, addr: u64) -> bool;
fn mmio_layout(&self) -> Arc<MmioLayout>;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for DeviceVmExitHandler usages and context
rg -n "DeviceVmExitHandler" -g "*.rs" -C 2

Repository: junyu0312/rust-vm

Length of output: 4126


🏁 Script executed:

#!/bin/bash
# Search for Arc<Mutex<dyn DeviceVmExitHandler patterns specifically
rg -n "Arc.*Mutex.*DeviceVmExitHandler" -g "*.rs" -C 2

Repository: junyu0312/rust-vm

Length of output: 1424


🏁 Script executed:

#!/bin/bash
# Check the trait definition more thoroughly to see if Send is already bound
cat -n crates/vm-core/src/device/vm_exit.rs

Repository: junyu0312/rust-vm

Length of output: 651


Add Send bound to DeviceVmExitHandler trait.
The trait is used in Arc<Mutex<dyn DeviceVmExitHandler>> across multiple VM implementations (kvm, hvp) and architecture handlers (aarch64), which suggests cross-thread usage. Without a Send supertrait bound, the trait object cannot be safely sent across threads. Add : Send to the trait definition:

pub trait DeviceVmExitHandler: Send {
    fn io_in(&mut self, port: u16, data: &mut [u8]) -> Result<()>;
    fn io_out(&mut self, port: u16, data: &[u8]) -> Result<()>;
    fn mmio_read(&self, addr: u64, len: usize, data: &mut [u8]) -> Result<()>;
    fn mmio_write(&self, addr: u64, len: usize, data: &[u8]) -> Result<()>;
    fn in_mmio_range(&self, addr: u64) -> bool;
    fn mmio_layout(&self) -> Arc<MmioLayout>;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm-core/src/device/vm_exit.rs` around lines 1 - 13, The
DeviceVmExitHandler trait is used as a trait object inside Arc<Mutex<dyn
DeviceVmExitHandler>> across threads, so add a Send supertrait to the trait
definition: change the declaration of DeviceVmExitHandler to require Send (i.e.,
pub trait DeviceVmExitHandler: Send { ... }) so that dyn DeviceVmExitHandler is
eligible to be sent between threads; update the trait declaration in vm_exit.rs
accordingly.

@junyu0312 junyu0312 merged commit 6e7c9a7 into main Feb 22, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant