1use super::handle::LayoutHandle;
7use crate::layout::LayoutDescriptor;
8use anyhow::Result;
9use bincode::{Decode, Encode};
10use serde::{Deserialize, Serialize};
11
12use kvbm_common::LogicalLayoutHandle;
13
14#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
16pub struct WorkerAddress {
17 pub worker_id: u64,
19 pub nixl_agent_name: String,
21}
22
23impl WorkerAddress {
24 pub fn new(worker_id: u64, nixl_agent_name: String) -> Self {
26 Self {
27 worker_id,
28 nixl_agent_name,
29 }
30 }
31}
32
33#[derive(Debug, Clone, Encode, Decode)]
38pub struct LogicalLayoutDescriptor {
39 pub handle: LayoutHandle,
41 #[bincode(with_serde)]
43 pub logical_type: LogicalLayoutHandle,
44 #[bincode(with_serde)]
46 pub layout: LayoutDescriptor,
47}
48
49impl LogicalLayoutDescriptor {
50 pub fn new(
52 handle: LayoutHandle,
53 logical_type: LogicalLayoutHandle,
54 layout: LayoutDescriptor,
55 ) -> Self {
56 Self {
57 handle,
58 logical_type,
59 layout,
60 }
61 }
62
63 pub fn new_with_default_type(handle: LayoutHandle, layout: LayoutDescriptor) -> Self {
72 Self {
73 handle,
74 logical_type: LogicalLayoutHandle::G2,
75 layout,
76 }
77 }
78}
79
80pub type LocalLayoutDescriptor = LogicalLayoutDescriptor;
82
83#[derive(Debug, Encode, Decode)]
87pub struct RdmaLayoutDescriptors {
88 pub worker_address: WorkerAddress,
90 pub nixl_metadata: Vec<u8>,
92 pub layouts: Vec<LogicalLayoutDescriptor>,
94}
95
96#[derive(Clone, Serialize, Deserialize, Encode, Decode)]
102#[serde(transparent)]
103pub struct SerializedLayout(Vec<u8>);
104
105impl SerializedLayout {
106 pub fn pack(
116 worker_address: WorkerAddress,
117 nixl_metadata: Vec<u8>,
118 layouts: Vec<LogicalLayoutDescriptor>,
119 ) -> Result<Self> {
120 let inner = RdmaLayoutDescriptors {
121 worker_address,
122 nixl_metadata,
123 layouts,
124 };
125 let bytes = bincode::encode_to_vec(&inner, bincode::config::standard())
126 .map_err(|e| anyhow::anyhow!("failed to encode managed memory metadata: {}", e))?;
127 Ok(Self(bytes))
128 }
129
130 pub fn unpack(&self) -> Result<RdmaLayoutDescriptors> {
135 let (inner, _) = bincode::decode_from_slice(&self.0, bincode::config::standard())
136 .map_err(|e| anyhow::anyhow!("failed to decode managed memory metadata: {}", e))?;
137 Ok(inner)
138 }
139
140 pub fn as_bytes(&self) -> &[u8] {
142 &self.0
143 }
144
145 pub fn from_bytes(bytes: Vec<u8>) -> Self {
147 Self(bytes)
148 }
149
150 pub fn len(&self) -> usize {
152 self.0.len()
153 }
154
155 pub fn is_empty(&self) -> bool {
157 self.0.is_empty()
158 }
159}
160
161impl std::fmt::Debug for SerializedLayout {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 f.debug_struct("SerializedLayout")
164 .field("size_bytes", &self.len())
165 .finish()
166 }
167}
168
169#[cfg(all(test, feature = "testing-kvbm"))]
170mod tests {
171 use super::*;
172 use crate::layout::{
173 BlockFormat, FullyContiguousDetails, KvBlockLayout, LayoutConfig, LayoutDescriptor,
174 LayoutTypeDetails, NixlMetadata,
175 };
176 use dynamo_memory::{MemoryRegion, StorageKind, nixl};
177 use kvbm_common::LogicalLayoutHandle;
178
179 fn make_test_serialized_layout() -> LayoutDescriptor {
180 let config = LayoutConfig::builder()
181 .num_blocks(2)
182 .num_layers(2)
183 .outer_dim(2)
184 .page_size(4)
185 .inner_dim(8)
186 .dtype_width_bytes(2)
187 .build()
188 .unwrap();
189
190 LayoutDescriptor {
191 version: 1,
192 layout_config: config,
193 location: StorageKind::System,
194 nixl_metadata: NixlMetadata::new("test".to_string(), nixl::MemType::Dram, 0),
195 memory_descriptors: vec![MemoryRegion {
196 addr: 0x1000,
197 size: 4096,
198 }],
199 layout_type_details: LayoutTypeDetails::FullyContiguous(FullyContiguousDetails {
200 block_format: BlockFormat::Operational,
201 kv_block_layout: KvBlockLayout::OperationalNHD,
202 }),
203 }
204 }
205
206 #[test]
207 fn test_worker_address() {
208 let addr = WorkerAddress::new(42, "test_agent".to_string());
209 assert_eq!(addr.worker_id, 42);
210 assert_eq!(addr.nixl_agent_name, "test_agent");
211 }
212
213 #[test]
214 fn test_serialized_layout_with_handle() {
215 let handle = LayoutHandle::new(1, 2);
216 let layout = make_test_serialized_layout();
217 let with_handle = LogicalLayoutDescriptor::new(handle, LogicalLayoutHandle::G2, layout);
218
219 assert_eq!(with_handle.handle, handle);
220 assert_eq!(with_handle.logical_type, LogicalLayoutHandle::G2);
221 }
222
223 #[test]
224 fn test_metadata_pack_unpack() {
225 let worker_address = WorkerAddress::new(100, "worker_100".to_string());
226 let nixl_metadata = vec![1, 2, 3, 4, 5];
227 let layouts = vec![LogicalLayoutDescriptor::new(
228 LayoutHandle::new(100, 1),
229 LogicalLayoutHandle::G2,
230 make_test_serialized_layout(),
231 )];
232
233 let packed =
234 SerializedLayout::pack(worker_address.clone(), nixl_metadata.clone(), layouts).unwrap();
235
236 assert!(!packed.is_empty());
237
238 let unpacked = packed.unpack().unwrap();
239
240 assert_eq!(unpacked.worker_address, worker_address);
241 assert_eq!(unpacked.nixl_metadata, nixl_metadata);
242 assert_eq!(unpacked.layouts.len(), 1);
243 assert_eq!(unpacked.layouts[0].handle.worker_id(), 100);
244 assert_eq!(unpacked.layouts[0].handle.layout_id(), 1);
245 assert_eq!(unpacked.layouts[0].logical_type, LogicalLayoutHandle::G2);
246 }
247
248 #[test]
249 fn test_metadata_multiple_layouts() {
250 let worker_address = WorkerAddress::new(200, "worker_200".to_string());
251 let nixl_metadata = vec![10, 20, 30];
252 let layouts = vec![
253 LogicalLayoutDescriptor::new(
254 LayoutHandle::new(200, 1),
255 LogicalLayoutHandle::G1,
256 make_test_serialized_layout(),
257 ),
258 LogicalLayoutDescriptor::new(
259 LayoutHandle::new(200, 2),
260 LogicalLayoutHandle::G2,
261 make_test_serialized_layout(),
262 ),
263 LogicalLayoutDescriptor::new(
264 LayoutHandle::new(200, 3),
265 LogicalLayoutHandle::G3,
266 make_test_serialized_layout(),
267 ),
268 ];
269
270 let packed =
271 SerializedLayout::pack(worker_address, nixl_metadata, layouts.clone()).unwrap();
272 let unpacked = packed.unpack().unwrap();
273
274 assert_eq!(unpacked.layouts.len(), 3);
275 let expected_logical_types = [
276 LogicalLayoutHandle::G1,
277 LogicalLayoutHandle::G2,
278 LogicalLayoutHandle::G3,
279 ];
280 for (i, layout) in unpacked.layouts.iter().enumerate() {
281 assert_eq!(layout.handle.worker_id(), 200);
282 assert_eq!(layout.handle.layout_id(), (i + 1) as u16);
283 assert_eq!(layout.logical_type, expected_logical_types[i]);
284 }
285 }
286
287 #[test]
288 fn test_metadata_from_bytes() {
289 let worker_address = WorkerAddress::new(42, "test".to_string());
290 let nixl_metadata = vec![1, 2, 3];
291 let layouts = vec![];
292
293 let packed = SerializedLayout::pack(worker_address, nixl_metadata, layouts).unwrap();
294 let bytes = packed.as_bytes().to_vec();
295
296 let restored = SerializedLayout::from_bytes(bytes);
297 let unpacked = restored.unpack().unwrap();
298
299 assert_eq!(unpacked.worker_address.worker_id, 42);
300 }
301}