Skip to content
Open
18 changes: 9 additions & 9 deletions docs/hugepages.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ described in our documentation on

## Known Limitations

Currently, hugetlbfs support is mutually exclusive with the following
Firecracker features:

- Memory Ballooning via the [Balloon Device](./ballooning.md)

Furthermore, enabling dirty page tracking for hugepage memory negates the
performance benefits of using huge pages. This is because KVM will
unconditionally establish guest page tables at 4K granularity if dirty page
tracking is enabled, even if the host users huge mappings.
Enabling dirty page tracking for hugepage memory negates the performance
benefits of using huge pages. This is because KVM will unconditionally establish
guest page tables at 4K granularity if dirty page tracking is enabled, even if
the host users huge mappings.

The traditional balloon device reports free pages at 4k granularity, this means
the device is unable to reclaim the hugepage backing of the guest and drop RSS.
However, the balloon can still be inflated and used to restrict memory usage in
the guest.

## FAQ

Expand Down
44 changes: 33 additions & 11 deletions resources/overlay/usr/local/bin/fast_page_fault_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <sys/mman.h> // mmap
#include <time.h> // clock_gettime
#include <fcntl.h> // open
#include <getopt.h> // getopt

#define MEM_SIZE_MIB (128 * 1024 * 1024)
#define NANOS_PER_SEC 1000000000
Expand All @@ -30,20 +31,39 @@ void touch_memory(void *mem, size_t size, char val) {

int main() {
sigset_t set;
int signal;
int signal, character;
void *ptr;
struct timespec start, end;
long duration_nanos;
FILE *out_file;

sigemptyset(&set);
if (sigaddset(&set, SIGUSR1) == -1) {
perror("sigaddset");
return 1;
char *options = 0;
int longindex = 0;
int signal_wait = 1;

struct option longopts[] = {
{"nosignal", no_argument, NULL, 's'},
{NULL, 0, NULL, 0}
};

while((character = getopt_long(argc, argv, "s", longopts, &longindex)) != -1) {
switch (character) {
case 's':
signal_wait = 0;
break;
}
}
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
perror("sigprocmask");
return 1;

if (signal_wait) {
sigemptyset(&set);
if (sigaddset(&set, SIGUSR1) == -1) {
perror("sigaddset");
return 1;
}
if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
perror("sigprocmask");
return 1;
}
}

ptr = mmap(NULL, MEM_SIZE_MIB, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
Expand All @@ -53,9 +73,11 @@ int main() {
return 1;
}

touch_memory(ptr, MEM_SIZE_MIB, 1);
if (signal_wait) {
touch_memory(ptr, MEM_SIZE_MIB, 1);

sigwait(&set, &signal);
sigwait(&set, &signal);
}

clock_gettime(CLOCK_BOOTTIME, &start);
touch_memory(ptr, MEM_SIZE_MIB, 2);
Expand All @@ -76,4 +98,4 @@ int main() {
}

return 0;
}
}
76 changes: 74 additions & 2 deletions src/firecracker/src/api_server/parsed_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl TryFrom<&Request> for ParsedRequest {

match (request.method(), path, request.body.as_ref()) {
(Method::Get, "", None) => parse_get_instance_info(),
(Method::Get, "balloon", None) => parse_get_balloon(path_tokens.next()),
(Method::Get, "balloon", None) => parse_get_balloon(path_tokens),
(Method::Get, "version", None) => parse_get_version(),
(Method::Get, "vm", None) if path_tokens.next() == Some("config") => {
Ok(ParsedRequest::new_sync(VmmAction::GetFullVmConfig))
Expand All @@ -104,7 +104,7 @@ impl TryFrom<&Request> for ParsedRequest {
(Method::Put, "vsock", Some(body)) => parse_put_vsock(body),
(Method::Put, "entropy", Some(body)) => parse_put_entropy(body),
(Method::Put, _, None) => method_to_error(Method::Put),
(Method::Patch, "balloon", Some(body)) => parse_patch_balloon(body, path_tokens.next()),
(Method::Patch, "balloon", body) => parse_patch_balloon(body, path_tokens),
(Method::Patch, "drives", Some(body)) => parse_patch_drive(body, path_tokens.next()),
(Method::Patch, "machine-config", Some(body)) => parse_patch_machine_config(body),
(Method::Patch, "mmds", Some(body)) => parse_patch_mmds(body),
Expand Down Expand Up @@ -175,6 +175,9 @@ impl ParsedRequest {
Self::success_response_with_data(balloon_config)
}
VmmData::BalloonStats(stats) => Self::success_response_with_data(stats),
VmmData::HintingStatus(hinting_status) => {
Self::success_response_with_data(hinting_status)
}
VmmData::InstanceInformation(info) => Self::success_response_with_data(info),
VmmData::VmmVersion(version) => Self::success_response_with_data(
&serde_json::json!({ "firecracker_version": version.as_str() }),
Expand Down Expand Up @@ -325,6 +328,7 @@ pub mod tests {
use micro_http::HttpConnection;
use vmm::builder::StartMicrovmError;
use vmm::cpu_config::templates::test_utils::build_test_template;
use vmm::devices::virtio::balloon::device::HintingStatus;
use vmm::resources::VmmConfig;
use vmm::rpc_interface::VmmActionError;
use vmm::vmm_config::balloon::{BalloonDeviceConfig, BalloonStats};
Expand Down Expand Up @@ -474,6 +478,17 @@ pub mod tests {
&parsed_request,
Err(RequestError::Generic(StatusCode::BadRequest, s)) if s == "Empty PATCH request.",
));

sender
.write_all(http_request("PATCH", "/balloon", None).as_bytes())
.unwrap();
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
let parsed_request = ParsedRequest::try_from(&req);
assert!(matches!(
&parsed_request,
Err(RequestError::Generic(StatusCode::BadRequest, s)) if s == "Empty PATCH request.",
));
}

#[test]
Expand Down Expand Up @@ -559,6 +574,9 @@ pub mod tests {
VmmData::BalloonStats(stats) => {
http_response(&serde_json::to_string(stats).unwrap(), 200)
}
VmmData::HintingStatus(status) => {
http_response(&serde_json::to_string(status).unwrap(), 200)
}
VmmData::Empty => http_response("", 204),
VmmData::FullVmConfig(cfg) => {
http_response(&serde_json::to_string(cfg).unwrap(), 200)
Expand Down Expand Up @@ -588,6 +606,9 @@ pub mod tests {
swap_out: Some(1),
..Default::default()
}));
verify_ok_response_with(VmmData::HintingStatus(HintingStatus {
..Default::default()
}));
verify_ok_response_with(VmmData::Empty);
verify_ok_response_with(VmmData::FullVmConfig(VmmConfig::default()));
verify_ok_response_with(VmmData::MachineConfiguration(MachineConfig::default()));
Expand Down Expand Up @@ -642,6 +663,18 @@ pub mod tests {
ParsedRequest::try_from(&req).unwrap();
}

#[test]
fn test_try_from_get_balloon_hinting() {
let (mut sender, receiver) = UnixStream::pair().unwrap();
let mut connection = HttpConnection::new(receiver);
sender
.write_all(http_request("GET", "/balloon/hinting/status", None).as_bytes())
.unwrap();
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
ParsedRequest::try_from(&req).unwrap();
}

#[test]
fn test_try_from_get_machine_config() {
let (mut sender, receiver) = UnixStream::pair().unwrap();
Expand Down Expand Up @@ -910,13 +943,52 @@ pub mod tests {
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
ParsedRequest::try_from(&req).unwrap();

let body = "{ \"stats_polling_interval_s\": 1 }";
sender
.write_all(http_request("PATCH", "/balloon/statistics", Some(body)).as_bytes())
.unwrap();
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
ParsedRequest::try_from(&req).unwrap();

let body = "{ \"acknowledge_on_stop\": true }";
sender
.write_all(http_request("PATCH", "/balloon/hinting/start", Some(body)).as_bytes())
.unwrap();
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
ParsedRequest::try_from(&req).unwrap();

let body = "{}";
sender
.write_all(http_request("PATCH", "/balloon/hinting/start", Some(body)).as_bytes())
.unwrap();
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
ParsedRequest::try_from(&req).unwrap();

sender
.write_all(http_request("PATCH", "/balloon/hinting/start", None).as_bytes())
.unwrap();
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
ParsedRequest::try_from(&req).unwrap();

let body = "";
sender
.write_all(http_request("PATCH", "/balloon/hinting/stop", Some(body)).as_bytes())
.unwrap();
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
ParsedRequest::try_from(&req).unwrap();

sender
.write_all(http_request("PATCH", "/balloon/hinting/stop", None).as_bytes())
.unwrap();
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
ParsedRequest::try_from(&req).unwrap();
}

#[test]
Expand Down
Loading