Skip to content

Commit 0bb99c5

Browse files
committed
Refactor
1 parent 1133bd6 commit 0bb99c5

File tree

11 files changed

+559
-19
lines changed

11 files changed

+559
-19
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@
212212
"syscall": "madvise",
213213
"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."
214214
},
215+
{
216+
"syscall": "mincore",
217+
"comment": "Used by get_memory_dirty_bitmap to check if memory pages are resident"
218+
},
215219
{
216220
"syscall": "mmap",
217221
"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
@@ -212,6 +212,10 @@
212212
"syscall": "madvise",
213213
"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."
214214
},
215+
{
216+
"syscall": "mincore",
217+
"comment": "Used by get_memory_dirty_bitmap to check if memory pages are resident"
218+
},
215219
{
216220
"syscall": "mmap",
217221
"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",
@@ -1152,4 +1156,4 @@
11521156
}
11531157
]
11541158
}
1155-
}
1159+
}

src/firecracker/src/api_server/parsed_request.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use super::request::logger::parse_put_logger;
2121
use super::request::machine_configuration::{
2222
parse_get_machine_config, parse_patch_machine_config, parse_put_machine_config,
2323
};
24+
use super::request::memory::{parse_get_memory, parse_get_memory_mappings};
2425
use super::request::metrics::parse_put_metrics;
2526
use super::request::mmds::{parse_get_mmds, parse_patch_mmds, parse_put_mmds};
2627
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: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ info:
55
The API is accessible through HTTP calls on specific URLs
66
carrying JSON modeled data.
77
The transport medium is a Unix Domain Socket.
8-
version: 1.12.1
8+
version: 1.12.2
99
termsOfService: ""
1010
contact:
1111
@@ -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.
@@ -996,11 +1025,6 @@ definitions:
9961025
vmm_version:
9971026
description: MicroVM hypervisor build version.
9981027
type: string
999-
memory_regions:
1000-
type: array
1001-
description: The regions of the guest memory.
1002-
items:
1003-
$ref: "#/definitions/GuestMemoryRegionMapping"
10041028

10051029
GuestMemoryRegionMapping:
10061030
type: object
@@ -1023,6 +1047,38 @@ definitions:
10231047
description: The page size in bytes.
10241048
type: integer
10251049

1050+
MemoryMappingsResponse:
1051+
type: object
1052+
description: Response containing memory region mappings.
1053+
required:
1054+
- mappings
1055+
properties:
1056+
mappings:
1057+
type: array
1058+
description: The memory region mappings.
1059+
items:
1060+
$ref: "#/definitions/GuestMemoryRegionMapping"
1061+
1062+
MemoryResponse:
1063+
type: object
1064+
description: Response containing the memory info (resident and empty pages).
1065+
required:
1066+
- resident
1067+
- empty
1068+
properties:
1069+
resident:
1070+
type: array
1071+
description: The resident bitmap as a vector of u64 values. Each bit represents if the page is resident.
1072+
items:
1073+
type: integer
1074+
format: uint64
1075+
empty:
1076+
type: array
1077+
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.
1078+
items:
1079+
type: integer
1080+
format: uint64
1081+
10261082
Logger:
10271083
type: object
10281084
description:

src/vmm/src/rpc_interface.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::vmm_config::balloon::{
2626
use crate::vmm_config::boot_source::{BootSourceConfig, BootSourceConfigError};
2727
use crate::vmm_config::drive::{BlockDeviceConfig, BlockDeviceUpdateConfig, DriveError};
2828
use crate::vmm_config::entropy::{EntropyDeviceConfig, EntropyDeviceError};
29-
use crate::vmm_config::instance_info::InstanceInfo;
29+
use crate::vmm_config::instance_info::{InstanceInfo, MemoryMappingsResponse, MemoryResponse};
3030
use crate::vmm_config::machine_config::{MachineConfig, MachineConfigError, MachineConfigUpdate};
3131
use crate::vmm_config::metrics::{MetricsConfig, MetricsConfigError};
3232
use crate::vmm_config::mmds::{MmdsConfig, MmdsConfigError};
@@ -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.machine_config.clone(),
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),
@@ -649,17 +658,28 @@ impl RuntimeApiController {
649658
)),
650659
GetVmInstanceInfo => {
651660
let locked_vmm = self.vmm.lock().expect("Poisoned lock");
652-
653-
let mut instance_info = locked_vmm.instance_info();
654-
655-
instance_info.memory_regions = Some(
656-
locked_vmm
657-
.vm
658-
.guest_memory_mappings(&VmInfo::from(&self.vm_resources)),
659-
);
660-
661+
let instance_info = locked_vmm.instance_info();
661662
Ok(VmmData::InstanceInformation(instance_info))
662663
}
664+
GetMemoryMappings => {
665+
let locked_vmm = self.vmm.lock().expect("Poisoned lock");
666+
let mappings = locked_vmm
667+
.vm
668+
.guest_memory_mappings(&VmInfo::from(&self.vm_resources));
669+
670+
Ok(VmmData::MemoryMappings(MemoryMappingsResponse { mappings }))
671+
}
672+
GetMemory => {
673+
let locked_vmm = self.vmm.lock().expect("Poisoned lock");
674+
let (resident_bitmap, empty_bitmap) = locked_vmm
675+
.vm
676+
.get_memory_info(&VmInfo::from(&self.vm_resources))
677+
.map_err(|e| VmmActionError::InternalVmm(VmmError::Vm(e)))?;
678+
Ok(VmmData::Memory(MemoryResponse {
679+
resident: resident_bitmap,
680+
empty: empty_bitmap,
681+
}))
682+
}
663683
GetVmmVersion => Ok(VmmData::VmmVersion(
664684
self.vmm.lock().expect("Poisoned lock").version(),
665685
)),

src/vmm/src/vmm_config/instance_info.rs

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

0 commit comments

Comments
 (0)