Skip to content

Commit f93cc74

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/[email protected]/ Signed-off-by: Albert Esteve <[email protected]>
1 parent 59d845c commit f93cc74

File tree

7 files changed

+201
-2
lines changed

7 files changed

+201
-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: 118 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,111 @@ mod tests {
10381049
handler.check_state().unwrap_err();
10391050
assert!(handler.as_raw_fd() >= 0);
10401051
}
1052+
1053+
#[test]
1054+
fn test_get_shmem_config_max_regions() {
1055+
// Create a configuration with maximum number of regions (8)
1056+
let memory_sizes = [
1057+
0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, 0x8000,
1058+
];
1059+
let config = VhostUserShMemConfig::new(8, &memory_sizes);
1060+
1061+
let (p1, p2) = UnixStream::pair().unwrap();
1062+
let mut dummy_backend = DummyBackendReqHandler::new();
1063+
dummy_backend.set_shmem_config(config);
1064+
let backend = Arc::new(Mutex::new(dummy_backend));
1065+
let mut handler = BackendReqHandler::new(
1066+
Endpoint::<VhostUserMsgHeader<FrontendReq>>::from_stream(p1),
1067+
backend,
1068+
);
1069+
handler.acked_protocol_features = VhostUserProtocolFeatures::SHMEM.bits();
1070+
1071+
let mut frontend_endpoint = Endpoint::<VhostUserMsgHeader<FrontendReq>>::from_stream(p2);
1072+
1073+
let handle = std::thread::spawn(move || {
1074+
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_SHMEM_CONFIG, 0, 0);
1075+
frontend_endpoint
1076+
.send_message(&hdr, &VhostUserEmpty, None)
1077+
.unwrap();
1078+
1079+
let (reply_hdr, reply_config, rfds) = frontend_endpoint
1080+
.recv_body::<VhostUserShMemConfig>()
1081+
.unwrap();
1082+
assert_eq!(reply_hdr.get_code().unwrap(), FrontendReq::GET_SHMEM_CONFIG);
1083+
assert!(reply_hdr.is_reply());
1084+
assert!(rfds.is_none());
1085+
reply_config
1086+
});
1087+
1088+
handler.handle_request().unwrap();
1089+
1090+
let reply_config = handle.join().unwrap();
1091+
assert_eq!(reply_config.nregions, 8);
1092+
for i in 0..8 {
1093+
assert_eq!(reply_config.memory_sizes[i], (i as u64 + 1) * 0x1000);
1094+
}
1095+
}
1096+
1097+
#[test]
1098+
fn test_get_shmem_config_non_continuous_regions() {
1099+
// Create a configuration with non-continuous regions
1100+
let memory_sizes = [0x10000, 0, 0x20000, 0, 0, 0, 0, 0];
1101+
let config = VhostUserShMemConfig::new(2, &memory_sizes);
1102+
1103+
let (p1, p2) = UnixStream::pair().unwrap();
1104+
let mut dummy_backend = DummyBackendReqHandler::new();
1105+
dummy_backend.set_shmem_config(config);
1106+
let backend = Arc::new(Mutex::new(dummy_backend));
1107+
let mut handler = BackendReqHandler::new(
1108+
Endpoint::<VhostUserMsgHeader<FrontendReq>>::from_stream(p1),
1109+
backend,
1110+
);
1111+
handler.acked_protocol_features = VhostUserProtocolFeatures::SHMEM.bits();
1112+
1113+
let mut frontend_endpoint = Endpoint::<VhostUserMsgHeader<FrontendReq>>::from_stream(p2);
1114+
1115+
let handle = std::thread::spawn(move || {
1116+
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_SHMEM_CONFIG, 0, 0);
1117+
frontend_endpoint
1118+
.send_message(&hdr, &VhostUserEmpty, None)
1119+
.unwrap();
1120+
1121+
let (reply_hdr, reply_config, rfds) = frontend_endpoint
1122+
.recv_body::<VhostUserShMemConfig>()
1123+
.unwrap();
1124+
assert_eq!(reply_hdr.get_code().unwrap(), FrontendReq::GET_SHMEM_CONFIG);
1125+
assert!(reply_hdr.is_reply());
1126+
assert!(rfds.is_none());
1127+
reply_config
1128+
});
1129+
1130+
handler.handle_request().unwrap();
1131+
1132+
let reply_config = handle.join().unwrap();
1133+
assert_eq!(reply_config.nregions, 2);
1134+
assert_eq!(reply_config.memory_sizes[0], 0x10000);
1135+
assert_eq!(reply_config.memory_sizes[1], 0);
1136+
assert_eq!(reply_config.memory_sizes[2], 0x20000);
1137+
for i in 3..8 {
1138+
assert_eq!(reply_config.memory_sizes[i], 0);
1139+
}
1140+
}
1141+
1142+
#[test]
1143+
fn test_get_shmem_config_feature_not_negotiated() {
1144+
// Test that the request fails when SHMEM protocol feature is not negotiated
1145+
let (p1, p2) = UnixStream::pair().unwrap();
1146+
let backend = Arc::new(Mutex::new(DummyBackendReqHandler::new()));
1147+
let mut handler = BackendReqHandler::new(
1148+
Endpoint::<VhostUserMsgHeader<FrontendReq>>::from_stream(p1),
1149+
backend,
1150+
);
1151+
let mut frontend_endpoint = Endpoint::<VhostUserMsgHeader<FrontendReq>>::from_stream(p2);
1152+
1153+
std::thread::spawn(move || {
1154+
let hdr = VhostUserMsgHeader::new(FrontendReq::GET_SHMEM_CONFIG, 0, 0);
1155+
let _ = frontend_endpoint.send_message(&hdr, &VhostUserEmpty, None);
1156+
});
1157+
assert!(handler.handle_request().is_err());
1158+
}
10411159
}

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: 30 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,34 @@ 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, Default, 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; 8],
703+
}
704+
705+
impl VhostUserShMemConfig {
706+
/// Create a new instance
707+
pub fn new(nregions: u32, memory: &[u64]) -> Self {
708+
let memory_sizes: [u64; 8] = std::array::from_fn(|i| *memory.get(i).unwrap_or(&0));
709+
Self {
710+
nregions,
711+
padding: 0,
712+
memory_sizes,
713+
}
714+
}
715+
}
716+
717+
// SAFETY: Safe because all fields of VhostUserSingleMemoryRegion are POD.
718+
unsafe impl ByteValued for VhostUserShMemConfig {}
719+
impl VhostUserMsgValidator for VhostUserShMemConfig {}
720+
691721
/// Vring state descriptor.
692722
#[repr(C, packed)]
693723
#[derive(Copy, Clone, Default)]

0 commit comments

Comments
 (0)