Skip to content

Commit aac8541

Browse files
committed
Split endpoints
1 parent 99e280c commit aac8541

File tree

11 files changed

+607
-22
lines changed

11 files changed

+607
-22
lines changed

resources/seccomp/aarch64-unknown-linux-musl.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@
220220
"syscall": "madvise",
221221
"comment": "Used by the VirtIO balloon device and by musl for some customer workloads. It is also used by aws-lc during random number generation. They setup a memory page that mark with MADV_WIPEONFORK to be able to detect forks. They also call it with -1 to see if madvise is supported in certain platforms."
222222
},
223+
{
224+
"syscall": "mincore",
225+
"comment": "Used by get_memory_dirty_bitmap to check if memory pages are resident"
226+
},
223227
{
224228
"syscall": "mmap",
225229
"comment": "Used by the VirtIO balloon device",

resources/seccomp/x86_64-unknown-linux-musl.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@
216216
"syscall": "madvise",
217217
"comment": "Used by the VirtIO balloon device and by musl for some customer workloads. It is also used by aws-lc during random number generation. They setup a memory page that mark with MADV_WIPEONFORK to be able to detect forks. They also call it with -1 to see if madvise is supported in certain platforms."
218218
},
219+
{
220+
"syscall": "mincore",
221+
"comment": "Used by get_memory_dirty_bitmap to check if memory pages are resident"
222+
},
219223
{
220224
"syscall": "mmap",
221225
"comment": "Used by the VirtIO balloon device",
@@ -524,8 +528,8 @@
524528
"comment": "sigaltstack is used by Rust stdlib to remove alternative signal stack during thread teardown."
525529
},
526530
{
527-
"syscall": "getrandom",
528-
"comment": "getrandom is used by `HttpServer` to reinialize `HashMap` after moving to the API thread"
531+
"syscall": "getrandom",
532+
"comment": "getrandom is used by `HttpServer` to reinialize `HashMap` after moving to the API thread"
529533
},
530534
{
531535
"syscall": "accept4",
@@ -1276,4 +1280,4 @@
12761280
}
12771281
]
12781282
}
1279-
}
1283+
}

