Skip to content

Commit fc22dee

Browse files
committed
feat(memfd): allow using memfd to back guest memory
Allow backing guest memory using memfd regardless of vhost-user setup. The option is controlled by a field in the memory backend property of the /snapshot/load endpoint. Using memfd is currently valid only for the UFFD backend.
1 parent 458ca91 commit fc22dee

11 files changed

Lines changed: 245 additions & 58 deletions

File tree

blah.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
claude --resume 683a5ffd-bcab-425e-9434-2ff90a83a8ad

src/firecracker/src/api_server/request/snapshot.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,21 @@ fn parse_put_snapshot_load(body: &Body) -> Result<ParsedRequest, RequestError> {
9191
// If `mem_file_path` is specified instead of `mem_backend`, we construct the
9292
// `MemBackendConfig` object from the path specified, with `File` as backend type.
9393
let mem_backend = match snapshot_config.mem_backend {
94-
Some(backend_cfg) => backend_cfg,
94+
Some(backend_cfg) => {
95+
if backend_cfg.use_memfd && backend_cfg.backend_type != MemBackendType::Uffd {
96+
return Err(RequestError::SerdeJson(serde_json::Error::custom(
97+
"use_memfd is only valid when backend_type is Uffd",
98+
)));
99+
}
100+
backend_cfg
101+
}
95102
None => {
96103
MemBackendConfig {
97104
// This is safe to unwrap() because we ensure above that one of the two:
98105
// either `mem_file_path` or `mem_backend` field is always specified.
99106
backend_path: snapshot_config.mem_file_path.unwrap(),
100107
backend_type: MemBackendType::File,
108+
use_memfd: false,
101109
}
102110
}
103111
};
@@ -184,6 +192,7 @@ mod tests {
184192
mem_backend: MemBackendConfig {
185193
backend_path: PathBuf::from("bar"),
186194
backend_type: MemBackendType::File,
195+
use_memfd: false,
187196
},
188197
track_dirty_pages: false,
189198
resume_vm: false,
@@ -215,6 +224,7 @@ mod tests {
215224
mem_backend: MemBackendConfig {
216225
backend_path: PathBuf::from("bar"),
217226
backend_type: MemBackendType::File,
227+
use_memfd: false,
218228
},
219229
track_dirty_pages: true,
220230
resume_vm: false,
@@ -246,6 +256,7 @@ mod tests {
246256
mem_backend: MemBackendConfig {
247257
backend_path: PathBuf::from("bar"),
248258
backend_type: MemBackendType::Uffd,
259+
use_memfd: false,
249260
},
250261
track_dirty_pages: false,
251262
resume_vm: true,
@@ -283,6 +294,7 @@ mod tests {
283294
mem_backend: MemBackendConfig {
284295
backend_path: PathBuf::from("bar"),
285296
backend_type: MemBackendType::Uffd,
297+
use_memfd: false,
286298
},
287299
track_dirty_pages: false,
288300
resume_vm: true,
@@ -314,6 +326,7 @@ mod tests {
314326
mem_backend: MemBackendConfig {
315327
backend_path: PathBuf::from("bar"),
316328
backend_type: MemBackendType::File,
329+
use_memfd: false,
317330
},
318331
track_dirty_pages: false,
319332
resume_vm: true,
@@ -401,6 +414,45 @@ mod tests {
401414
);
402415
parse_put_snapshot(&Body::new(body), Some("invalid")).unwrap_err();
403416
parse_put_snapshot(&Body::new(body), None).unwrap_err();
417+
418+
// use_memfd=true with Uffd backend must be accepted and propagated.
419+
let body = r#"{
420+
"snapshot_path": "foo",
421+
"mem_backend": {
422+
"backend_path": "bar",
423+
"backend_type": "Uffd",
424+
"use_memfd": true
425+
}
426+
}"#;
427+
let expected_config = LoadSnapshotParams {
428+
snapshot_path: PathBuf::from("foo"),
429+
mem_backend: MemBackendConfig {
430+
backend_path: PathBuf::from("bar"),
431+
backend_type: MemBackendType::Uffd,
432+
use_memfd: true,
433+
},
434+
track_dirty_pages: false,
435+
resume_vm: false,
436+
network_overrides: vec![],
437+
clock_realtime: false,
438+
};
439+
let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap();
440+
assert!(parsed_request.parsing_info().take_deprecation_message().is_none());
441+
assert_eq!(
442+
vmm_action_from_request(parsed_request),
443+
VmmAction::LoadSnapshot(expected_config)
444+
);
445+
446+
// use_memfd=true with File backend must be rejected.
447+
let body = r#"{
448+
"snapshot_path": "foo",
449+
"mem_backend": {
450+
"backend_path": "bar",
451+
"backend_type": "File",
452+
"use_memfd": true
453+
}
454+
}"#;
455+
parse_put_snapshot(&Body::new(body), Some("load")).unwrap_err();
404456
}
405457

406458
#[test]

src/firecracker/swagger/firecracker.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,6 +1542,13 @@ definitions:
15421542
2) Path to the UDS where a process is listening for a UFFD initialization
15431543
control payload and open file descriptor that it can use to serve this
15441544
process's guest memory page faults
1545+
use_memfd:
1546+
type: boolean
1547+
description:
1548+
When true, guest memory is backed by a memfd and its file descriptor is
1549+
sent to the UFFD handler over the UFFD socket alongside the UFFD itself.
1550+
Only valid when 'backend_type' is 'Uffd'.
1551+
default: false
15451552

15461553
Metrics:
15471554
type: object

src/vmm/src/devices/virtio/vhost_user.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ pub(crate) mod tests {
485485
memory::create(
486486
regions.iter().copied(),
487487
libc::MAP_PRIVATE,
488-
Some(file),
488+
Some(Arc::new(file)),
489489
false,
490490
)
491491
.unwrap()

0 commit comments

Comments
 (0)