Skip to content

Commit 89c6bea

Browse files
committed
vhost-user: Add support for GET_SHMEM_CONFIG message
Add support for GET_SHMEM_CONFIG message to retrieve VirtIO Shared Memory Regions configuration. This is useful when the frontend is unaware of specific backend type and configuration of the memory layout. Based on the patch [1] which is just waiting for being merged. [1] - https://lore.kernel.org/all/20251111091058.879669-1-aesteve@redhat.com/ Signed-off-by: Albert Esteve <aesteve@redhat.com>
1 parent 59d845c commit 89c6bea

File tree

7 files changed

+208
-2
lines changed

7 files changed

+208
-2
lines changed

vhost-user-backend/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## [Unreleased]
44

55
### Added
6+
- [[#339]](https://github.com/rust-vmm/vhost/pull/339) Add support for `GET_SHMEM_CONFIG` message
7+
68
### Changed
79
### Deprecated
810
### Fixed

vhost-user-backend/src/backend.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use std::sync::{Arc, Mutex, RwLock};
2525

2626
use vhost::vhost_user::message::{
2727
VhostTransferStateDirection, VhostTransferStatePhase, VhostUserProtocolFeatures,
28-
VhostUserSharedMsg,
28+
VhostUserShMemConfig, VhostUserSharedMsg,
2929
};
3030
use vhost::vhost_user::Backend;
3131
use vm_memory::bitmap::Bitmap;
@@ -180,6 +180,13 @@ pub trait VhostUserBackend: Send + Sync {
180180
"back end does not support state transfer",
181181
))
182182
}
183+
184+
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig> {
185+
Err(std::io::Error::new(
186+
std::io::ErrorKind::Unsupported,
187+
"back end does not support shared memory regions",
188+
))
189+
}
183190
}
184191

185192
/// Trait without interior mutability for vhost user backend servers to implement concrete services.
@@ -322,6 +329,13 @@ pub trait VhostUserBackendMut: Send + Sync {
322329
"back end does not support state transfer",
323330
))
324331
}
332+
333+
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig> {
334+
Err(std::io::Error::new(
335+
std::io::ErrorKind::Unsupported,
336+
"back end does not support shared memory regions",
337+
))
338+
}
325339
}
326340

