diff --git a/.buildkite/pipeline_cross.py b/.buildkite/pipeline_cross.py index c9730721e56..9d254f49e6b 100755 --- a/.buildkite/pipeline_cross.py +++ b/.buildkite/pipeline_cross.py @@ -38,7 +38,7 @@ def restore_step(label, src_instance, src_kv, dst_instance, dst_os, dst_kv): def cross_steps(): """Generate group steps""" instances_x86_64 = ["c5n.metal", "m5n.metal", "m6i.metal", "m6a.metal"] - instances_aarch64 = ["m6g.metal", "m7g.metal"] + instances_aarch64 = ["m7g.metal"] groups = [] commands = [ "./tools/devtool -y build --release", @@ -53,7 +53,7 @@ def cross_steps(): commands, timeout=30, artifact_paths="snapshots/**/*", - instances=instances_x86_64 + instances_aarch64, + instances=instances_x86_64, platforms=DEFAULT_PLATFORMS, ) ) @@ -65,15 +65,10 @@ def cross_steps(): "c5n.metal": ["m5n.metal", "m6i.metal"], "m5n.metal": ["c5n.metal", "m6i.metal"], "m6i.metal": ["c5n.metal", "m5n.metal"], - "m6g.metal": ["m7g.metal"], - "m7g.metal": ["m6g.metal"], } # https://github.com/firecracker-microvm/firecracker/blob/main/docs/kernel-policy.md#experimental-snapshot-compatibility-across-kernel-versions - aarch64_platforms = [ - ("al2", "linux_5.10"), - ("al2023", "linux_6.1"), - ] + aarch64_platforms = [("al2023", "linux_6.1")] perms_aarch64 = itertools.product( instances_aarch64, aarch64_platforms, instances_aarch64, aarch64_platforms ) @@ -91,8 +86,8 @@ def cross_steps(): # the integration tests already test src == dst, so we skip it if src_instance == dst_instance and src_kv == dst_kv: continue - # 5.10 -> 4.14 is not supported - if dst_kv == "linux_4.14": + # newer -> older is not supported, and does not work + if src_kv > dst_kv: continue if src_instance != dst_instance and dst_instance not in supported.get( src_instance, [] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f51b8a41a43..a543effb691 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,19 +1,19 @@ # All markdown files -*.md @xmarcalx @kalyazin @pb8o @sudanl0 +*.md @xmarcalx @kalyazin @pb8o # But not the ones in docs/ docs/*.md # Except these specific ones -docs/getting-started.md @xmarcalx @kalyazin @pb8o @sudanl0 -docs/prod-host-setup.md @xmarcalx @kalyazin @pb8o @sudanl0 +docs/getting-started.md @xmarcalx @kalyazin @pb8o +docs/prod-host-setup.md @xmarcalx @kalyazin @pb8o # Also cover all "*policy*.md" documents -**/*policy*.md @xmarcalx @kalyazin @pb8o @sudanl0 -**/*POLICY*.md @xmarcalx @kalyazin @pb8o @sudanl0 +**/*policy*.md @xmarcalx @kalyazin @pb8o +**/*POLICY*.md @xmarcalx @kalyazin @pb8o # Also these non-md files in the repository root -THIRD_PARTY @xmarcalx @kalyazin @pb8o @sudanl0 -LICENSE @xmarcalx @kalyazin @pb8o @sudanl0 -NOTICE @xmarcalx @kalyazin @pb8o @sudanl0 -PGP-KEY.asc @xmarcalx @kalyazin @pb8o @sudanl0 +THIRD_PARTY @xmarcalx @kalyazin @pb8o +LICENSE @xmarcalx @kalyazin @pb8o +NOTICE @xmarcalx @kalyazin @pb8o +PGP-KEY.asc @xmarcalx @kalyazin @pb8o diff --git a/CHANGELOG.md b/CHANGELOG.md index 82647dd4440..7781f2850ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,12 @@ and this project adheres to UFFD support not being forward-compatible with new ioctl options introduced in Linux 6.6. See also https://github.com/bytecodealliance/userfaultfd-rs/issues/61. +- [#4618](https://github.com/firecracker-microvm/firecracker/pull/4618): On + x86_64, when taking a snapshot, if a vCPU has MSR_IA32_TSC_DEADLINE set to 0, + Firecracker will replace it with the MSR_IA32_TSC value from the same vCPU. + This is to guarantee that the vCPU will continue receiving TSC interrupts + after restoring from the snapshot even if an interrupt is lost when taking a + snapshot. ## \[1.7.0\] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 94aa0997abc..312489a7aa8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,10 +32,10 @@ you want to merge your changes to Firecracker: against the main branch of the Firecracker repository. 1. Add two reviewers to your pull request (a maintainer will do that for you if you're new). Work with your reviewers to address any comments and obtain a - minimum of 2 approvals, at least one of which must be provided by - [a maintainer](MAINTAINERS.md). To update your pull request amend existing - commits whenever applicable and then push the new changes to your pull - request branch. + minimum of 2 approvals from [maintainers](MAINTAINERS.md). To update your + pull request, amend existing commits whenever applicable. Then force-push the + new changes to your pull request branch. Address all review comments you + receive. 1. Once the pull request is approved, one of the maintainers will merge it. ## Request for Comments @@ -119,6 +119,33 @@ Your contribution needs to meet the following standards: } ``` +- Avoid using `Option::unwrap`/`Result::unwrap`. Prefer propagating errors + instead of aborting execution, or using `Option::expect`/`Result::except` if + no alternative exists. Leave a comment explaining why the code will not panic + in practice. Often, `unwrap`s are used because a previous check ensures they + are safe, e.g. + + ```rs + let my_value: u32 = ...; + if my_value <= u16::MAX { + Ok(my_value.try_into::().unwrap()) + } else { + Err(Error::Overflow) + } + ``` + + These can often be rewritten using `.map`/`.map_err` or `match`/`if let` + constructs such as + + ```rs + my_value.try_into::() + .map_err(|_| Error::Overflow) + ``` + + See also + [this PR](https://github.com/firecracker-microvm/firecracker/pull/3557) for a + lot of examples. + - Document your pull requests. Include the reasoning behind each change, and the testing done. diff --git a/Cargo.lock b/Cargo.lock index e31b201f914..becd63584f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -133,9 +133,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8487b59d62764df8231cb371c459314df895b41756df457a1fb1243d65c89195" +checksum = "474d7cec9d0a1126fad1b224b767fcbf351c23b0309bb21ec210bcfd379926a5" dependencies = [ "aws-lc-fips-sys", "aws-lc-sys", @@ -147,9 +147,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c15eb61145320320eb919d9bab524617a7aa4216c78d342fae3a758bc33073e4" +checksum = "7505fc3cb7acbf42699a43a79dd9caa4ed9e99861dfbb837c5c0fb5a0a8d2980" dependencies = [ "bindgen 0.69.4", "cc", @@ -192,7 +192,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -214,7 +214,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.63", + "syn 2.0.66", "which", ] @@ -254,9 +254,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" dependencies = [ "jobserver", "libc", @@ -280,9 +280,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "ciborium" @@ -323,9 +323,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -372,7 +372,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -386,10 +386,10 @@ name = "clippy-tracing" version = "0.1.0" dependencies = [ "clap", - "itertools 0.12.1", + "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", "uuid", "walkdir", ] @@ -411,7 +411,7 @@ checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "cpu-template-helper" -version = "1.8.0-dev" +version = "1.9.0-dev" dependencies = [ "clap", "displaydoc", @@ -523,7 +523,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -534,9 +534,9 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "env_filter" @@ -589,7 +589,7 @@ dependencies = [ [[package]] name = "firecracker" -version = "1.8.0-dev" +version = "1.9.0-dev" dependencies = [ "bincode", "cargo_toml", @@ -750,6 +750,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -758,11 +767,11 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jailer" -version = "1.8.0-dev" +version = "1.9.0-dev" dependencies = [ "libc", "log-instrument", - "nix 0.28.0", + "nix 0.29.0", "regex", "thiserror", "utils", @@ -779,9 +788,9 @@ dependencies = [ [[package]] name = "kvm-bindings" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82e7e8725a39a0015e511a46cc1f7d90cecc180db1610c4d0d4339a9e48bd21" +checksum = "7ac3147c9763fd8fa7865a90d6aee87f157b59167145b38e671bbc66b116f1e8" dependencies = [ "serde", "vmm-sys-util", @@ -814,9 +823,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libloading" @@ -845,9 +854,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" @@ -873,7 +882,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -925,9 +934,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.5.0", "cfg-if", @@ -1010,14 +1019,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] @@ -1088,7 +1097,7 @@ dependencies = [ [[package]] name = "rebase-snap" -version = "1.8.0-dev" +version = "1.9.0-dev" dependencies = [ "displaydoc", "libc", @@ -1162,7 +1171,7 @@ dependencies = [ [[package]] name = "seccompiler" -version = "1.8.0-dev" +version = "1.9.0-dev" dependencies = [ "bincode", "displaydoc", @@ -1185,22 +1194,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.201" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -1216,9 +1225,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -1246,7 +1255,7 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "snapshot-editor" -version = "1.8.0-dev" +version = "1.9.0-dev" dependencies = [ "clap", "clap-num", @@ -1284,9 +1293,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.63" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -1295,22 +1304,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] @@ -1334,9 +1343,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "serde", "serde_spanned", @@ -1346,18 +1355,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", @@ -1522,7 +1531,7 @@ dependencies = [ "device_tree", "displaydoc", "event-manager", - "itertools 0.12.1", + "itertools 0.13.0", "kvm-bindings", "kvm-ioctls", "lazy_static", @@ -1697,9 +1706,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" dependencies = [ "memchr", ] @@ -1722,11 +1731,25 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.66", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 8518d4638a0..1ba4c85d1a0 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -11,5 +11,4 @@ Firecracker is maintained by a dedicated team within Amazon: - Nikita Kalyazin - Pablo Barbachano - Patrick Roy -- Sudan Landge - Takahiro Itazuri diff --git a/docs/getting-started.md b/docs/getting-started.md index a89f0452aea..f9534fb64f2 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -83,13 +83,13 @@ kernel image with a Ubuntu 22.04 rootfs from our CI: ARCH="$(uname -m)" # Download a linux kernel binary -wget https://s3.amazonaws.com/spec.ccfc.min/firecracker-ci/v1.8/${ARCH}/vmlinux-5.10.210 +wget https://s3.amazonaws.com/spec.ccfc.min/firecracker-ci/v1.9/${ARCH}/vmlinux-5.10.217 # Download a rootfs -wget https://s3.amazonaws.com/spec.ccfc.min/firecracker-ci/v1.8/${ARCH}/ubuntu-22.04.ext4 +wget https://s3.amazonaws.com/spec.ccfc.min/firecracker-ci/v1.9/${ARCH}/ubuntu-22.04.ext4 # Download the ssh key for the rootfs -wget https://s3.amazonaws.com/spec.ccfc.min/firecracker-ci/v1.8/${ARCH}/ubuntu-22.04.id_rsa +wget https://s3.amazonaws.com/spec.ccfc.min/firecracker-ci/v1.9/${ARCH}/ubuntu-22.04.id_rsa # Set user read permission on the ssh key chmod 400 ./ubuntu-22.04.id_rsa diff --git a/docs/snapshotting/snapshot-support.md b/docs/snapshotting/snapshot-support.md index 76f12666d6b..a36aa622567 100644 --- a/docs/snapshotting/snapshot-support.md +++ b/docs/snapshotting/snapshot-support.md @@ -171,6 +171,11 @@ The snapshot functionality is still in developer preview due to the following: the data store is not persisted across snapshots. - Configuration information for metrics and logs are not saved to the snapshot. These need to be reconfigured on the restored microVM. +- On x86_64, if a vCPU has MSR_IA32_TSC_DEADLINE set to 0 when a snapshot is + taken, Firecracker replaces it with the MSR_IA32_TSC value from the same vCPU. + This is to guarantee that the vCPU will continue receiving TSC interrupts + after restoring from the snapshot even if an interrupt is lost when taking a + snapshot. ## Snapshot versioning @@ -643,11 +648,8 @@ supported host kernel versions by generating snapshot artifacts through [this tool](../../tools/create_snapshot_artifact) and checking devices' functionality using [this test](../../tests/integration_tests/functional/test_snapshot_restore_cross_kernel.py). -The microVM snapshotted is built from -[this configuration file](../../tools/create_snapshot_artifact/complex_vm_config.json). -The test restores the snapshot and ensures that all the devices set-up in the -configuration file (network devices, disk, vsock, balloon and MMDS) are -operational post-load. +The test restores the snapshot and ensures that all the devices set-up (network +devices, disk, vsock, balloon and MMDS) are operational post-load. In those tests the instance is fixed, except some combinations where we also test across the same CPU family (Intel x86, Gravitons). In general cross-CPU diff --git a/resources/rebuild.sh b/resources/rebuild.sh index cda05d77211..6372c27e066 100755 --- a/resources/rebuild.sh +++ b/resources/rebuild.sh @@ -151,18 +151,11 @@ function get_linux_tarball { echo "Downloading the latest patch version for v$KERNEL_VERSION..." local major_version="${KERNEL_VERSION%%.*}" local url_base="https://cdn.kernel.org/pub/linux/kernel" - # 5.10 kernels starting from 5.10.211 don't build with our - # configuration. For now, pin it to the last working version. - # TODO: once this is fixed upstream we can remove this pin. - if [[ $KERNEL_VERSION == "5.10" ]]; then - local LATEST_VERSION="linux-5.10.210.tar.xz" - else - local LATEST_VERSION=$( - curl -fsSL $url_base/v$major_version.x/ \ - | grep -o "linux-$KERNEL_VERSION\.[0-9]*\.tar.xz" \ - | sort -rV \ - | head -n 1 || true) - fi + local LATEST_VERSION=$( + curl -fsSL $url_base/v$major_version.x/ \ + | grep -o "linux-$KERNEL_VERSION\.[0-9]*\.tar.xz" \ + | sort -rV \ + | head -n 1 || true) # Fetch tarball and sha256 checksum. curl -fsSLO "$url_base/v$major_version.x/sha256sums.asc" curl -fsSLO "$url_base/v$major_version.x/$LATEST_VERSION" diff --git a/src/acpi-tables/Cargo.toml b/src/acpi-tables/Cargo.toml index d548cb181e7..8cf2ce0fc50 100644 --- a/src/acpi-tables/Cargo.toml +++ b/src/acpi-tables/Cargo.toml @@ -8,7 +8,7 @@ license = "Apache-2.0" [dependencies] displaydoc = "0.2.4" -thiserror = "1.0.60" +thiserror = "1.0.61" vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-bitmap"] } zerocopy = { version = "0.7.34", features = ["derive"] } diff --git a/src/clippy-tracing/Cargo.toml b/src/clippy-tracing/Cargo.toml index c7e55601e09..e816c64b43a 100644 --- a/src/clippy-tracing/Cargo.toml +++ b/src/clippy-tracing/Cargo.toml @@ -11,10 +11,10 @@ bench = false [dependencies] clap = { version = "4.5.4", features = ["derive"] } -itertools = "0.12.1" -proc-macro2 = { version = "1.0.82", features = ["span-locations"] } +itertools = "0.13.0" +proc-macro2 = { version = "1.0.85", features = ["span-locations"] } quote = "1.0.36" -syn = { version = "2.0.63", features = ["full", "extra-traits", "visit", "visit-mut", "printing"] } +syn = { version = "2.0.66", features = ["full", "extra-traits", "visit", "visit-mut", "printing"] } walkdir = "2.5.0" [dev-dependencies] diff --git a/src/cpu-template-helper/Cargo.toml b/src/cpu-template-helper/Cargo.toml index 4f5b9dec586..3d5fb3c71b5 100644 --- a/src/cpu-template-helper/Cargo.toml +++ b/src/cpu-template-helper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cpu-template-helper" -version = "1.8.0-dev" +version = "1.9.0-dev" authors = ["Amazon Firecracker team "] edition = "2021" license = "Apache-2.0" @@ -12,11 +12,11 @@ bench = false [dependencies] clap = { version = "4.5.4", features = ["derive", "string"] } displaydoc = "0.2.4" -libc = "0.2.154" +libc = "0.2.155" log-instrument = { path = "../log-instrument", optional = true } -serde = { version = "1.0.201", features = ["derive"] } +serde = { version = "1.0.203", features = ["derive"] } serde_json = "1.0.117" -thiserror = "1.0.60" +thiserror = "1.0.61" vmm = { path = "../vmm" } vmm-sys-util = { version = "0.12.1", features = ["with-serde"] } diff --git a/src/cpu-template-helper/src/fingerprint/compare.rs b/src/cpu-template-helper/src/fingerprint/compare.rs index 74c7ffcdba6..13198d09a78 100644 --- a/src/cpu-template-helper/src/fingerprint/compare.rs +++ b/src/cpu-template-helper/src/fingerprint/compare.rs @@ -7,7 +7,7 @@ use crate::fingerprint::{Fingerprint, FingerprintField}; #[derive(Debug, thiserror::Error, displaydoc::Display)] pub enum FingerprintCompareError { - /// Difference detected between source and target:\n{0} + /// Difference detected between source and target: {0} DiffDetected(String), /// Failed to serialize/deserialize JSON: {0} Serde(#[from] serde_json::Error), @@ -85,7 +85,10 @@ pub fn compare( if results.is_empty() { Ok(()) } else { - Err(FingerprintCompareError::DiffDetected(results.join("\n"))) + Err(FingerprintCompareError::DiffDetected(format!( + "\n{}", + results.join("\n") + ))) } } @@ -132,7 +135,7 @@ mod tests { Err(FingerprintCompareError::DiffDetected(err)) => { assert_eq!( err, - "{\ + "\n{\ \n \"name\": \"kernel_version\",\ \n \"prev\": \"sample_kernel_version\",\ \n \"curr\": \"different_kernel_version\"\ diff --git a/src/cpu-template-helper/src/main.rs b/src/cpu-template-helper/src/main.rs index 4730c08ac20..11f762d993c 100644 --- a/src/cpu-template-helper/src/main.rs +++ b/src/cpu-template-helper/src/main.rs @@ -190,14 +190,14 @@ fn run(cli: Cli) -> Result<(), HelperError> { Ok(()) } -fn main() -> Result<(), HelperError> { +fn main() -> std::process::ExitCode { let cli = Cli::parse(); let result = run(cli); if let Err(e) = result { eprintln!("{}", e); - Err(e) + std::process::ExitCode::FAILURE } else { - Ok(()) + std::process::ExitCode::SUCCESS } } diff --git a/src/cpu-template-helper/src/template/dump/x86_64.rs b/src/cpu-template-helper/src/template/dump/x86_64.rs index e859a709131..61be42d6aa3 100644 --- a/src/cpu-template-helper/src/template/dump/x86_64.rs +++ b/src/cpu-template-helper/src/template/dump/x86_64.rs @@ -68,9 +68,11 @@ fn msrs_to_modifier(msrs: &HashMap) -> Vec { // Fireracker diables some features (e.g., PMU) and doesn't support some features (e.g., Hyper-V), // MSRs related to such features are not useful as CPU configuration dump. Excluding such MSRs // reduces maintenance cost when KVM makes change their default values. -const MSR_EXCLUSION_LIST: [MsrRange; 9] = [ +const MSR_EXCLUSION_LIST: [MsrRange; 10] = [ // - MSR_IA32_TSC (0x10): vary depending on the elapsed time. MSR_RANGE!(MSR_IA32_TSC), + // - MSR_IA32_TSC_DEADLINE (0x6e0): varies depending on the elapsed time. + MSR_RANGE!(MSR_IA32_TSC_DEADLINE), // Firecracker doesn't support MCE. // - MSR_IA32_MCG_STATUS (0x17a) // - MSR_IA32_MCG_EXT_CTL (0x4d0) diff --git a/src/firecracker/Cargo.toml b/src/firecracker/Cargo.toml index 117847bea08..fafa587c708 100644 --- a/src/firecracker/Cargo.toml +++ b/src/firecracker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "firecracker" -version = "1.8.0-dev" +version = "1.9.0-dev" authors = ["Amazon Firecracker team "] edition = "2021" build = "build.rs" @@ -18,32 +18,32 @@ bench = false [dependencies] displaydoc = "0.2.4" event-manager = "0.4.0" -libc = "0.2.154" +libc = "0.2.155" log-instrument = { path = "../log-instrument", optional = true } micro_http = { git = "https://github.com/firecracker-microvm/micro-http" } seccompiler = { path = "../seccompiler" } -serde = { version = "1.0.201", features = ["derive"] } +serde = { version = "1.0.203", features = ["derive"] } serde_derive = "1.0.136" serde_json = "1.0.117" -thiserror = "1.0.60" +thiserror = "1.0.61" timerfd = "1.6.0" utils = { path = "../utils" } vmm = { path = "../vmm" } [dev-dependencies] cargo_toml = "0.20.2" -libc = "0.2.154" +libc = "0.2.155" regex = { version = "1.10.4", default-features = false, features = ["std", "unicode-perl"] } # Dev-Dependencies for uffd examples -serde = { version = "1.0.201", features = ["derive"] } +serde = { version = "1.0.203", features = ["derive"] } userfaultfd = "0.8.1" [build-dependencies] bincode = "1.2.1" seccompiler = { path = "../seccompiler" } -serde = { version = "1.0.201" } +serde = { version = "1.0.203" } serde_json = "1.0.117" [features] diff --git a/src/firecracker/swagger/firecracker.yaml b/src/firecracker/swagger/firecracker.yaml index 6140beff730..69b1a4f5577 100644 --- a/src/firecracker/swagger/firecracker.yaml +++ b/src/firecracker/swagger/firecracker.yaml @@ -5,7 +5,7 @@ info: The API is accessible through HTTP calls on specific URLs carrying JSON modeled data. The transport medium is a Unix Domain Socket. - version: 1.8.0-dev + version: 1.9.0-dev termsOfService: "" contact: email: "compute-capsule@amazon.com" diff --git a/src/jailer/Cargo.toml b/src/jailer/Cargo.toml index 08764d8015f..a57f81eabcb 100644 --- a/src/jailer/Cargo.toml +++ b/src/jailer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jailer" -version = "1.8.0-dev" +version = "1.9.0-dev" authors = ["Amazon Firecracker team "] edition = "2021" description = "Process for starting Firecracker in production scenarios; applies a cgroup/namespace isolation barrier and then drops privileges." @@ -12,11 +12,11 @@ name = "jailer" bench = false [dependencies] -libc = "0.2.154" +libc = "0.2.155" log-instrument = { path = "../log-instrument", optional = true } -nix = { version = "0.28.0", default-features = false, features = ["dir"] } +nix = { version = "0.29.0", default-features = false, features = ["dir"] } regex = { version = "1.10.4", default-features = false, features = ["std"] } -thiserror = "1.0.60" +thiserror = "1.0.61" utils = { path = "../utils" } diff --git a/src/log-instrument-macros/Cargo.toml b/src/log-instrument-macros/Cargo.toml index 854f6c0825b..11f88beb294 100644 --- a/src/log-instrument-macros/Cargo.toml +++ b/src/log-instrument-macros/Cargo.toml @@ -11,9 +11,9 @@ proc-macro = true bench = false [dependencies] -proc-macro2 = "1.0.82" +proc-macro2 = "1.0.85" quote = "1.0.36" -syn = { version = "2.0.63", features = ["full", "extra-traits"] } +syn = { version = "2.0.66", features = ["full", "extra-traits"] } [lints] workspace = true diff --git a/src/rebase-snap/Cargo.toml b/src/rebase-snap/Cargo.toml index 73e6247f00f..05d9845849e 100644 --- a/src/rebase-snap/Cargo.toml +++ b/src/rebase-snap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rebase-snap" -version = "1.8.0-dev" +version = "1.9.0-dev" authors = ["Amazon Firecracker team "] edition = "2021" license = "Apache-2.0" @@ -11,9 +11,9 @@ bench = false [dependencies] displaydoc = "0.2.4" -libc = "0.2.154" +libc = "0.2.155" log-instrument = { path = "../log-instrument", optional = true } -thiserror = "1.0.60" +thiserror = "1.0.61" utils = { path = "../utils" } diff --git a/src/seccompiler/Cargo.toml b/src/seccompiler/Cargo.toml index 443bf3aad65..cbe54c877ce 100644 --- a/src/seccompiler/Cargo.toml +++ b/src/seccompiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "seccompiler" -version = "1.8.0-dev" +version = "1.9.0-dev" authors = ["Amazon Firecracker team "] edition = "2021" description = "Program that compiles multi-threaded seccomp-bpf filters expressed as JSON into raw BPF programs, serializing them and outputting them to a file." @@ -18,11 +18,11 @@ bench = false [dependencies] bincode = "1.2.1" displaydoc = "0.2.4" -libc = "0.2.154" +libc = "0.2.155" log-instrument = { path = "../log-instrument", optional = true } -serde = { version = "1.0.201", features = ["derive"] } +serde = { version = "1.0.203", features = ["derive"] } serde_json = "1.0.117" -thiserror = "1.0.60" +thiserror = "1.0.61" utils = { path = "../utils" } diff --git a/src/seccompiler/src/seccompiler_bin.rs b/src/seccompiler/src/seccompiler_bin.rs index 692ec5c8494..c6c97690bf6 100644 --- a/src/seccompiler/src/seccompiler_bin.rs +++ b/src/seccompiler/src/seccompiler_bin.rs @@ -112,16 +112,14 @@ fn build_arg_parser() -> ArgParser<'static> { } fn get_argument_values(arguments: &ArgumentsBag) -> Result { - let arch_string = arguments.single_value("target-arch"); - if arch_string.is_none() { + let Some(arch_string) = arguments.single_value("target-arch") else { return Err(SeccompError::MissingTargetArch); - } - let target_arch: TargetArch = arch_string.unwrap().as_str().try_into()?; + }; + let target_arch: TargetArch = arch_string.as_str().try_into()?; - let input_file = arguments.single_value("input-file"); - if input_file.is_none() { + let Some(input_file) = arguments.single_value("input-file") else { return Err(SeccompError::MissingInputFile); - } + }; let is_basic = arguments.flag_present("basic"); if is_basic { @@ -133,7 +131,7 @@ fn get_argument_values(arguments: &ArgumentsBag) -> Result"] edition = "2021" license = "Apache-2.0" @@ -14,10 +14,10 @@ clap = { version = "4.5.4", features = ["derive", "string"] } displaydoc = "0.2.4" fc_utils = { package = "utils", path = "../utils" } -libc = "0.2.154" +libc = "0.2.155" log-instrument = { path = "../log-instrument", optional = true } semver = "1.0.23" -thiserror = "1.0.60" +thiserror = "1.0.61" vmm = { path = "../vmm" } [target.'cfg(target_arch = "aarch64")'.dependencies] diff --git a/src/utils/Cargo.toml b/src/utils/Cargo.toml index 882deb548fe..27522103d7f 100644 --- a/src/utils/Cargo.toml +++ b/src/utils/Cargo.toml @@ -11,10 +11,10 @@ bench = false [dependencies] derive_more = { version = "0.99.17", default-features = false, features = ["from"] } displaydoc = "0.2.4" -libc = "0.2.154" +libc = "0.2.155" log-instrument = { path = "../log-instrument", optional = true } -serde = { version = "1.0.201", features = ["derive"] } -thiserror = "1.0.60" +serde = { version = "1.0.203", features = ["derive"] } +thiserror = "1.0.61" vm-memory = { version = "0.14.1", features = ["backend-mmap", "backend-bitmap"] } vmm-sys-util = { version = "0.12.1", features = ["with-serde"] } diff --git a/src/utils/src/net/mac.rs b/src/utils/src/net/mac.rs index d0f6e3057c4..3ffba73874d 100644 --- a/src/utils/src/net/mac.rs +++ b/src/utils/src/net/mac.rs @@ -22,6 +22,7 @@ pub const MAC_ADDR_LEN: u8 = 6; /// Represents a MAC address #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +#[repr(transparent)] /// Representation of a MAC address. pub struct MacAddr { bytes: [u8; MAC_ADDR_LEN as usize], diff --git a/src/vmm/Cargo.toml b/src/vmm/Cargo.toml index 636bef95ed0..4de8974420e 100644 --- a/src/vmm/Cargo.toml +++ b/src/vmm/Cargo.toml @@ -11,7 +11,7 @@ bench = false [dependencies] acpi_tables = { path = "../acpi-tables" } aes-gcm = { version = "0.10.1", default-features = false, features = ["aes"] } -aws-lc-rs = { version = "1.7.1", features = ["bindgen"] } +aws-lc-rs = { version = "1.7.2", features = ["bindgen"] } base64 = "0.22.1" bincode = "1.2.1" bitflags = "2.5.0" @@ -22,7 +22,7 @@ event-manager = "0.4.0" kvm-bindings = { version = "0.8.0", features = ["fam-wrappers", "serde"] } kvm-ioctls = "0.17.0" lazy_static = "1.4.0" -libc = "0.2.154" +libc = "0.2.155" linux-loader = "0.11.0" log = { version = "0.4.17", features = ["std", "serde"] } log-instrument = { path = "../log-instrument", optional = true } @@ -31,11 +31,11 @@ micro_http = { git = "https://github.com/firecracker-microvm/micro-http" } seccompiler = { path = "../seccompiler" } semver = { version = "1.0.23", features = ["serde"] } -serde = { version = "1.0.201", features = ["derive", "rc"] } +serde = { version = "1.0.203", features = ["derive", "rc"] } serde_json = "1.0.117" slab = "0.4.7" smallvec = "1.11.2" -thiserror = "1.0.60" +thiserror = "1.0.61" timerfd = "1.5.0" userfaultfd = "0.8.1" utils = { path = "../utils" } @@ -51,7 +51,7 @@ vm-fdt = "0.3.0" [dev-dependencies] criterion = { version = "0.5.0", default-features = false } device_tree = "1.1.0" -itertools = "0.12.0" +itertools = "0.13.0" proptest = { version = "1.0.0", default-features = false, features = ["std"] } [features] diff --git a/src/vmm/src/devices/virtio/balloon/device.rs b/src/vmm/src/devices/virtio/balloon/device.rs index 9a69312729f..77a13902cc2 100644 --- a/src/vmm/src/devices/virtio/balloon/device.rs +++ b/src/vmm/src/devices/virtio/balloon/device.rs @@ -1,11 +1,10 @@ // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use std::io::Write; +use std::fmt; use std::sync::atomic::AtomicU32; use std::sync::Arc; use std::time::Duration; -use std::{cmp, fmt}; use log::error; use serde::Serialize; @@ -593,20 +592,12 @@ impl VirtioDevice for Balloon { self.irq_trigger.irq_status.clone() } - fn read_config(&self, offset: u64, mut data: &mut [u8]) { - let config_space_bytes = self.config_space.as_slice(); - let config_len = config_space_bytes.len() as u64; - if offset >= config_len { + fn read_config(&self, offset: u64, data: &mut [u8]) { + if let Some(config_space_bytes) = self.config_space.as_slice().get(u64_to_usize(offset)..) { + let len = config_space_bytes.len().min(data.len()); + data[..len].copy_from_slice(&config_space_bytes[..len]); + } else { error!("Failed to read config space"); - return; - } - - if let Some(end) = offset.checked_add(data.len() as u64) { - // This write can't fail, offset and end are checked against config_len. - data.write_all( - &config_space_bytes[u64_to_usize(offset)..u64_to_usize(cmp::min(end, config_len))], - ) - .unwrap(); } } diff --git a/src/vmm/src/devices/virtio/block/vhost_user/device.rs b/src/vmm/src/devices/virtio/block/vhost_user/device.rs index a9cb8523f0f..2304262c26e 100644 --- a/src/vmm/src/devices/virtio/block/vhost_user/device.rs +++ b/src/vmm/src/devices/virtio/block/vhost_user/device.rs @@ -4,8 +4,6 @@ // Portions Copyright 2019 Intel Corporation. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use std::cmp; -use std::io::Write; use std::sync::atomic::AtomicU32; use std::sync::Arc; @@ -322,19 +320,13 @@ impl VirtioDevice for VhostUserBlock self.irq_trigger.irq_status.clone() } - fn read_config(&self, offset: u64, mut data: &mut [u8]) { - let config_len = self.config_space.len() as u64; - if offset >= config_len { + fn read_config(&self, offset: u64, data: &mut [u8]) { + if let Some(config_space_bytes) = self.config_space.as_slice().get(u64_to_usize(offset)..) { + let len = config_space_bytes.len().min(data.len()); + data[..len].copy_from_slice(&config_space_bytes[..len]); + } else { error!("Failed to read config space"); self.metrics.cfg_fails.inc(); - return; - } - if let Some(end) = offset.checked_add(data.len() as u64) { - // This write can't fail, offset and end are checked against config_len. - data.write_all( - &self.config_space[u64_to_usize(offset)..u64_to_usize(cmp::min(end, config_len))], - ) - .unwrap(); } } diff --git a/src/vmm/src/devices/virtio/block/virtio/metrics.rs b/src/vmm/src/devices/virtio/block/virtio/metrics.rs index 253cd9c561d..69a521fde54 100644 --- a/src/vmm/src/devices/virtio/block/virtio/metrics.rs +++ b/src/vmm/src/devices/virtio/block/virtio/metrics.rs @@ -100,20 +100,14 @@ impl BlockMetricsPerDevice { /// lock is always initialized so it is safe the unwrap /// the lock without a check. pub fn alloc(drive_id: String) -> Arc { - if METRICS.read().unwrap().metrics.get(&drive_id).is_none() { + Arc::clone( METRICS .write() .unwrap() .metrics - .insert(drive_id.clone(), Arc::new(BlockDeviceMetrics::new())); - } - METRICS - .read() - .unwrap() - .metrics - .get(&drive_id) - .unwrap() - .clone() + .entry(drive_id) + .or_insert_with(|| Arc::new(BlockDeviceMetrics::default())), + ) } } diff --git a/src/vmm/src/devices/virtio/iovec.rs b/src/vmm/src/devices/virtio/iovec.rs index ff992ab77f4..78e4e26aeb5 100644 --- a/src/vmm/src/devices/virtio/iovec.rs +++ b/src/vmm/src/devices/virtio/iovec.rs @@ -18,6 +18,8 @@ pub enum IoVecError { WriteOnlyDescriptor, /// Tried to create an 'IoVecMut` from a read-only descriptor chain ReadOnlyDescriptor, + /// Tried to create an `IoVec` or `IoVecMut` from a descriptor chain that was too large + OverflowedDescriptor, /// Guest memory error: {0} GuestMemory(#[from] GuestMemoryError), } @@ -40,14 +42,14 @@ pub struct IoVecBuffer { // container of the memory regions included in this IO vector vecs: IoVecVec, // Total length of the IoVecBuffer - len: usize, + len: u32, } impl IoVecBuffer { /// Create an `IoVecBuffer` from a `DescriptorChain` pub fn from_descriptor_chain(head: DescriptorChain) -> Result { let mut vecs = IoVecVec::new(); - let mut len = 0usize; + let mut len = 0u32; let mut next_descriptor = Some(head); while let Some(desc) = next_descriptor { @@ -68,7 +70,9 @@ impl IoVecBuffer { iov_base, iov_len: desc.len as size_t, }); - len += desc.len as usize; + len = len + .checked_add(desc.len) + .ok_or(IoVecError::OverflowedDescriptor)?; next_descriptor = desc.next_descriptor(); } @@ -77,7 +81,7 @@ impl IoVecBuffer { } /// Get the total length of the memory regions covered by this `IoVecBuffer` - pub(crate) fn len(&self) -> usize { + pub(crate) fn len(&self) -> u32 { self.len } @@ -106,7 +110,7 @@ impl IoVecBuffer { mut buf: &mut [u8], offset: usize, ) -> Result<(), VolatileMemoryError> { - if offset < self.len() { + if offset < self.len() as usize { let expected = buf.len(); let bytes_read = self.read_volatile_at(&mut buf, offset, expected)?; @@ -188,14 +192,14 @@ pub struct IoVecBufferMut { // container of the memory regions included in this IO vector vecs: IoVecVec, // Total length of the IoVecBufferMut - len: usize, + len: u32, } impl IoVecBufferMut { /// Create an `IoVecBufferMut` from a `DescriptorChain` pub fn from_descriptor_chain(head: DescriptorChain) -> Result { let mut vecs = IoVecVec::new(); - let mut len = 0usize; + let mut len = 0u32; for desc in head { if !desc.is_write_only() { @@ -217,14 +221,16 @@ impl IoVecBufferMut { iov_base, iov_len: desc.len as size_t, }); - len += desc.len as usize; + len = len + .checked_add(desc.len) + .ok_or(IoVecError::OverflowedDescriptor)?; } Ok(Self { vecs, len }) } /// Get the total length of the memory regions covered by this `IoVecBuffer` - pub(crate) fn len(&self) -> usize { + pub(crate) fn len(&self) -> u32 { self.len } @@ -244,7 +250,7 @@ impl IoVecBufferMut { mut buf: &[u8], offset: usize, ) -> Result<(), VolatileMemoryError> { - if offset < self.len() { + if offset < self.len() as usize { let expected = buf.len(); let bytes_written = self.write_volatile_at(&mut buf, offset, expected)?; @@ -335,18 +341,18 @@ mod tests { iov_len: buf.len(), }] .into(), - len: buf.len(), + len: buf.len().try_into().unwrap(), } } } impl<'a> From> for IoVecBuffer { fn from(buffer: Vec<&'a [u8]>) -> Self { - let mut len = 0; + let mut len = 0_u32; let vecs = buffer .into_iter() .map(|slice| { - len += slice.len(); + len += TryInto::::try_into(slice.len()).unwrap(); iovec { iov_base: slice.as_ptr() as *mut c_void, iov_len: slice.len(), @@ -366,7 +372,7 @@ mod tests { iov_len: buf.len(), }] .into(), - len: buf.len(), + len: buf.len().try_into().unwrap(), } } } @@ -607,7 +613,6 @@ mod verification { use libc::{c_void, iovec}; use vm_memory::bitmap::BitmapSlice; - use vm_memory::volatile_memory::Error; use vm_memory::VolatileSlice; use super::{IoVecBuffer, IoVecBufferMut, IoVecVec}; @@ -622,10 +627,10 @@ mod verification { // >= 1. const MAX_DESC_LENGTH: usize = 4; - fn create_iovecs(mem: *mut u8, size: usize) -> (IoVecVec, usize) { + fn create_iovecs(mem: *mut u8, size: usize) -> (IoVecVec, u32) { let nr_descs: usize = kani::any_where(|&n| n <= MAX_DESC_LENGTH); let mut vecs: Vec = Vec::with_capacity(nr_descs); - let mut len = 0usize; + let mut len = 0u32; for _ in 0..nr_descs { // The `IoVecBuffer(Mut)` constructors ensure that the memory region described by every // `Descriptor` in the chain is a valid, i.e. it is memory with then guest's memory @@ -637,7 +642,7 @@ mod verification { let iov_base = unsafe { mem.offset(addr.try_into().unwrap()) } as *mut c_void; vecs.push(iovec { iov_base, iov_len }); - len += iov_len; + len += u32::try_from(iov_len).unwrap(); } (vecs, len) @@ -712,7 +717,7 @@ mod verification { let iov: IoVecBuffer = kani::any(); let mut buf = vec![0; GUEST_MEMORY_SIZE]; - let offset: usize = kani::any(); + let offset: u32 = kani::any(); // We can't really check the contents that the operation here writes into `buf`, because // our `IoVecBuffer` being completely arbitrary can contain overlapping memory regions, so @@ -724,9 +729,13 @@ mod verification { // Furthermore, we know our Read-/WriteVolatile implementation above is infallible, so // provided that the logic inside read_volatile_at is correct, we should always get Ok(...) assert_eq!( - iov.read_volatile_at(&mut KaniBuffer(&mut buf), offset, GUEST_MEMORY_SIZE) - .unwrap(), - buf.len().min(iov.len().saturating_sub(offset)) + iov.read_volatile_at( + &mut KaniBuffer(&mut buf), + offset as usize, + GUEST_MEMORY_SIZE + ) + .unwrap(), + buf.len().min(iov.len().saturating_sub(offset) as usize) ); } @@ -737,7 +746,7 @@ mod verification { let mut iov_mut: IoVecBufferMut = kani::any(); let mut buf = kani::vec::any_vec::(); - let offset: usize = kani::any(); + let offset: u32 = kani::any(); // We can't really check the contents that the operation here writes into `IoVecBufferMut`, // because our `IoVecBufferMut` being completely arbitrary can contain overlapping memory @@ -750,9 +759,13 @@ mod verification { // provided that the logic inside write_volatile_at is correct, we should always get Ok(...) assert_eq!( iov_mut - .write_volatile_at(&mut KaniBuffer(&mut buf), offset, GUEST_MEMORY_SIZE) + .write_volatile_at( + &mut KaniBuffer(&mut buf), + offset as usize, + GUEST_MEMORY_SIZE + ) .unwrap(), - buf.len().min(iov_mut.len().saturating_sub(offset)) + buf.len().min(iov_mut.len().saturating_sub(offset) as usize) ); } } diff --git a/src/vmm/src/devices/virtio/net/device.rs b/src/vmm/src/devices/virtio/net/device.rs index 0e7a77aadb6..f79692012e5 100755 --- a/src/vmm/src/devices/virtio/net/device.rs +++ b/src/vmm/src/devices/virtio/net/device.rs @@ -7,11 +7,10 @@ #[cfg(not(test))] use std::io::Read; -use std::io::Write; +use std::mem; use std::net::Ipv4Addr; use std::sync::atomic::AtomicU32; use std::sync::{Arc, Mutex}; -use std::{cmp, mem}; use libc::EAGAIN; use log::{error, warn}; @@ -96,11 +95,12 @@ fn init_vnet_hdr(buf: &mut [u8]) { } #[derive(Debug, Default, Clone, Copy)] +#[repr(C)] pub struct ConfigSpace { pub guest_mac: MacAddr, } -// SAFETY: `ConfigSpace` contains only PODs. +// SAFETY: `ConfigSpace` contains only PODs in `repr(C)` or `repr(transparent)`, without padding. unsafe impl ByteValued for ConfigSpace {} /// VirtIO network device. @@ -462,7 +462,7 @@ impl Net { if let Some(ns) = mmds_ns { if ns.is_mmds_frame(headers) { - let mut frame = vec![0u8; frame_iovec.len() - vnet_hdr_len()]; + let mut frame = vec![0u8; frame_iovec.len() as usize - vnet_hdr_len()]; // Ok to unwrap here, because we are passing a buffer that has the exact size // of the `IoVecBuffer` minus the VNET headers. frame_iovec @@ -472,7 +472,7 @@ impl Net { METRICS.mmds.rx_accepted.inc(); // MMDS frames are not accounted by the rate limiter. - Self::rate_limiter_replenish_op(rate_limiter, frame_iovec.len() as u64); + Self::rate_limiter_replenish_op(rate_limiter, u64::from(frame_iovec.len())); // MMDS consumed the frame. return Ok(true); @@ -493,7 +493,7 @@ impl Net { let _metric = net_metrics.tap_write_agg.record_latency_metrics(); match Self::write_tap(tap, frame_iovec) { Ok(_) => { - let len = frame_iovec.len() as u64; + let len = u64::from(frame_iovec.len()); net_metrics.tx_bytes_count.add(len); net_metrics.tx_packets_count.inc(); net_metrics.tx_count.inc(); @@ -609,7 +609,7 @@ impl Net { }; // We only handle frames that are up to MAX_BUFFER_SIZE - if buffer.len() > MAX_BUFFER_SIZE { + if buffer.len() as usize > MAX_BUFFER_SIZE { error!("net: received too big frame from driver"); self.metrics.tx_malformed_frames.inc(); tx_queue @@ -618,7 +618,7 @@ impl Net { continue; } - if !Self::rate_limiter_consume_op(&mut self.tx_rate_limiter, buffer.len() as u64) { + if !Self::rate_limiter_consume_op(&mut self.tx_rate_limiter, u64::from(buffer.len())) { tx_queue.undo_pop(); self.metrics.tx_rate_limiter_throttled.inc(); break; @@ -831,20 +831,13 @@ impl VirtioDevice for Net { self.irq_trigger.irq_status.clone() } - fn read_config(&self, offset: u64, mut data: &mut [u8]) { - let config_space_bytes = self.config_space.as_slice(); - let config_len = config_space_bytes.len() as u64; - if offset >= config_len { + fn read_config(&self, offset: u64, data: &mut [u8]) { + if let Some(config_space_bytes) = self.config_space.as_slice().get(u64_to_usize(offset)..) { + let len = config_space_bytes.len().min(data.len()); + data[..len].copy_from_slice(&config_space_bytes[..len]); + } else { error!("Failed to read config space"); self.metrics.cfg_fails.inc(); - return; - } - if let Some(end) = offset.checked_add(data.len() as u64) { - // This write can't fail, offset and end are checked against config_len. - data.write_all( - &config_space_bytes[u64_to_usize(offset)..u64_to_usize(cmp::min(end, config_len))], - ) - .unwrap(); } } diff --git a/src/vmm/src/devices/virtio/net/metrics.rs b/src/vmm/src/devices/virtio/net/metrics.rs index 135fe1c8639..684a248cdc0 100644 --- a/src/vmm/src/devices/virtio/net/metrics.rs +++ b/src/vmm/src/devices/virtio/net/metrics.rs @@ -102,20 +102,14 @@ impl NetMetricsPerDevice { /// lock is always initialized so it is safe the unwrap /// the lock without a check. pub fn alloc(iface_id: String) -> Arc { - if METRICS.read().unwrap().metrics.get(&iface_id).is_none() { + Arc::clone( METRICS .write() .unwrap() .metrics - .insert(iface_id.clone(), Arc::new(NetDeviceMetrics::new())); - } - METRICS - .read() - .unwrap() - .metrics - .get(&iface_id) - .unwrap() - .clone() + .entry(iface_id) + .or_insert_with(|| Arc::new(NetDeviceMetrics::default())), + ) } } diff --git a/src/vmm/src/devices/virtio/net/tap.rs b/src/vmm/src/devices/virtio/net/tap.rs index 5c8151f27ea..9da0cbf5785 100644 --- a/src/vmm/src/devices/virtio/net/tap.rs +++ b/src/vmm/src/devices/virtio/net/tap.rs @@ -363,7 +363,7 @@ pub mod tests { tap.write_iovec(&scattered).unwrap(); - let mut read_buf = vec![0u8; scattered.len()]; + let mut read_buf = vec![0u8; scattered.len() as usize]; assert!(tap_traffic_simulator.pop_rx_packet(&mut read_buf)); assert_eq!( &read_buf[..PAYLOAD_SIZE - VNET_HDR_SIZE], diff --git a/src/vmm/src/devices/virtio/rng/device.rs b/src/vmm/src/devices/virtio/rng/device.rs index 7ef96bd30f5..512bf66c1d3 100644 --- a/src/vmm/src/devices/virtio/rng/device.rs +++ b/src/vmm/src/devices/virtio/rng/device.rs @@ -112,7 +112,7 @@ impl Entropy { return Ok(0); } - let mut rand_bytes = vec![0; iovec.len()]; + let mut rand_bytes = vec![0; iovec.len() as usize]; rand::fill(&mut rand_bytes).map_err(|err| { METRICS.host_rng_fails.inc(); err @@ -120,7 +120,7 @@ impl Entropy { // It is ok to unwrap here. We are writing `iovec.len()` bytes at offset 0. iovec.write_all_volatile_at(&rand_bytes, 0).unwrap(); - Ok(iovec.len().try_into().unwrap()) + Ok(iovec.len()) } fn process_entropy_queue(&mut self) { @@ -142,7 +142,7 @@ impl Entropy { // Check for available rate limiting budget. // If not enough budget is available, leave the request descriptor in the queue // to handle once we do have budget. - if !Self::rate_limit_request(&mut self.rate_limiter, iovec.len() as u64) { + if !Self::rate_limit_request(&mut self.rate_limiter, u64::from(iovec.len())) { debug!("entropy: throttling entropy queue"); METRICS.entropy_rate_limiter_throttled.inc(); self.queues[RNG_QUEUE].undo_pop(); diff --git a/src/vmm/src/devices/virtio/vhost_user_metrics.rs b/src/vmm/src/devices/virtio/vhost_user_metrics.rs index ed64be31d59..87c86e847fb 100644 --- a/src/vmm/src/devices/virtio/vhost_user_metrics.rs +++ b/src/vmm/src/devices/virtio/vhost_user_metrics.rs @@ -95,19 +95,14 @@ impl VhostUserMetricsPerDevice { /// lock is always initialized so it is safe the unwrap /// the lock without a check. pub fn alloc(drive_id: String) -> Arc { - if METRICS.read().unwrap().metrics.get(&drive_id).is_none() { - METRICS.write().unwrap().metrics.insert( - drive_id.clone(), - Arc::new(VhostUserDeviceMetrics::default()), - ); - } - METRICS - .read() - .unwrap() - .metrics - .get(&drive_id) - .unwrap() - .clone() + Arc::clone( + METRICS + .write() + .unwrap() + .metrics + .entry(drive_id) + .or_insert_with(|| Arc::new(VhostUserDeviceMetrics::default())), + ) } } diff --git a/src/vmm/src/devices/virtio/vsock/mod.rs b/src/vmm/src/devices/virtio/vsock/mod.rs index 30edc9addd5..a87b6b488d8 100644 --- a/src/vmm/src/devices/virtio/vsock/mod.rs +++ b/src/vmm/src/devices/virtio/vsock/mod.rs @@ -110,7 +110,7 @@ mod defs { #[rustfmt::skip] pub enum VsockError { /** The total length of the descriptor chain ({0}) is too short to hold a packet of length {1} + header */ - DescChainTooShortForPacket(usize, u32), + DescChainTooShortForPacket(u32, u32), /// Empty queue EmptyQueue, /// EventFd error: {0} @@ -122,6 +122,8 @@ pub enum VsockError { /** The total length of the descriptor chain ({0}) is less than the number of bytes required\ to hold a vsock packet header.*/ DescChainTooShortForHeader(usize), + /// The descriptor chain length was greater than the max ([u32::MAX]) + DescChainOverflow, /// The vsock header `len` field holds an invalid value: {0} InvalidPktLen(u32), /// A data fetch was attempted when no data was available. @@ -144,6 +146,7 @@ impl From for VsockError { IoVecError::WriteOnlyDescriptor => VsockError::UnreadableDescriptor, IoVecError::ReadOnlyDescriptor => VsockError::UnwritableDescriptor, IoVecError::GuestMemory(err) => VsockError::GuestMemoryMmap(err), + IoVecError::OverflowedDescriptor => VsockError::DescChainOverflow, } } } diff --git a/src/vmm/src/devices/virtio/vsock/packet.rs b/src/vmm/src/devices/virtio/vsock/packet.rs index 7985be15485..ba82b169b3c 100644 --- a/src/vmm/src/devices/virtio/vsock/packet.rs +++ b/src/vmm/src/devices/virtio/vsock/packet.rs @@ -139,7 +139,7 @@ impl VsockPacket { return Err(VsockError::InvalidPktLen(hdr.len)); } - if (hdr.len as usize) > buffer.len() - VSOCK_PKT_HDR_SIZE as usize { + if hdr.len > buffer.len() - VSOCK_PKT_HDR_SIZE { return Err(VsockError::DescChainTooShortForPacket( buffer.len(), hdr.len, @@ -160,8 +160,8 @@ impl VsockPacket { pub fn from_rx_virtq_head(chain: DescriptorChain) -> Result { let buffer = IoVecBufferMut::from_descriptor_chain(chain)?; - if buffer.len() < VSOCK_PKT_HDR_SIZE as usize { - return Err(VsockError::DescChainTooShortForHeader(buffer.len())); + if buffer.len() < VSOCK_PKT_HDR_SIZE { + return Err(VsockError::DescChainTooShortForHeader(buffer.len() as usize)); } Ok(Self { @@ -212,7 +212,7 @@ impl VsockPacket { VsockPacketBuffer::Tx(ref iovec_buf) => iovec_buf.len(), VsockPacketBuffer::Rx(ref iovec_buf) => iovec_buf.len(), }; - chain_length - VSOCK_PKT_HDR_SIZE as usize + (chain_length - VSOCK_PKT_HDR_SIZE) as usize } pub fn read_at_offset_from( @@ -225,8 +225,7 @@ impl VsockPacket { VsockPacketBuffer::Tx(_) => Err(VsockError::UnwritableDescriptor), VsockPacketBuffer::Rx(ref mut buffer) => { if count - > buffer - .len() + > (buffer.len() as usize) .saturating_sub(VSOCK_PKT_HDR_SIZE as usize) .saturating_sub(offset) { @@ -249,8 +248,7 @@ impl VsockPacket { match self.buffer { VsockPacketBuffer::Tx(ref buffer) => { if count - > buffer - .len() + > (buffer.len() as usize) .saturating_sub(VSOCK_PKT_HDR_SIZE as usize) .saturating_sub(offset) { @@ -427,9 +425,10 @@ mod tests { .unwrap(), ) .unwrap(); + assert_eq!( - pkt.buf_size(), - handler_ctx.guest_txvq.dtable[1].len.get() as usize + TryInto::::try_into(pkt.buf_size()).unwrap(), + handler_ctx.guest_txvq.dtable[1].len.get() ); } diff --git a/src/vmm/src/devices/virtio/vsock/unix/muxer_killq.rs b/src/vmm/src/devices/virtio/vsock/unix/muxer_killq.rs index c7b5b66c58f..360dfdf49e9 100644 --- a/src/vmm/src/devices/virtio/vsock/unix/muxer_killq.rs +++ b/src/vmm/src/devices/virtio/vsock/unix/muxer_killq.rs @@ -106,7 +106,7 @@ impl MuxerKillQ { pub fn pop(&mut self) -> Option { if let Some(item) = self.q.front() { if Instant::now() > item.kill_time { - return Some(self.q.pop_front().unwrap().key); + return self.q.pop_front().map(|entry| entry.key); } } None diff --git a/src/vmm/src/dumbo/tcp/connection.rs b/src/vmm/src/dumbo/tcp/connection.rs index 1ca8042f8d2..5fbcd089067 100644 --- a/src/vmm/src/dumbo/tcp/connection.rs +++ b/src/vmm/src/dumbo/tcp/connection.rs @@ -714,13 +714,9 @@ impl Connection { // We check this here because if a valid payload has been received, then we must have // set enqueue_ack = true earlier. - if payload_len > 0 { + if let Some(payload_len) = NonZeroUsize::new(payload_len.into()) { buf[..payload_len.into()].copy_from_slice(s.payload()); - // The unwrap is safe because payload_len > 0. - return Ok(( - Some(NonZeroUsize::new(payload_len.into()).unwrap()), - recv_status_flags, - )); + return Ok((Some(payload_len), recv_status_flags)); } } diff --git a/src/vmm/src/vmm_config/net.rs b/src/vmm/src/vmm_config/net.rs index c4e835e1ad9..5782face4b5 100644 --- a/src/vmm/src/vmm_config/net.rs +++ b/src/vmm/src/vmm_config/net.rs @@ -109,20 +109,20 @@ impl NetBuilder { &mut self, netif_config: NetworkInterfaceConfig, ) -> Result>, NetworkInterfaceError> { - let mac_conflict = |net: &Arc>| { - let net = net.lock().expect("Poisoned lock"); - // Check if another net dev has same MAC. - netif_config.guest_mac.is_some() - && netif_config.guest_mac.as_ref() == net.guest_mac() - && &netif_config.iface_id != net.id() - }; - // Validate there is no Mac conflict. - // No need to validate host_dev_name conflict. In such a case, - // an error will be thrown during device creation anyway. - if self.net_devices.iter().any(mac_conflict) { - return Err(NetworkInterfaceError::GuestMacAddressInUse( - netif_config.guest_mac.unwrap().to_string(), - )); + if let Some(ref mac_address) = netif_config.guest_mac { + let mac_conflict = |net: &Arc>| { + let net = net.lock().expect("Poisoned lock"); + // Check if another net dev has same MAC. + Some(mac_address) == net.guest_mac() && &netif_config.iface_id != net.id() + }; + // Validate there is no Mac conflict. + // No need to validate host_dev_name conflict. In such a case, + // an error will be thrown during device creation anyway. + if self.net_devices.iter().any(mac_conflict) { + return Err(NetworkInterfaceError::GuestMacAddressInUse( + mac_address.to_string(), + )); + } } // If this is an update, just remove the old one. diff --git a/src/vmm/src/vstate/vcpu/x86_64.rs b/src/vmm/src/vstate/vcpu/x86_64.rs index b4542c1c598..67dc8681f25 100644 --- a/src/vmm/src/vstate/vcpu/x86_64.rs +++ b/src/vmm/src/vstate/vcpu/x86_64.rs @@ -19,6 +19,7 @@ use serde::{Deserialize, Serialize}; use crate::arch::x86_64::interrupts; use crate::arch::x86_64::msr::{create_boot_msr_entries, MsrError}; use crate::arch::x86_64::regs::{SetupFpuError, SetupRegistersError, SetupSpecialRegistersError}; +use crate::arch_gen::x86::msr_index::{MSR_IA32_TSC, MSR_IA32_TSC_DEADLINE}; use crate::cpu_config::x86_64::{cpuid, CpuConfiguration}; use crate::logger::{IncMetric, METRICS}; use crate::vstate::memory::{Address, GuestAddress, GuestMemoryMmap}; @@ -282,6 +283,39 @@ impl KvmVcpu { Ok(cpuid) } + /// If the IA32_TSC_DEADLINE MSR value is zero, update it + /// with the IA32_TSC value to guarantee that + /// the vCPU will continue receiving interrupts after restoring from a snapshot. + /// + /// Rationale: we observed that sometimes when taking a snapshot, + /// the IA32_TSC_DEADLINE MSR is cleared, but the interrupt is not + /// delivered to the guest, leading to a situation where one + /// of the vCPUs never receives TSC interrupts after restoring, + /// until the MSR is updated externally, eg by setting the system time. + fn fix_zero_tsc_deadline_msr(msr_chunks: &mut [Msrs]) { + // We do not expect more than 1 TSC MSR entry, but if there are multiple, pick the maximum. + let max_tsc_value = msr_chunks + .iter() + .flat_map(|msrs| msrs.as_slice()) + .filter(|msr| msr.index == MSR_IA32_TSC) + .map(|msr| msr.data) + .max(); + + if let Some(tsc_value) = max_tsc_value { + msr_chunks + .iter_mut() + .flat_map(|msrs| msrs.as_mut_slice()) + .filter(|msr| msr.index == MSR_IA32_TSC_DEADLINE && msr.data == 0) + .for_each(|msr| { + warn!( + "MSR_IA32_TSC_DEADLINE is 0, replacing with {:x}.", + tsc_value + ); + msr.data = tsc_value; + }); + } + } + /// Get MSR chunks for the given MSR index list. /// /// KVM only supports getting `KVM_MAX_MSR_ENTRIES` at a time, so we divide @@ -321,6 +355,8 @@ impl KvmVcpu { msr_chunks.push(msrs); } + Self::fix_zero_tsc_deadline_msr(&mut msr_chunks); + Ok(msr_chunks) } @@ -594,6 +630,7 @@ mod tests { use std::os::unix::io::AsRawFd; + use kvm_bindings::kvm_msr_entry; use kvm_ioctls::Cap; use super::*; @@ -949,4 +986,77 @@ mod tests { } } } + + fn msrs_from_entries(msr_entries: &[(u32, u64)]) -> Msrs { + Msrs::from_entries( + &msr_entries + .iter() + .map(|&(index, data)| kvm_msr_entry { + index, + data, + ..Default::default() + }) + .collect::>(), + ) + .unwrap() + } + + fn assert_msrs(msr_chunks: &[Msrs], expected_msr_entries: &[(u32, u64)]) { + let flattened_msrs = msr_chunks.iter().flat_map(|msrs| msrs.as_slice()); + for (a, b) in flattened_msrs.zip(expected_msr_entries.iter()) { + assert_eq!(a.index, b.0); + assert_eq!(a.data, b.1); + } + } + + #[test] + fn test_fix_zero_tsc_deadline_msr_zero_same_chunk() { + // Place both TSC and TSC_DEADLINE MSRs in the same chunk. + let mut msr_chunks = [msrs_from_entries(&[ + (MSR_IA32_TSC_DEADLINE, 0), + (MSR_IA32_TSC, 42), + ])]; + + KvmVcpu::fix_zero_tsc_deadline_msr(&mut msr_chunks); + + // We expect for the MSR_IA32_TSC_DEADLINE to get updated with the MSR_IA32_TSC value. + assert_msrs( + &msr_chunks, + &[(MSR_IA32_TSC_DEADLINE, 42), (MSR_IA32_TSC, 42)], + ); + } + + #[test] + fn test_fix_zero_tsc_deadline_msr_zero_separate_chunks() { + // Place both TSC and TSC_DEADLINE MSRs in separate chunks. + let mut msr_chunks = [ + msrs_from_entries(&[(MSR_IA32_TSC_DEADLINE, 0)]), + msrs_from_entries(&[(MSR_IA32_TSC, 42)]), + ]; + + KvmVcpu::fix_zero_tsc_deadline_msr(&mut msr_chunks); + + // We expect for the MSR_IA32_TSC_DEADLINE to get updated with the MSR_IA32_TSC value. + assert_msrs( + &msr_chunks, + &[(MSR_IA32_TSC_DEADLINE, 42), (MSR_IA32_TSC, 42)], + ); + } + + #[test] + fn test_fix_zero_tsc_deadline_msr_non_zero() { + let mut msr_chunks = [msrs_from_entries(&[ + (MSR_IA32_TSC_DEADLINE, 1), + (MSR_IA32_TSC, 2), + ])]; + + KvmVcpu::fix_zero_tsc_deadline_msr(&mut msr_chunks); + + // We expect that MSR_IA32_TSC_DEADLINE should remain unchanged, because it is non-zero + // already. + assert_msrs( + &msr_chunks, + &[(MSR_IA32_TSC_DEADLINE, 1), (MSR_IA32_TSC, 2)], + ); + } } diff --git a/src/vmm/src/vstate/vm.rs b/src/vmm/src/vstate/vm.rs index 1e0e451e058..55b0ec146e0 100644 --- a/src/vmm/src/vstate/vm.rs +++ b/src/vmm/src/vstate/vm.rs @@ -235,10 +235,10 @@ impl Vm { } guest_mem .iter() - .enumerate() - .try_for_each(|(index, region)| { + .zip(0u32..) + .try_for_each(|(region, slot)| { let memory_region = kvm_userspace_memory_region { - slot: u32::try_from(index).unwrap(), + slot, guest_phys_addr: region.start_addr().raw_value(), memory_size: region.len(), // It's safe to unwrap because the guest address is valid. diff --git a/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_4.14host.json b/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_4.14host.json index 5c0805211de..e1252ee67a7 100644 --- a/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_4.14host.json +++ b/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_4.14host.json @@ -1276,10 +1276,6 @@ "addr": "0x277", "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" }, - { - "addr": "0x6e0", - "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" - }, { "addr": "0x4b564d00", "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" diff --git a/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_5.10host.json b/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_5.10host.json index feafc6061ba..5501ca88ce4 100644 --- a/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_5.10host.json +++ b/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_5.10host.json @@ -1426,10 +1426,6 @@ "addr": "0x277", "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" }, - { - "addr": "0x6e0", - "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" - }, { "addr": "0x4b564d00", "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" diff --git a/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_6.1host.json b/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_6.1host.json index 17520de539a..e0330895de7 100644 --- a/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_6.1host.json +++ b/tests/data/cpu_template_helper/fingerprint_AMD_MILAN_6.1host.json @@ -1472,10 +1472,6 @@ "addr": "0x277", "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" }, - { - "addr": "0x6e0", - "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" - }, { "addr": "0x4b564d00", "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_4.14host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_4.14host.json index 65faa69f424..bb9ce77cb16 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_4.14host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_4.14host.json @@ -962,10 +962,6 @@ "addr": "0x277", "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" }, - { - "addr": "0x6e0", - "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" - }, { "addr": "0xd90", "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_5.10host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_5.10host.json index 31f8dad2924..8568992dcef 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_5.10host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_5.10host.json @@ -1158,10 +1158,6 @@ "addr": "0x277", "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" }, - { - "addr": "0x6e0", - "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" - }, { "addr": "0xd90", "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_6.1host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_6.1host.json index d19fc9254bf..fc9ee8dddf8 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_6.1host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_CASCADELAKE_6.1host.json @@ -1158,10 +1158,6 @@ "addr": "0x277", "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" }, - { - "addr": "0x6e0", - "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" - }, { "addr": "0xd90", "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_4.14host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_4.14host.json index 3c0c51dd927..61c717defd6 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_4.14host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_4.14host.json @@ -916,10 +916,6 @@ "addr": "0x277", "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" }, - { - "addr": "0x6e0", - "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" - }, { "addr": "0x4b564d00", "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_5.10host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_5.10host.json index 863ffd2851d..3a6c8d5123e 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_5.10host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_5.10host.json @@ -1250,10 +1250,6 @@ "addr": "0x277", "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" }, - { - "addr": "0x6e0", - "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" - }, { "addr": "0x4b564d00", "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_6.1host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_6.1host.json index c5fd825edd0..d307c491d2a 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_6.1host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_ICELAKE_6.1host.json @@ -1273,10 +1273,6 @@ "addr": "0x277", "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" }, - { - "addr": "0x6e0", - "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" - }, { "addr": "0x4b564d00", "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_4.14host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_4.14host.json index 1714ac56edd..35c73d0e8af 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_4.14host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_4.14host.json @@ -962,10 +962,6 @@ "addr": "0x277", "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" }, - { - "addr": "0x6e0", - "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" - }, { "addr": "0xd90", "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_5.10host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_5.10host.json index 1681b9f7d24..c80924adefa 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_5.10host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_5.10host.json @@ -1158,10 +1158,6 @@ "addr": "0x277", "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" }, - { - "addr": "0x6e0", - "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" - }, { "addr": "0xd90", "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" diff --git a/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_6.1host.json b/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_6.1host.json index 6fc76c794e1..324619e3268 100644 --- a/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_6.1host.json +++ b/tests/data/cpu_template_helper/fingerprint_INTEL_SKYLAKE_6.1host.json @@ -1158,10 +1158,6 @@ "addr": "0x277", "bitmap": "0b0000000000000111000001000000011000000000000001110000010000000110" }, - { - "addr": "0x6e0", - "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" - }, { "addr": "0xd90", "bitmap": "0b0000000000000000000000000000000000000000000000000000000000000000" diff --git a/tests/integration_tests/functional/test_snapshot_restore_cross_kernel.py b/tests/integration_tests/functional/test_snapshot_restore_cross_kernel.py index d5535a27a33..9a3b02f481f 100644 --- a/tests/integration_tests/functional/test_snapshot_restore_cross_kernel.py +++ b/tests/integration_tests/functional/test_snapshot_restore_cross_kernel.py @@ -5,6 +5,7 @@ import json import logging +import platform from pathlib import Path import pytest @@ -16,13 +17,15 @@ guest_run_fio_iteration, populate_data_store, ) -from framework.utils_cpuid import CpuVendor, get_cpu_vendor +from framework.utils_cpu_templates import get_supported_cpu_templates from framework.utils_vsock import check_vsock_device from integration_tests.functional.test_balloon import ( get_stable_rss_mem_by_pid, make_guest_dirty_memory, ) +pytestmark = pytest.mark.nonci + def _test_balloon(microvm): # Get the firecracker pid. @@ -65,17 +68,27 @@ def _test_mmds(vm, mmds_net_iface): cmd = generate_mmds_get_request(mmds_ipv4_address, token=token) _, stdout, _ = vm.ssh.run(cmd) - assert json.load(stdout) == data_store + assert json.loads(stdout) == data_store + + +def get_snapshot_dirs(): + """Get all the snapshot directories""" + snapshot_root_name = "snapshot_artifacts" + snapshot_root_dir = Path(FC_WORKSPACE_DIR) / snapshot_root_name + cpu_templates = [] + if platform.machine() == "x86_64": + cpu_templates = ["None"] + cpu_templates += get_supported_cpu_templates() + for cpu_template in cpu_templates: + for snapshot_dir in snapshot_root_dir.glob(f"*_{cpu_template}_guest_snapshot"): + assert snapshot_dir.is_dir() + yield pytest.param(snapshot_dir, id=snapshot_dir.name) @pytest.mark.timeout(600) -@pytest.mark.nonci -@pytest.mark.parametrize( - "cpu_template", - ["C3", "T2", "T2S", "None"] if get_cpu_vendor() == CpuVendor.INTEL else ["None"], -) +@pytest.mark.parametrize("snapshot_dir", get_snapshot_dirs()) def test_snap_restore_from_artifacts( - microvm_factory, bin_vsock_path, test_fc_session_root_path, cpu_template + microvm_factory, bin_vsock_path, test_fc_session_root_path, snapshot_dir ): """ Restore from snapshots obtained with all supported guest kernel versions. @@ -87,43 +100,38 @@ def test_snap_restore_from_artifacts( """ logger = logging.getLogger("cross_kernel_snapshot_restore") - snapshot_root_name = "snapshot_artifacts" - snapshot_root_dir = Path(FC_WORKSPACE_DIR) / snapshot_root_name - # Iterate through all subdirectories based on CPU template # in the snapshot root dir. - snap_subdirs = snapshot_root_dir.glob(f".*_{cpu_template}_guest_snapshot") - for snapshot_dir in snap_subdirs: - assert snapshot_dir.is_dir() - logger.info("Working with snapshot artifacts in %s.", snapshot_dir) + logger.info("Working with snapshot artifacts in %s.", snapshot_dir) - vm = microvm_factory.build() - vm.spawn() - logger.info("Loading microVM from snapshot...") - vm.restore_from_path(snapshot_dir) - vm.resume() + vm = microvm_factory.build() + vm.time_api_requests = False + vm.spawn() + logger.info("Loading microVM from snapshot...") + vm.restore_from_path(snapshot_dir) + vm.resume() - # Ensure microVM is running. - assert vm.state == "Running" + # Ensure microVM is running. + assert vm.state == "Running" - # Test that net devices have connectivity after restore. - for idx, iface in enumerate(vm.iface.values()["iface"]): - logger.info("Testing net device %s...", iface.dev_name) - exit_code, _, _ = vm.ssh_iface(idx).run("sync") - assert exit_code == 0 + # Test that net devices have connectivity after restore. + for idx, iface in enumerate(vm.iface.values()): + logger.info("Testing net device %s...", iface["iface"].dev_name) + exit_code, _, _ = vm.ssh_iface(idx).run("true") + assert exit_code == 0 - logger.info("Testing data store behavior...") - _test_mmds(vm, vm.iface["eth3"]["iface"]) + logger.info("Testing data store behavior...") + _test_mmds(vm, vm.iface["eth3"]["iface"]) - logger.info("Testing balloon device...") - _test_balloon(vm) + logger.info("Testing balloon device...") + _test_balloon(vm) - logger.info("Testing vsock device...") - check_vsock_device(vm, bin_vsock_path, test_fc_session_root_path, vm.ssh) + logger.info("Testing vsock device...") + check_vsock_device(vm, bin_vsock_path, test_fc_session_root_path, vm.ssh) - # Run fio on the guest. - # TODO: check the result of FIO or use fsck to check that the root device is - # not corrupted. No obvious errors will be returned here. - guest_run_fio_iteration(vm.ssh, 0) + # Run fio on the guest. + # TODO: check the result of FIO or use fsck to check that the root device is + # not corrupted. No obvious errors will be returned here. + guest_run_fio_iteration(vm.ssh, 0) - vm.kill() + vm.kill() diff --git a/tests/pytest.ini b/tests/pytest.ini index 4a7b3ae1c9e..a74f76ab5e3 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -15,7 +15,9 @@ markers = norecursedirs = .* ; Default timeout for tests. can be overwritten at finer grained levels. -timeout = 300 +; TODO: reduce the number of times to build Firecracker in A/B test and set it +; back to 300 seconds. +timeout = 600 ; Set the cache dir location to our build dir, so we don't litter the source ; tree. diff --git a/tools/create_snapshot_artifact/complex_vm_config.json b/tools/create_snapshot_artifact/complex_vm_config.json deleted file mode 100644 index fdb06dfacb8..00000000000 --- a/tools/create_snapshot_artifact/complex_vm_config.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "boot-source": { - "kernel_image_path": "vmlinux.bin", - "boot_args": "console=ttyS0 reboot=k panic=1 pci=off", - "initrd_path": null - }, - "drives": [ - { - "drive_id": "rootfs", - "path_on_host": "ubuntu-18.04.ext4", - "is_root_device": true, - "partuuid": null, - "is_read_only": false, - "cache_type": "Unsafe", - "io_engine": "Sync", - "rate_limiter": null - } - ], - "machine-config": { - "vcpu_count": 2, - "mem_size_mib": 1024, - "track_dirty_pages": true - }, - "balloon": { - "amount_mib": 0, - "deflate_on_oom": true, - "stats_polling_interval_s": 1 - }, - "network-interfaces": [ - { - "iface_id": "1", - "host_dev_name": "tap0", - "guest_mac": "06:00:c0:a8:00:02", - "rx_rate_limiter": null, - "tx_rate_limiter": null - }, - { - "iface_id": "2", - "host_dev_name": "tap1", - "guest_mac": "06:00:c0:a8:01:02", - "rx_rate_limiter": null, - "tx_rate_limiter": null - }, - { - "iface_id": "3", - "host_dev_name": "tap2", - "guest_mac": "06:00:c0:a8:02:02", - "rx_rate_limiter": null, - "tx_rate_limiter": null - }, - { - "iface_id": "4", - "host_dev_name": "tap3", - "guest_mac": "06:00:c0:a8:03:02", - "rx_rate_limiter": null, - "tx_rate_limiter": null - } - ], - "vsock": { - "guest_cid": 3, - "uds_path": "/v.sock", - "vsock_id": "vsock0" - }, - "logger": null, - "metrics": null, - "mmds-config": { - "version": "V2", - "network_interfaces": ["4"] - } -} diff --git a/tools/create_snapshot_artifact/main.py b/tools/create_snapshot_artifact/main.py index 0c95a39a3ab..3bf7b6d9e9f 100755 --- a/tools/create_snapshot_artifact/main.py +++ b/tools/create_snapshot_artifact/main.py @@ -5,6 +5,7 @@ import json import os +import platform import re import shutil import sys @@ -16,8 +17,12 @@ # pylint: disable=wrong-import-position from framework.artifacts import disks, kernels from framework.microvm import MicroVMFactory -from framework.utils import generate_mmds_get_request, generate_mmds_session_token -from framework.utils_cpuid import CpuVendor, get_cpu_vendor +from framework.utils import ( + configure_mmds, + generate_mmds_get_request, + generate_mmds_session_token, +) +from framework.utils_cpu_templates import get_supported_cpu_templates from host_tools.cargo_build import get_firecracker_binaries # pylint: enable=wrong-import-position @@ -25,8 +30,6 @@ # Default IPv4 address to route MMDS requests. IPV4_ADDRESS = "169.254.169.254" NET_IFACE_FOR_MMDS = "eth3" -# Path to the VM configuration file. -VM_CONFIG_FILE = "tools/create_snapshot_artifact/complex_vm_config.json" # Root directory for the snapshot artifacts. SNAPSHOT_ARTIFACTS_ROOT_DIR = "snapshot_artifacts" @@ -75,8 +78,8 @@ def main(): | -> vm.mem -> vm.vmstate - -> ubuntu-18.04.id_rsa - -> ubuntu-18.04.ext4 + -> ubuntu-22.04.id_rsa + -> ubuntu-22.04.ext4 -> __guest_snapshot | ... @@ -87,85 +90,75 @@ def main(): shutil.rmtree(SNAPSHOT_ARTIFACTS_ROOT_DIR, ignore_errors=True) vm_factory = MicroVMFactory(*get_firecracker_binaries()) - cpu_templates = ["None"] - if get_cpu_vendor() == CpuVendor.INTEL: - cpu_templates.extend(["C3", "T2", "T2S"]) + cpu_templates = [] + if platform.machine() == "x86_64": + cpu_templates = ["None"] + cpu_templates += get_supported_cpu_templates() for cpu_template in cpu_templates: for kernel in kernels(glob="vmlinux-*"): for rootfs in disks(glob="ubuntu-*.squashfs"): print(kernel, rootfs, cpu_template) - vm = vm_factory.build() - create_snapshots(vm, rootfs, kernel, cpu_template) - - -def create_snapshots(vm, rootfs, kernel, cpu_template): - """Snapshot microVM built from vm configuration file.""" - # Get ssh key from read-only artifact. - vm.ssh_key = rootfs.with_suffix(".id_rsa") - vm.rootfs_file = rootfs - vm.kernel_file = kernel - - # adapt the JSON file - vm_config_file = Path(VM_CONFIG_FILE) - obj = json.load(vm_config_file.open(encoding="UTF-8")) - obj["boot-source"]["kernel_image_path"] = kernel.name - obj["drives"][0]["path_on_host"] = rootfs.name - obj["drives"][0]["is_read_only"] = True - obj["machine-config"]["cpu_template"] = cpu_template - vm.create_jailed_resource(vm_config_file) - vm_config = Path(vm.chroot()) / vm_config_file.name - vm_config.write_text(json.dumps(obj)) - vm.jailer.extra_args = {"config-file": vm_config_file.name} - - # since we are using a JSON file, we need to do this manually - vm.create_jailed_resource(rootfs) - vm.create_jailed_resource(kernel) - - for i in range(4): - vm.add_net_iface(api=False) - - vm.spawn(log_level="Info") - - # Ensure the microVM has started. - assert vm.state == "Running" - - # Populate MMDS. - data_store = { - "latest": { - "meta-data": { - "ami-id": "ami-12345678", - "reservation-id": "r-fea54097", - "local-hostname": "ip-10-251-50-12.ec2.internal", - "public-hostname": "ec2-203-0-113-25.compute-1.amazonaws.com", - } - } - } - populate_mmds(vm, data_store) - - # Iterate and validate connectivity on all ifaces after boot. - for i in range(4): - exit_code, _, _ = vm.ssh_iface(i).run("sync") - assert exit_code == 0 - - # Validate MMDS. - validate_mmds(vm.ssh, data_store) - - # Snapshot the microVM. - snapshot = vm.snapshot_diff() - - # Create snapshot artifacts directory specific for the kernel version used. - guest_kernel_version = re.search("vmlinux-(.*)", kernel.name) - - snapshot_artifacts_dir = ( - Path(SNAPSHOT_ARTIFACTS_ROOT_DIR) - / f"{guest_kernel_version.group(1)}_{cpu_template}_guest_snapshot" - ) - snapshot_artifacts_dir.mkdir(parents=True) - snapshot.save_to(snapshot_artifacts_dir) - print(f"Copied snapshot to: {snapshot_artifacts_dir}.") - - vm.kill() + vm = vm_factory.build(kernel, rootfs) + vm.spawn(log_level="Info") + vm.basic_config( + vcpu_count=2, + mem_size_mib=1024, + cpu_template=cpu_template, + track_dirty_pages=True, + ) + # Add 4 network devices + for i in range(4): + vm.add_net_iface() + # Add a vsock device + vm.api.vsock.put(vsock_id="vsock0", guest_cid=3, uds_path="/v.sock") + # Add MMDS + configure_mmds(vm, ["eth3"], version="V2") + # Add a memory balloon. + vm.api.balloon.put( + amount_mib=0, deflate_on_oom=True, stats_polling_interval_s=1 + ) + + vm.start() + # Ensure the microVM has started. + assert vm.state == "Running" + + # Populate MMDS. + data_store = { + "latest": { + "meta-data": { + "ami-id": "ami-12345678", + "reservation-id": "r-fea54097", + "local-hostname": "ip-10-251-50-12.ec2.internal", + "public-hostname": "ec2-203-0-113-25.compute-1.amazonaws.com", + } + } + } + populate_mmds(vm, data_store) + + # Iterate and validate connectivity on all ifaces after boot. + for i in range(4): + exit_code, _, _ = vm.ssh_iface(i).run("sync") + assert exit_code == 0 + + # Validate MMDS. + validate_mmds(vm.ssh, data_store) + + # Snapshot the microVM. + snapshot = vm.snapshot_diff() + + # Create snapshot artifacts directory specific for the kernel version used. + guest_kernel_version = re.search("vmlinux-(.*)", kernel.name) + + snapshot_artifacts_dir = ( + Path(SNAPSHOT_ARTIFACTS_ROOT_DIR) + / f"{guest_kernel_version.group(1)}_{cpu_template}_guest_snapshot" + ) + snapshot_artifacts_dir.mkdir(parents=True) + snapshot.save_to(snapshot_artifacts_dir) + print(f"Copied snapshot to: {snapshot_artifacts_dir}.") + + vm.kill() if __name__ == "__main__": diff --git a/tools/devctr/poetry.lock b/tools/devctr/poetry.lock index a11620e6dd1..91f390c508d 100644 --- a/tools/devctr/poetry.lock +++ b/tools/devctr/poetry.lock @@ -1604,13 +1604,13 @@ files = [ [[package]] name = "requests" -version = "2.31.0" +version = "2.32.0" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.0-py3-none-any.whl", hash = "sha256:f2c3881dddb70d056c5bd7600a4fae312b2a300e39be6a118d30b90bd27262b5"}, + {file = "requests-2.32.0.tar.gz", hash = "sha256:fa5490319474c82ef1d2c9bc459d3652e3ae4ef4c4ebdd18a21145a47ca4b6b8"}, ] [package.dependencies] @@ -2220,4 +2220,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "3.10.*" -content-hash = "728ac7b42a271f17dfbb885850832c4007a8c99c474a0eb9d3e2ac47ceefb9a5" +content-hash = "8cb650f28e76aa658188d4056fcada2cc0077ff789c4a910dd6019530d65c85c" diff --git a/tools/devctr/pyproject.toml b/tools/devctr/pyproject.toml index 32eb1045f38..00c3068cf1e 100644 --- a/tools/devctr/pyproject.toml +++ b/tools/devctr/pyproject.toml @@ -30,7 +30,7 @@ pytest-timeout = "^2.1.0" pytest-xdist = "^3.3.1" python = "3.10.*" PyYAML = "^6.0" -requests = "^2.31.0" +requests = "^2.32.0" requests-unixsocket = "^0.3.0" scipy = "^1.11.2" setproctitle = "^1.3.2" diff --git a/tools/devtool b/tools/devtool index ecaf4167324..d9da59f0e24 100755 --- a/tools/devtool +++ b/tools/devtool @@ -305,7 +305,6 @@ run_devctr() { --rm \ --volume /dev:/dev \ --volume "$FC_ROOT_DIR:$CTR_FC_ROOT_DIR:z" \ - --network="host" \ --tmpfs /srv:exec,dev,size=32G \ -v /boot:/boot \ --env PYTHONDONTWRITEBYTECODE=1 \ @@ -526,7 +525,7 @@ ensure_ci_artifacts() { # Fetch all the artifacts so they are local say "Fetching CI artifacts from S3" - S3_URL=s3://spec.ccfc.min/firecracker-ci/v1.8/$(uname -m) + S3_URL=s3://spec.ccfc.min/firecracker-ci/v1.9/$(uname -m) ARTIFACTS=$MICROVM_IMAGES_DIR/$(uname -m) if [ ! -d "$ARTIFACTS" ]; then mkdir -pv $ARTIFACTS diff --git a/tools/test.sh b/tools/test.sh index 206ed4e9cd7..0bf67a65666 100755 --- a/tools/test.sh +++ b/tools/test.sh @@ -36,5 +36,21 @@ cp -ruvf build/img /srv cd tests export PYTEST_ADDOPTS="${PYTEST_ADDOPTS:-} --pdbcls=IPython.terminal.debugger:TerminalPdb" -pytest "$@" -exit $? + +{ + # disable errexit momentarily so we can capture the exit status + set +e + pytest "$@" + ret=$? + set -e +} + +# if the tests failed and we are running in CI, print some disk usage stats +# to help troubleshooting +if [ $ret != 0 ] && [ "$BUILDKITE" == "true" ]; then + df -ih + df -h + du -h / 2>/dev/null |sort -h |tail -32 +fi + +exit $ret