Skip to content

Commit d438d0d

Browse files
author
Dorinda Bassey
committed
Add VhostUserRng device implementation
Implements a vhost-user RNG device as a thin wrapper around the core VhostUserDevice. The VMM now switches between the standard RNG device and vhost-user RNG depending on whether a socket path is configured via krun_add_vhost_user_device() This allows us to use the RNG device from the rust-vmm vhost-device running in a separate process for better isolation and flexibility. Signed-off-by: Dorinda Bassey <dbassey@redhat.com>
1 parent cf3590d commit d438d0d

5 files changed

Lines changed: 215 additions & 2 deletions

File tree

src/devices/src/virtio/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pub use self::rng::*;
5959
#[cfg(feature = "snd")]
6060
pub use self::snd::Snd;
6161
#[cfg(feature = "vhost-user")]
62-
pub use self::vhost_user::VhostUserDevice;
62+
pub use self::vhost_user::{VhostUserDevice, VhostUserRng};
6363
pub use self::vsock::*;
6464

6565
/// When the driver initializes the device, it lets the device know about the

src/devices/src/virtio/vhost_user/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@
77
//! to run in separate processes for better isolation and flexibility.
88
99
mod device;
10+
mod rng;
1011

1112
pub use device::VhostUserDevice;
13+
pub use rng::VhostUserRng;
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright 2026, Red Hat Inc. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! Vhost-user RNG device implementation.
5+
6+
use crate::io::{Error, ErrorKind};
7+
use std::io::Result as IoResult;
8+
9+
use vm_memory::GuestMemoryMmap;
10+
11+
use super::device::VhostUserDevice;
12+
use crate::virtio::{ActivateResult, InterruptTransport, Queue as VirtQueue, VirtioDevice};
13+
14+
// RNG device constants
15+
const RNG_DEV_ID: &str = "vhost_user_rng";
16+
const NUM_QUEUES: usize = 1;
17+
const QUEUE_SIZES: &[u16] = &[256; NUM_QUEUES];
18+
const VIRTIO_ID_RNG: u32 = 4;
19+
20+
/// Vhost-user RNG device.
21+
///
22+
/// This is a thin wrapper around VhostUserDevice specialized for the RNG device type.
23+
pub struct VhostUserRng {
24+
inner: VhostUserDevice,
25+
}
26+
27+
impl VhostUserRng {
28+
/// Create a new vhost-user RNG device.
29+
///
30+
/// # Arguments
31+
///
32+
/// * `socket_path` - Path to the vhost-user Unix domain socket
33+
/// * `name` - Device name for logging (None = use default "vhost_user_rng")
34+
/// * `num_queues` - Number of virtqueues (0 = use default)
35+
/// * `queue_sizes` - Size of each queue (empty slice = use defaults)
36+
pub fn new(
37+
socket_path: &str,
38+
name: Option<String>,
39+
num_queues: u16,
40+
queue_sizes: &[u16],
41+
) -> IoResult<Self> {
42+
let (actual_num_queues, actual_queue_sizes): (usize, &[u16]) =
43+
if num_queues == 0 || queue_sizes.is_empty() {
44+
(NUM_QUEUES, QUEUE_SIZES)
45+
} else {
46+
(num_queues as usize, queue_sizes)
47+
};
48+
49+
if actual_num_queues == 0 || actual_queue_sizes.len() != actual_num_queues {
50+
return Err(Error::new(
51+
ErrorKind::InvalidInput,
52+
format!(
53+
"Invalid queue configuration: num_queues={}, queue_sizes.len()={}",
54+
actual_num_queues,
55+
actual_queue_sizes.len()
56+
),
57+
));
58+
}
59+
60+
let queues: Vec<VirtQueue> = actual_queue_sizes
61+
.iter()
62+
.map(|&max_size| VirtQueue::new(max_size))
63+
.collect();
64+
65+
let device_name = name.unwrap_or_else(|| RNG_DEV_ID.to_string());
66+
67+
let inner = VhostUserDevice::new(socket_path, VIRTIO_ID_RNG, device_name, queues)?;
68+
69+
Ok(VhostUserRng { inner })
70+
}
71+
72+
pub fn id(&self) -> &str {
73+
RNG_DEV_ID
74+
}
75+
}
76+
77+
impl VirtioDevice for VhostUserRng {
78+
fn device_type(&self) -> u32 {
79+
self.inner.device_type()
80+
}
81+
82+
fn device_name(&self) -> &str {
83+
self.inner.device_name()
84+
}
85+
86+
fn queues(&self) -> &[VirtQueue] {
87+
self.inner.queues()
88+
}
89+
90+
fn queues_mut(&mut self) -> &mut [VirtQueue] {
91+
self.inner.queues_mut()
92+
}
93+
94+
fn queue_events(&self) -> &[utils::eventfd::EventFd] {
95+
self.inner.queue_events()
96+
}
97+
98+
fn avail_features(&self) -> u64 {
99+
self.inner.avail_features()
100+
}
101+
102+
fn acked_features(&self) -> u64 {
103+
self.inner.acked_features()
104+
}
105+
106+
fn set_acked_features(&mut self, acked_features: u64) {
107+
self.inner.set_acked_features(acked_features)
108+
}
109+
110+
fn read_config(&self, offset: u64, data: &mut [u8]) {
111+
self.inner.read_config(offset, data)
112+
}
113+
114+
fn write_config(&mut self, offset: u64, data: &[u8]) {
115+
self.inner.write_config(offset, data)
116+
}
117+
118+
fn activate(&mut self, mem: GuestMemoryMmap, interrupt: InterruptTransport) -> ActivateResult {
119+
self.inner.activate(mem, interrupt)
120+
}
121+
122+
fn is_activated(&self) -> bool {
123+
self.inner.is_activated()
124+
}
125+
126+
fn reset(&mut self) -> bool {
127+
self.inner.reset()
128+
}
129+
}