327341
impl<T: VhostUserBackend> VhostUserBackend for Arc<T> {
@@ -411,6 +425,10 @@ impl<T: VhostUserBackend> VhostUserBackend for Arc<T> {
411425
fn check_device_state(&self) -> Result<()> {
412426
self.deref().check_device_state()
413427
}
428+
429+
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig> {
430+
self.deref().get_shmem_config()
431+
}
414432
}
415433

416434
impl<T: VhostUserBackendMut> VhostUserBackend for Mutex<T> {
@@ -503,6 +521,10 @@ impl<T: VhostUserBackendMut> VhostUserBackend for Mutex<T> {
503521
fn check_device_state(&self) -> Result<()> {
504522
self.lock().unwrap().check_device_state()
505523
}
524+
525+
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig> {
526+
self.lock().unwrap().get_shmem_config()
527+
}
506528
}
507529

508530
impl<T: VhostUserBackendMut> VhostUserBackend for RwLock<T> {
@@ -595,6 +617,10 @@ impl<T: VhostUserBackendMut> VhostUserBackend for RwLock<T> {
595617
fn check_device_state(&self) -> Result<()> {
596618
self.read().unwrap().check_device_state()
597619
}
620+
621+
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig> {
622+
self.read().unwrap().get_shmem_config()
623+
}
598624
}
599625

600626
#[cfg(test)]

vhost-user-backend/src/handler.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::bitmap::{BitmapReplace, MemRegionBitmap, MmapLogReg};
1818
use userfaultfd::{Uffd, UffdBuilder};
1919
use vhost::vhost_user::message::{
2020
VhostTransferStateDirection, VhostTransferStatePhase, VhostUserConfigFlags, VhostUserLog,
21-
VhostUserMemoryRegion, VhostUserProtocolFeatures, VhostUserSharedMsg,
21+
VhostUserMemoryRegion, VhostUserProtocolFeatures, VhostUserShMemConfig, VhostUserSharedMsg,
2222
VhostUserSingleMemoryRegion, VhostUserVirtioFeatures, VhostUserVringAddrFlags,
2323
VhostUserVringState,
2424
};
@@ -677,6 +677,12 @@ where
677677
.map_err(VhostUserError::ReqHandlerError)
678678
}
679679

680+
fn get_shmem_config(&self) -> VhostUserResult<VhostUserShMemConfig> {
681+
self.backend
682+
.get_shmem_config()
683+
.map_err(VhostUserError::ReqHandlerError)
684+
}
685+
680686
#[cfg(feature = "postcopy")]
681687
fn postcopy_advice(&mut self) -> VhostUserResult<File> {
682688
let mut uffd_builder = UffdBuilder::new();

vhost/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Added
66
- [[#251]](https://github.com/rust-vmm/vhost/pull/251) Add `SHMEM_MAP` and `SHMEM_UNMAP` support
7+
- [[#339]](https://github.com/rust-vmm/vhost/pull/339) Add support for `GET_SHMEM_CONFIG` message
78

89
### Changed
910
### Deprecated

vhost/src/vhost_user/backend_req_handler.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ pub trait VhostUserBackendReqHandler {
8181
fd: File,
8282
) -> Result<Option<File>>;
8383
fn check_device_state(&self) -> Result<()>;
84+
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig>;
8485
#[cfg(feature = "postcopy")]
8586
fn postcopy_advice(&self) -> Result<File>;
8687
#[cfg(feature = "postcopy")]
@@ -146,6 +147,7 @@ pub trait VhostUserBackendReqHandlerMut {
146147
fd: File,
147148
) -> Result<Option<File>>;
148149
fn check_device_state(&mut self) -> Result<()>;
150+
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig>;
149151
#[cfg(feature = "postcopy")]
150152
fn postcopy_advice(&mut self) -> Result<File>;
151153
#[cfg(feature = "postcopy")]
@@ -289,6 +291,10 @@ impl<T: VhostUserBackendReqHandlerMut> VhostUserBackendReqHandler for Mutex<T> {
289291
self.lock().unwrap().check_device_state()
290292
}
291293

294+
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig> {
295+
self.lock().unwrap().get_shmem_config()
296+
}
297+
292298
#[cfg(feature = "postcopy")]
293299
fn postcopy_advice(&self) -> Result<File> {
294300
self.lock().unwrap().postcopy_advice()
@@ -679,6 +685,11 @@ impl<S: VhostUserBackendReqHandler> BackendReqHandler<S> {
679685
};
680686
self.send_reply_message(&hdr, &msg)?;
681687
}
688+
Ok(FrontendReq::GET_SHMEM_CONFIG) => {
689+
self.check_proto_feature(VhostUserProtocolFeatures::SHMEM)?;
690+
let msg = self.backend.get_shmem_config().unwrap_or_default();
691+
self.send_reply_message(&hdr, &msg)?;
692+
}
682693
#[cfg(feature = "postcopy")]
683694
Ok(FrontendReq::POSTCOPY_ADVISE) => {
684695
self.check_proto_feature(VhostUserProtocolFeatures::PAGEFAULT)?;
@@ -1038,4 +1049,108 @@ mod tests {
10381049
handler.check_state().unwrap_err();
10391050
assert!(handler.as_raw_fd() >= 0);
10401051
}
1052+
1053+
// Helper to send GET_SHMEM_CONFIG request and receive response
1054+
fn send_get_shmem_config_request(
1055+
mut endpoint: Endpoint<VhostUserMsgHeader<FrontendReq>>,
1056+
) -> VhostUserShMemConfig {
1057+
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_SHMEM_CONFIG, 0, 0);
1058+
endpoint.send_message(&hdr, &VhostUserEmpty, None).unwrap();
1059+
1060+
let (reply_hdr, reply_config, rfds) = endpoint.recv_body::<VhostUserShMemConfig>().unwrap();
1061+
assert_eq!(reply_hdr.get_code().unwrap(), FrontendReq::GET_SHMEM_CONFIG);
1062+
assert!(reply_hdr.is_reply());
1063+
assert!(rfds.is_none());
1064+
reply_config
1065+
}
1066+
1067+
// Helper to create handler with SHMEM protocol feature enabled
1068+
fn create_handler_with_shmem(
1069+
backend: Arc<Mutex<DummyBackendReqHandler>>,
1070+
p1: UnixStream,
1071+
) -> BackendReqHandler<Mutex<DummyBackendReqHandler>> {
1072+
let mut handler = BackendReqHandler::new(
1073+
Endpoint::<VhostUserMsgHeader<FrontendReq>>::from_stream(p1),
1074+
backend,
1075+
);
1076+
handler.acked_protocol_features = VhostUserProtocolFeatures::SHMEM.bits();
1077+
handler
1078+
}
1079+
1080+
#[test]
1081+
fn test_get_shmem_config_multiple_regions() {
1082+
let memory_sizes = [
1083+
0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, 0x8000,
1084+
];
1085+
let config = VhostUserShMemConfig::new(8, &memory_sizes);
1086+
1087+
let (p1, p2) = UnixStream::pair().unwrap();
1088+
let mut dummy_backend = DummyBackendReqHandler::new();
1089+
dummy_backend.set_shmem_config(config);
1090+
let mut handler = create_handler_with_shmem(Arc::new(Mutex::new(dummy_backend)), p1);
1091+
1092+
let handle = std::thread::spawn(move || {
1093+
send_get_shmem_config_request(Endpoint::<VhostUserMsgHeader<FrontendReq>>::from_stream(
1094+
p2,
1095+
))
1096+
});
1097+
1098+
handler.handle_request().unwrap();
1099+
1100+
let reply_config = handle.join().unwrap();
1101+
assert_eq!(reply_config.nregions, 8);
1102+
for i in 0..8 {
1103+
assert_eq!(reply_config.memory_sizes[i], (i as u64 + 1) * 0x1000);
1104+
}
1105+
for i in 8..256 {
1106+
assert_eq!(reply_config.memory_sizes[i], 0);
1107+
}
1108+
}
1109+
1110+
#[test]
1111+
fn test_get_shmem_config_non_continuous_regions() {
1112+
// Create a configuration with non-continuous regions
1113+
let memory_sizes = [0x10000, 0, 0x20000, 0, 0, 0, 0, 0];
1114+
let config = VhostUserShMemConfig::new(2, &memory_sizes);
1115+
1116+
let (p1, p2) = UnixStream::pair().unwrap();
1117+
let mut dummy_backend = DummyBackendReqHandler::new();
1118+
dummy_backend.set_shmem_config(config);
1119+
let mut handler = create_handler_with_shmem(Arc::new(Mutex::new(dummy_backend)), p1);
1120+
1121+
let handle = std::thread::spawn(move || {
1122+
send_get_shmem_config_request(Endpoint::<VhostUserMsgHeader<FrontendReq>>::from_stream(
1123+
p2,
1124+
))
1125+
});
1126+
1127+
handler.handle_request().unwrap();
1128+
1129+
let reply_config = handle.join().unwrap();
1130+
assert_eq!(reply_config.nregions, 2);
1131+
assert_eq!(reply_config.memory_sizes[0], 0x10000);
1132+
assert_eq!(reply_config.memory_sizes[1], 0);
1133+
assert_eq!(reply_config.memory_sizes[2], 0x20000);
1134+
for i in 3..256 {
1135+
assert_eq!(reply_config.memory_sizes[i], 0);
1136+
}
1137+
}
1138+
1139+
#[test]
1140+
fn test_get_shmem_config_feature_not_negotiated() {
1141+
// Test that the request fails when SHMEM protocol feature is not negotiated
1142+
let (p1, p2) = UnixStream::pair().unwrap();
1143+
let backend = Arc::new(Mutex::new(DummyBackendReqHandler::new()));
1144+
let mut handler = BackendReqHandler::new(
1145+
Endpoint::<VhostUserMsgHeader<FrontendReq>>::from_stream(p1),
1146+
backend,
1147+
);
1148+
let mut frontend_endpoint = Endpoint::<VhostUserMsgHeader<FrontendReq>>::from_stream(p2);
1149+
1150+
std::thread::spawn(move || {
1151+
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_SHMEM_CONFIG, 0, 0);
1152+
let _ = frontend_endpoint.send_message(&hdr, &VhostUserEmpty, None);
1153+
});
1154+
assert!(handler.handle_request().is_err());
1155+
}
10411156
}

vhost/src/vhost_user/dummy_backend.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub struct DummyBackendReqHandler {
2727
pub vring_enabled: [bool; MAX_QUEUE_NUM],
2828
pub inflight_file: Option<File>,
2929
pub shared_file: Option<File>,
30+
pub shmem_config: Option<VhostUserShMemConfig>,
3031
}
3132

3233
impl DummyBackendReqHandler {
@@ -37,6 +38,12 @@ impl DummyBackendReqHandler {
3738
}
3839
}
3940

41+
/// Set the shared memory configuration to be returned by `get_shmem_config`
42+
pub fn set_shmem_config(&mut self, config: VhostUserShMemConfig) {
43+
self.acked_protocol_features |= VhostUserProtocolFeatures::SHMEM.bits();
44+
self.shmem_config = Some(config);
45+
}
46+
4047
/// Helper to check if VirtioFeature enabled
4148
fn check_feature(&self, feat: VhostUserVirtioFeatures) -> Result<()> {
4249
if self.acked_features & feat.bits() != 0 {
@@ -329,6 +336,15 @@ impl VhostUserBackendReqHandlerMut for DummyBackendReqHandler {
329336
)))
330337
}
331338

339+
fn get_shmem_config(&self) -> Result<VhostUserShMemConfig> {
340+
self.shmem_config.ok_or_else(|| {
341+
Error::ReqHandlerError(std::io::Error::new(
342+
std::io::ErrorKind::Unsupported,
343+
"dummy back end does not support shared memory regions",
344+
))
345+
})
346+
}
347+
332348
#[cfg(feature = "postcopy")]
333349
fn postcopy_advice(&mut self) -> Result<File> {
334350
let file = tempfile::tempfile().unwrap();

vhost/src/vhost_user/message.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ enum_value! {
169169
/// After transferring state, check the backend for any errors that may have
170170
/// occurred during the transfer
171171
CHECK_DEVICE_STATE = 43,
172+
/// Get shared memory regions configuration from the backend.
173+
GET_SHMEM_CONFIG = 44,
172174
}
173175
}
174176

@@ -688,6 +690,44 @@ impl VhostUserSingleMemoryRegion {
688690
unsafe impl ByteValued for VhostUserSingleMemoryRegion {}
689691
impl VhostUserMsgValidator for VhostUserSingleMemoryRegion {}
690692

693+
/// Get shared memory regions configuration.
694+
#[repr(C)]
695+
#[derive(Debug, Clone, Copy)]
696+
pub struct VhostUserShMemConfig {
697+
/// Total number of shared memory regions
698+
pub nregions: u32,
699+
/// Padding for correct alignment
700+
padding: u32,
701+
/// Size of each memory region
702+
pub memory_sizes: [u64; 256],
703+
}
704+
705+
impl Default for VhostUserShMemConfig {
706+
fn default() -> Self {
707+
Self {
708+
nregions: 0,
709+
padding: 0,
710+
memory_sizes: [0; 256],
711+
}
712+
}
713+
}
714+
715+
impl VhostUserShMemConfig {
716+
/// Create a new instance
717+
pub fn new(nregions: u32, memory: &[u64]) -> Self {
718+
let memory_sizes: [u64; 256] = std::array::from_fn(|i| *memory.get(i).unwrap_or(&0));
719+
Self {
720+
nregions,
721+
padding: 0,
722+
memory_sizes,
723+
}
724+
}
725+
}
726+
727+
// SAFETY: Safe because all fields of VhostUserSingleMemoryRegion are POD.
728+
unsafe impl ByteValued for VhostUserShMemConfig {}
729+
impl VhostUserMsgValidator for VhostUserShMemConfig {}
730+
691731
/// Vring state descriptor.
692732
#[repr(C, packed)]
693733
#[derive(Copy, Clone, Default)]

0 commit comments

Comments
 (0)