src/firecracker/src/api_server/parsed_request.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use super::request::logger::parse_put_logger;
2020
use super::request::machine_configuration::{
2121
parse_get_machine_config, parse_patch_machine_config, parse_put_machine_config,
2222
};
23+
use super::request::memory::{parse_get_memory, parse_get_memory_mappings};
2324
use super::request::metrics::parse_put_metrics;
2425
use super::request::mmds::{parse_get_mmds, parse_patch_mmds, parse_put_mmds};
2526
use super::request::net::{parse_patch_net, parse_put_net};
@@ -82,6 +83,14 @@ impl TryFrom<&Request> for ParsedRequest {
8283
Ok(ParsedRequest::new_sync(VmmAction::GetFullVmConfig))
8384
}
8485
(Method::Get, "machine-config", None) => parse_get_machine_config(),
86+
(Method::Get, "memory", None) => match path_tokens.next() {
87+
Some("mappings") => parse_get_memory_mappings(),
88+
None => parse_get_memory(),
89+
_ => Err(RequestError::InvalidPathMethod(
90+
request_uri.to_string(),
91+
Method::Get,
92+
)),
93+
},
8594
(Method::Get, "mmds", None) => parse_get_mmds(),
8695
(Method::Get, _, Some(_)) => method_to_error(Method::Get),
8796
(Method::Put, "actions", Some(body)) => parse_put_actions(body),
@@ -172,6 +181,8 @@ impl ParsedRequest {
172181
}
173182
VmmData::BalloonStats(stats) => Self::success_response_with_data(stats),
174183
VmmData::InstanceInformation(info) => Self::success_response_with_data(info),
184+
VmmData::MemoryMappings(mappings) => Self::success_response_with_data(mappings),
185+
VmmData::Memory(memory) => Self::success_response_with_data(memory),
175186
VmmData::VmmVersion(version) => Self::success_response_with_data(
176187
&serde_json::json!({ "firecracker_version": version.as_str() }),
177188
),
@@ -568,6 +579,12 @@ pub mod tests {
568579
VmmData::InstanceInformation(info) => {
569580
http_response(&serde_json::to_string(info).unwrap(), 200)
570581
}
582+
VmmData::MemoryMappings(mappings) => {
583+
http_response(&serde_json::to_string(mappings).unwrap(), 200)
584+
}
585+
VmmData::Memory(memory) => {
586+
http_response(&serde_json::to_string(memory).unwrap(), 200)
587+
}
571588
VmmData::VmmVersion(version) => http_response(
572589
&serde_json::json!({ "firecracker_version": version.as_str() }).to_string(),
573590
200,
@@ -589,6 +606,15 @@ pub mod tests {
589606
verify_ok_response_with(VmmData::MachineConfiguration(MachineConfig::default()));
590607
verify_ok_response_with(VmmData::MmdsValue(serde_json::from_str("{}").unwrap()));
591608
verify_ok_response_with(VmmData::InstanceInformation(InstanceInfo::default()));
609+
verify_ok_response_with(VmmData::MemoryMappings(
610+
vmm::vmm_config::instance_info::MemoryMappingsResponse { mappings: vec![] },
611+
));
612+
verify_ok_response_with(VmmData::Memory(
613+
vmm::vmm_config::instance_info::MemoryResponse {
614+
resident: vec![],
615+
empty: vec![],
616+
},
617+
));
592618
verify_ok_response_with(VmmData::VmmVersion(String::default()));
593619

594620
// Error.
@@ -662,6 +688,30 @@ pub mod tests {
662688
ParsedRequest::try_from(&req).unwrap();
663689
}
664690

691+
#[test]
692+
fn test_try_from_get_memory_mappings() {
693+
let (mut sender, receiver) = UnixStream::pair().unwrap();
694+
let mut connection = HttpConnection::new(receiver);
695+
sender
696+
.write_all(http_request("GET", "/memory/mappings", None).as_bytes())
697+
.unwrap();
698+
connection.try_read().unwrap();
699+
let req = connection.pop_parsed_request().unwrap();
700+
ParsedRequest::try_from(&req).unwrap();
701+
}
702+
703+
#[test]
704+
fn test_try_from_get_memory() {
705+
let (mut sender, receiver) = UnixStream::pair().unwrap();
706+
let mut connection = HttpConnection::new(receiver);
707+
sender
708+
.write_all(http_request("GET", "/memory", None).as_bytes())
709+
.unwrap();
710+
connection.try_read().unwrap();
711+
let req = connection.pop_parsed_request().unwrap();
712+
ParsedRequest::try_from(&req).unwrap();
713+
}
714+
665715
#[test]
666716
fn test_try_from_get_version() {
667717
let (mut sender, receiver) = UnixStream::pair().unwrap();
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use vmm::logger::{IncMetric, METRICS};
5+
use vmm::rpc_interface::VmmAction;
6+
7+
use super::super::parsed_request::{ParsedRequest, RequestError};
8+
9+
pub(crate) fn parse_get_memory_mappings() -> Result<ParsedRequest, RequestError> {
10+
METRICS.get_api_requests.instance_info_count.inc();
11+
Ok(ParsedRequest::new_sync(VmmAction::GetMemoryMappings))
12+
}
13+
14+
pub(crate) fn parse_get_memory() -> Result<ParsedRequest, RequestError> {
15+
METRICS.get_api_requests.instance_info_count.inc();
16+
Ok(ParsedRequest::new_sync(VmmAction::GetMemory))
17+
}
18+
19+
#[cfg(test)]
20+
mod tests {
21+
use super::*;
22+
use crate::api_server::parsed_request::RequestAction;
23+
24+
#[test]
25+
fn test_parse_get_memory_mappings_request() {
26+
match parse_get_memory_mappings().unwrap().into_parts() {
27+
(RequestAction::Sync(action), _) if *action == VmmAction::GetMemoryMappings => {}
28+
_ => panic!("Test failed."),
29+
}
30+
}
31+
32+
#[test]
33+
fn test_parse_get_memory_request() {
34+
match parse_get_memory().unwrap().into_parts() {
35+
(RequestAction::Sync(action), _) if *action == VmmAction::GetMemory => {}
36+
_ => panic!("Test failed."),
37+
}
38+
}
39+
}

src/firecracker/src/api_server/request/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub mod entropy;
1010
pub mod instance_info;
1111
pub mod logger;
1212
pub mod machine_configuration;
13+
pub mod memory;
1314
pub mod metrics;
1415
pub mod mmds;
1516
pub mod net;

src/firecracker/swagger/firecracker.yaml

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,35 @@ paths:
618618
schema:
619619
$ref: "#/definitions/Error"
620620

621+
/memory/mappings:
622+
get:
623+
summary: Gets the memory mappings with skippable pages bitmap.
624+
operationId: getMemoryMappings
625+
responses:
626+
200:
627+
description: OK
628+
schema:
629+
$ref: "#/definitions/MemoryMappingsResponse"
630+
default:
631+
description: Internal server error
632+
schema:
633+
$ref: "#/definitions/Error"
634+
635+
/memory:
636+
get:
637+
summary: Gets the memory info (resident and empty pages).
638+
description: Returns an object with resident and empty bitmaps. The resident bitmap marks all pages that are resident. The empty bitmap marks zero pages (subset of resident pages). This is checked at the pageSize of each region. All regions must have the same page size.
639+
operationId: getMemory
640+
responses:
641+
200:
642+
description: OK
643+
schema:
644+
$ref: "#/definitions/MemoryResponse"
645+
default:
646+
description: Internal server error
647+
schema:
648+
$ref: "#/definitions/Error"
649+
621650
/version:
622651
get:
623652
summary: Gets the Firecracker version.
@@ -990,11 +1019,6 @@ definitions:
9901019
vmm_version:
9911020
description: MicroVM hypervisor build version.
9921021
type: string
993-
memory_regions:
994-
type: array
995-
description: The regions of the guest memory.
996-
items:
997-
$ref: "#/definitions/GuestMemoryRegionMapping"
9981022

9991023
GuestMemoryRegionMapping:
10001024
type: object
@@ -1017,6 +1041,38 @@ definitions:
10171041
description: The page size in bytes.
10181042
type: integer
10191043

1044+
MemoryMappingsResponse:
1045+
type: object
1046+
description: Response containing memory region mappings.
1047+
required:
1048+
- mappings
1049+
properties:
1050+
mappings:
1051+
type: array
1052+
description: The memory region mappings.
1053+
items:
1054+
$ref: "#/definitions/GuestMemoryRegionMapping"
1055+
1056+
MemoryResponse:
1057+
type: object
1058+
description: Response containing the memory info (resident and empty pages).
1059+
required:
1060+
- resident
1061+
- empty
1062+
properties:
1063+
resident:
1064+
type: array
1065+
description: The resident bitmap as a vector of u64 values. Each bit represents if the page is resident.
1066+
items:
1067+
type: integer
1068+
format: uint64
1069+
empty:
1070+
type: array
1071+
description: The empty bitmap as a vector of u64 values. Each bit represents if the page is zero (empty). This is a subset of the resident pages.
1072+
items:
1073+
type: integer
1074+
format: uint64
1075+
10201076
Logger:
10211077
type: object
10221078
description:

src/vmm/src/rpc_interface.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ use crate::vmm_config::balloon::{
2525
use crate::vmm_config::boot_source::{BootSourceConfig, BootSourceConfigError};
2626
use crate::vmm_config::drive::{BlockDeviceConfig, BlockDeviceUpdateConfig, DriveError};
2727
use crate::vmm_config::entropy::{EntropyDeviceConfig, EntropyDeviceError};
28-
use crate::vmm_config::instance_info::InstanceInfo;
29-
use crate::vmm_config::machine_config::{MachineConfig, MachineConfigUpdate, VmConfigError};
28+
use crate::vmm_config::instance_info::{InstanceInfo, MemoryMappingsResponse, MemoryResponse};
29+
use crate::vmm_config::machine_config::{MachineConfig, MachineConfigError, MachineConfigUpdate};
3030
use crate::vmm_config::metrics::{MetricsConfig, MetricsConfigError};
3131
use crate::vmm_config::mmds::{MmdsConfig, MmdsConfigError};
3232
use crate::vmm_config::net::{
@@ -65,6 +65,10 @@ pub enum VmmAction {
6565
GetVmMachineConfig,
6666
/// Get microVM instance information.
6767
GetVmInstanceInfo,
68+
/// Get memory mappings with skippable pages bitmap.
69+
GetMemoryMappings,
70+
/// Get memory info (resident and empty pages).
71+
GetMemory,
6872
/// Get microVM version.
6973
GetVmmVersion,
7074
/// Flush the metrics. This action can only be called after the logger has been configured.
@@ -189,6 +193,10 @@ pub enum VmmData {
189193
MmdsValue(serde_json::Value),
190194
/// The microVM instance information.
191195
InstanceInformation(InstanceInfo),
196+
/// Memory mappings with skippable pages bitmap.
197+
MemoryMappings(MemoryMappingsResponse),
198+
/// Memory info (resident and empty pages).
199+
Memory(MemoryResponse),
192200
/// The microVM version.
193201
VmmVersion(String),
194202
}
@@ -419,6 +427,7 @@ impl<'a> PrebootApiController<'a> {
419427
&self.vm_resources.vm_config,
420428
))),
421429
GetVmInstanceInfo => Ok(VmmData::InstanceInformation(self.instance_info.clone())),
430+
GetMemoryMappings | GetMemory => Err(VmmActionError::OperationNotSupportedPreBoot),
422431
GetVmmVersion => Ok(VmmData::VmmVersion(self.instance_info.vmm_version.clone())),
423432
InsertBlockDevice(config) => self.insert_block_device(config),
424433
InsertNetworkDevice(config) => self.insert_net_device(config),
@@ -648,14 +657,28 @@ impl RuntimeApiController {
648657
))),
649658
GetVmInstanceInfo => {
650659
let locked_vmm = self.vmm.lock().expect("Poisoned lock");
651-
652-
let mut instance_info = locked_vmm.instance_info();
653-
654-
instance_info.memory_regions =
655-
Some(locked_vmm.guest_memory_mappings(&VmInfo::from(&self.vm_resources)));
656-
660+
let instance_info = locked_vmm.instance_info();
657661
Ok(VmmData::InstanceInformation(instance_info))
658662
}
663+
GetMemoryMappings => {
664+
let locked_vmm = self.vmm.lock().expect("Poisoned lock");
665+
let mappings = locked_vmm
666+
.vm
667+
.guest_memory_mappings(&VmInfo::from(&self.vm_resources));
668+
669+
Ok(VmmData::MemoryMappings(MemoryMappingsResponse { mappings }))
670+
}
671+
GetMemory => {
672+
let locked_vmm = self.vmm.lock().expect("Poisoned lock");
673+
let (resident_bitmap, empty_bitmap) = locked_vmm
674+
.vm
675+
.get_memory_info(&VmInfo::from(&self.vm_resources))
676+
.map_err(|e| VmmActionError::InternalVmm(VmmError::Vm(e)))?;
677+
Ok(VmmData::Memory(MemoryResponse {
678+
resident: resident_bitmap,
679+
empty: empty_bitmap,
680+
}))
681+
}
659682
GetVmmVersion => Ok(VmmData::VmmVersion(
660683
self.vmm.lock().expect("Poisoned lock").version(),
661684
)),

src/vmm/src/vmm_config/instance_info.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,20 @@ pub struct InstanceInfo {
5151
#[serde(skip_serializing_if = "Option::is_none")]
5252
pub memory_regions: Option<Vec<GuestMemoryRegionMapping>>,
5353
}
54+
55+
/// Response structure for the memory mappings endpoint.
56+
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
57+
pub struct MemoryMappingsResponse {
58+
/// The memory region mappings.
59+
pub mappings: Vec<GuestMemoryRegionMapping>,
60+
}
61+
62+
/// Response structure for the memory endpoint.
63+
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
64+
pub struct MemoryResponse {
65+
/// The resident bitmap as a vector of u64 values. Each bit represents if the page is resident.
66+
pub resident: Vec<u64>,
67+
/// The empty bitmap as a vector of u64 values. Each bit represents if the page is zero (empty).
68+
/// This is a subset of the resident pages.
69+
pub empty: Vec<u64>,
70+
}

0 commit comments

Comments
 (0)