src/vmm/src/builder.rs

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,28 @@ pub fn build_microvm(
976976
#[cfg(not(feature = "tee"))]
977977
attach_balloon_device(&mut vmm, event_manager, intc.clone())?;
978978
#[cfg(not(feature = "tee"))]
979-
attach_rng_device(&mut vmm, event_manager, intc.clone())?;
979+
{
980+
#[cfg(feature = "vhost-user")]
981+
{
982+
const VIRTIO_ID_RNG: u32 = 4;
983+
984+
let has_vhost_user_rng = vm_resources
985+
.vhost_user_devices
986+
.iter()
987+
.any(|dev| dev.device_type == VIRTIO_ID_RNG);
988+
989+
if has_vhost_user_rng {
990+
for device_config in &vm_resources.vhost_user_devices {
991+
attach_vhost_user_device(&mut vmm, intc.clone(), device_config)?;
992+
}
993+
} else {
994+
attach_rng_device(&mut vmm, event_manager, intc.clone())?;
995+
}
996+
}
997+
998+
#[cfg(not(feature = "vhost-user"))]
999+
attach_rng_device(&mut vmm, event_manager, intc.clone())?;
1000+
}
9801001
let mut console_id = 0;
9811002
if !vm_resources.disable_implicit_console {
9821003
attach_console_devices(
@@ -2388,6 +2409,62 @@ fn attach_rng_device(
23882409
Ok(())
23892410
}
23902411

2412+
#[cfg(not(feature = "tee"))]
2413+
#[cfg(feature = "vhost-user")]
2414+
fn attach_vhost_user_device(
2415+
vmm: &mut Vmm,
2416+
intc: IrqChip,
2417+
device_config: &VhostUserDeviceConfig,
2418+
) -> std::result::Result<(), StartMicrovmError> {
2419+
use self::StartMicrovmError::*;
2420+
2421+
const VIRTIO_ID_RNG: u32 = 4;
2422+
const VIRTIO_ID_SND: u32 = 25;
2423+
const VIRTIO_ID_CAN: u32 = 36;
2424+
2425+
match device_config.device_type {
2426+
VIRTIO_ID_RNG => {
2427+
let rng = Arc::new(Mutex::new(
2428+
devices::virtio::VhostUserRng::new(
2429+
&device_config.socket_path,
2430+
device_config.name.clone(),
2431+
device_config.num_queues,
2432+
&device_config.queue_sizes,
2433+
)
2434+
.map_err(|e| RegisterRngDevice(device_manager::mmio::Error::VhostUserDevice(e)))?,
2435+
));
2436+
2437+
let id = String::from(rng.lock().unwrap().id());
2438+
attach_mmio_device(vmm, id, intc.clone(), rng).map_err(RegisterRngDevice)?;
2439+
}
2440+
VIRTIO_ID_SND | VIRTIO_ID_CAN => {
2441+
// TODO: Implement when vhost-user sound/CAN device wrappers are added
2442+
return Err(RegisterRngDevice(
2443+
device_manager::mmio::Error::VhostUserDevice(std::io::Error::new(
2444+
std::io::ErrorKind::Unsupported,
2445+
format!(
2446+
"Vhost-user device type {} not yet implemented",
2447+
device_config.device_type
2448+
),
2449+
)),
2450+
));
2451+
}
2452+
_ => {
2453+
return Err(RegisterRngDevice(
2454+
device_manager::mmio::Error::VhostUserDevice(std::io::Error::new(
2455+
std::io::ErrorKind::InvalidInput,
2456+
format!(
2457+
"Unknown vhost-user device type {}",
2458+
device_config.device_type
2459+
),
2460+
)),
2461+
));
2462+
}
2463+
}
2464+
2465+
Ok(())
2466+
}
2467+
23912468
#[cfg(feature = "gpu")]
23922469
#[allow(clippy::too_many_arguments)]
23932470
fn attach_gpu_device(

src/vmm/src/device_manager/kvm/mmio.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ pub enum Error {
4141
DeviceNotFound,
4242
/// Failed to update the mmio device.
4343
UpdateFailed,
44+
/// Failed to create vhost-user device.
45+
#[cfg(feature = "vhost-user")]
46+
VhostUserDevice(io::Error),
4447
}
4548

4649
impl fmt::Display for Error {
@@ -59,6 +62,8 @@ impl fmt::Display for Error {
5962
Error::RegisterIrqFd(ref e) => write!(f, "failed to register irqfd: {e}"),
6063
Error::DeviceNotFound => write!(f, "the device couldn't be found"),
6164
Error::UpdateFailed => write!(f, "failed to update the mmio device"),
65+
#[cfg(feature = "vhost-user")]
66+
Error::VhostUserDevice(ref e) => write!(f, "failed to create vhost-user device: {e}"),
6267
}
6368
}
6469
}

0 commit comments

Comments
 (0)