diff options
author | Frederick Mayle <fmayle@google.com> | 2024-04-16 22:44:01 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2024-04-16 22:44:01 +0000 |
commit | 61c9a84871f705327e37883d4a637b7297766c43 (patch) | |
tree | 717f3c1a4923e084a4cfde51ca9162b4a75f2994 | |
parent | efb7605ac161ba257b8e442e1421e2ce4e161460 (diff) | |
parent | 2a9a3407346fd09aa3ddf89d320e4700211cee5a (diff) | |
download | crosvm-61c9a84871f705327e37883d4a637b7297766c43.tar.gz |
Merge changes from topic "fmayle-crosvm-merge-04122024" into main
* changes:
UPSTREAM: base: fix musl build
Merge remote-tracking branch 'aosp/upstream-main'
188 files changed, 3336 insertions, 3081 deletions
diff --git a/Android.bp b/Android.bp index 5c237099a..77c2eebaa 100644 --- a/Android.bp +++ b/Android.bp @@ -1,5 +1,7 @@ // This file is generated by cargo_embargo. -// Do not modify this file as changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. +// Content before the first "rust_*" or "genrule" module is preserved. package { default_applicable_licenses: ["external_crosvm_license"], diff --git a/Cargo.lock b/Cargo.lock index c579fa3bf..59faa9fa6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,21 @@ dependencies = [ ] [[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] name = "aho-corasick" version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -82,6 +97,7 @@ dependencies = [ "jail", "kernel_cmdline", "libc", + "metrics", "minijail", "power_monitor", "remain", @@ -216,6 +232,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] name = "balloon_control" version = "0.1.0" dependencies = [ @@ -638,6 +669,7 @@ dependencies = [ "sync", "tempfile", "thiserror", + "tokio", "win_util", "winapi", ] @@ -1323,6 +1355,12 @@ dependencies = [ ] [[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1801,6 +1839,7 @@ dependencies = [ "base", "cfg-if", "chrono", + "metrics_events", "metrics_generic", "serde", "sync", @@ -1808,19 +1847,33 @@ dependencies = [ ] [[package]] -name = "metrics_generic" +name = "metrics_events" version = "0.1.0" dependencies = [ "anyhow", - "base", "cfg-if", - "proto_build_tools", - "protobuf", + "metrics_events_generic", "serde", "win_util", ] [[package]] +name = "metrics_events_generic" +version = "0.1.0" +dependencies = [ + "serde", +] + +[[package]] +name = "metrics_generic" +version = "0.1.0" +dependencies = [ + "anyhow", + "base", + "metrics_events", +] + +[[package]] name = "minijail" version = "0.2.3" dependencies = [ @@ -1845,6 +1898,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] name = "named-lock" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1965,6 +2038,15 @@ dependencies = [ ] [[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] name = "once_cell" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2106,9 +2188,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -2409,6 +2491,12 @@ dependencies = [ ] [[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2575,6 +2663,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2597,6 +2695,7 @@ dependencies = [ "jail", "libc", "libtest-mimic", + "metrics", "num_cpus", "once_cell", "remain", @@ -2738,6 +2837,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "windows-sys 0.48.0", +] + +[[package]] name = "toml" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3125,12 +3240,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.1", "windows_aarch64_msvc 0.42.1", "windows_i686_gnu 0.42.1", "windows_i686_msvc 0.42.1", "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.1", "windows_x86_64_msvc 0.42.1", ] @@ -3140,7 +3255,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -3149,22 +3273,43 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.1", "windows_aarch64_msvc 0.42.1", "windows_i686_gnu 0.42.1", "windows_i686_msvc 0.42.1", "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.1", "windows_x86_64_msvc 0.42.1", ] [[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] name = "windows_aarch64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] name = "windows_aarch64_msvc" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3177,6 +3322,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] name = "windows_i686_gnu" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3189,6 +3340,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] name = "windows_i686_msvc" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3201,6 +3358,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] name = "windows_x86_64_gnu" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3213,12 +3376,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] name = "windows_x86_64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] name = "windows_x86_64_msvc" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3231,6 +3406,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] name = "wio" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index c3967f8d4..1e3b7a841 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ members = [ "linux_input_sys", "media/ffmpeg", "metrics", + "metrics_events", "net_sys", "net_util", "power_monitor", @@ -170,6 +171,9 @@ swap = ["aarch64/swap", "arch/swap", "devices/swap", "vm_control/swap", "x86_64/ ## Enables collection of VM statistics. stats = ["devices/stats"] +## Supports tokio as an asynchronous executor. +tokio = ["cros_async/tokio"] + ## Enables USB host device passthrough via an emulated XHCI controller. ## USB is supported only on unix/linux. The feature is a no-op on windows. usb = ["devices/usb"] @@ -347,6 +351,7 @@ all-default = [ "registered_events", "slirp", "swap", + "tokio", "trace_marker", "vaapi", "video-decoder", diff --git a/aarch64/Android.bp b/aarch64/Android.bp index a4c3ac791..e51fc5cc0 100644 --- a/aarch64/Android.bp +++ b/aarch64/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/acpi_tables/Android.bp b/acpi_tables/Android.bp index ae09cfb5c..f39bca670 100644 --- a/acpi_tables/Android.bp +++ b/acpi_tables/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/android-merge-2-cargo-embargo.sh b/android-merge-2-cargo-embargo.sh index f5f1759b3..ceaaa1ded 100755 --- a/android-merge-2-cargo-embargo.sh +++ b/android-merge-2-cargo-embargo.sh @@ -40,7 +40,7 @@ done # # TODO: Consider using android's prebuilt rust binaries. Currently doesn't work # because they try to incorrectly use system clang and llvm. -RUST_TOOLCHAIN="1.68.2" +RUST_TOOLCHAIN="1.73.0" rustup which --toolchain $RUST_TOOLCHAIN cargo || \ rustup toolchain install $RUST_TOOLCHAIN CARGO_BIN="$(dirname $(rustup which --toolchain $RUST_TOOLCHAIN cargo))" @@ -67,7 +67,3 @@ fi # cargo_embargo runs. This didn't happen with cargo2android.py because it # ignored the lock file. git restore Cargo.lock - -# Fix workstation specific path in "metrics" crate's generated files. -# TODO(b/232150148): Find a better solution for protobuf generated files. -sed --in-place 's/path = ".*\/out/path = "./' vendor/generic/metrics/src/out/generated.rs diff --git a/arch/Android.bp b/arch/Android.bp index 4c3e68495..738e27584 100644 --- a/arch/Android.bp +++ b/arch/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { @@ -44,6 +45,7 @@ rust_test { "libjail", "libkernel_cmdline", "liblibc", + "libmetrics", "libminijail_rust", "libpower_monitor", "libresources", @@ -88,6 +90,7 @@ rust_library { "libjail", "libkernel_cmdline", "liblibc", + "libmetrics", "libminijail_rust", "libpower_monitor", "libresources", diff --git a/arch/Cargo.toml b/arch/Cargo.toml index f362ff4b9..8fb29995d 100644 --- a/arch/Cargo.toml +++ b/arch/Cargo.toml @@ -25,6 +25,7 @@ hypervisor = { path = "../hypervisor" } jail = { path = "../jail" } kernel_cmdline = { path = "../kernel_cmdline" } libc = "*" +metrics = { path = "../metrics" } resources = { path = "../resources" } remain = "*" serde = { version = "*", features = [ "derive"] } diff --git a/arch/src/lib.rs b/arch/src/lib.rs index 5c26e3b9d..de7a19320 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -740,6 +740,7 @@ pub fn configure_pci_device<V: VmArch, Vcpu: VcpuArch>( let mut keep_rds = device.keep_rds(); syslog::push_descriptors(&mut keep_rds); cros_tracing::push_descriptors!(&mut keep_rds); + metrics::push_descriptors(&mut keep_rds); device .register_device_capabilities() @@ -821,6 +822,7 @@ pub fn generate_virtio_mmio_bus( let mut keep_rds = device.keep_rds(); syslog::push_descriptors(&mut keep_rds); cros_tracing::push_descriptors!(&mut keep_rds); + metrics::push_descriptors(&mut keep_rds); let irq_num = resources .allocate_irq() @@ -1148,6 +1150,7 @@ pub fn generate_pci_root( let mut keep_rds = device.keep_rds(); syslog::push_descriptors(&mut keep_rds); cros_tracing::push_descriptors!(&mut keep_rds); + metrics::push_descriptors(&mut keep_rds); keep_rds.append(&mut vm.get_memory().as_raw_descriptors()); let ranges = io_ranges.remove(&dev_idx).unwrap_or_default(); diff --git a/arch/src/sys/linux.rs b/arch/src/sys/linux.rs index 4289aea5a..f536535f6 100644 --- a/arch/src/sys/linux.rs +++ b/arch/src/sys/linux.rs @@ -93,6 +93,7 @@ pub fn add_goldfish_battery( let mut keep_rds = goldfish_bat.keep_rds(); syslog::push_descriptors(&mut keep_rds); cros_tracing::push_descriptors!(&mut keep_rds); + metrics::push_descriptors(&mut keep_rds); mmio_bus .insert( Arc::new(Mutex::new( @@ -188,6 +189,7 @@ pub fn generate_platform_bus( let mut keep_rds = device.keep_rds(); syslog::push_descriptors(&mut keep_rds); cros_tracing::push_descriptors!(&mut keep_rds); + metrics::push_descriptors(&mut keep_rds); let irqs = device .get_platform_irqs() diff --git a/argh_helpers/Android.bp b/argh_helpers/Android.bp index 95ec35255..33024d115 100644 --- a/argh_helpers/Android.bp +++ b/argh_helpers/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/audio_util/Android.bp b/audio_util/Android.bp index afc89376f..4000343d6 100644 --- a/audio_util/Android.bp +++ b/audio_util/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/base/Android.bp b/base/Android.bp index 1ed8cb7da..4a871fb0e 100644 --- a/base/Android.bp +++ b/base/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/base/base_event_token_derive/Android.bp b/base/base_event_token_derive/Android.bp index 92a8f2b51..7f9b0862d 100644 --- a/base/base_event_token_derive/Android.bp +++ b/base/base_event_token_derive/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/base/src/lib.rs b/base/src/lib.rs index e23f2876e..e7b77f9c9 100644 --- a/base/src/lib.rs +++ b/base/src/lib.rs @@ -15,6 +15,7 @@ mod file_traits; mod iobuf; mod mmap; mod notifiers; +mod periodic_logger; mod shm; pub mod syslog; pub mod test_utils; diff --git a/base/src/mmap.rs b/base/src/mmap.rs index 6654d078e..8095135eb 100644 --- a/base/src/mmap.rs +++ b/base/src/mmap.rs @@ -13,6 +13,7 @@ use std::ptr::write_unaligned; use std::ptr::write_volatile; use std::sync::atomic::fence; use std::sync::atomic::Ordering; +use std::sync::OnceLock; use remain::sorted; use serde::Deserialize; @@ -29,6 +30,44 @@ use crate::VolatileMemoryError; use crate::VolatileMemoryResult; use crate::VolatileSlice; +static CACHELINE_SIZE: OnceLock<usize> = OnceLock::new(); + +#[allow(unused_assignments)] +fn get_cacheline_size_once() -> usize { + let mut assume_reason: &str = "unknown"; + cfg_if::cfg_if! { + if #[cfg(all(any(target_os = "android", target_os = "linux"), not(target_env = "musl")))] { + // SAFETY: + // Safe because we check the return value for errors or unsupported requests + let linesize = unsafe { libc::sysconf(libc::_SC_LEVEL1_DCACHE_LINESIZE) }; + if linesize > 0 { + return linesize as usize; + } else { + assume_reason = "sysconf cacheline size query failed"; + } + } else { + assume_reason = "cacheline size query not implemented for platform/arch"; + } + } + + let assumed_size = 64; + log::debug!( + "assuming cacheline_size={}; reason: {}.", + assumed_size, + assume_reason + ); + assumed_size +} + +/// Returns the system's effective cacheline size (e.g. the granularity at which arch-specific +/// cacheline management, such as with the clflush instruction, is expected to occur). +#[inline(always)] +fn get_cacheline_size() -> usize { + let size = *CACHELINE_SIZE.get_or_init(get_cacheline_size_once); + assert!(size > 0); + size +} + #[sorted] #[derive(Debug, thiserror::Error)] pub enum Error { @@ -44,6 +83,8 @@ pub enum Error { InvalidOffset, #[error("requested memory range spans past the end of the region: offset={0} count={1} region_size={2}")] InvalidRange(usize, usize, usize), + #[error("operation is not implemented on platform/architecture: {0}")] + NotImplemented(&'static str), #[error("requested memory is not page aligned")] NotPageAligned, #[error("failed to read from file to memory: {0}")] @@ -132,6 +173,27 @@ pub struct MemoryMapping { pub(crate) _file_descriptor: Option<SafeDescriptor>, } +#[inline(always)] +unsafe fn flush_one(_addr: *const u8) -> Result<()> { + cfg_if::cfg_if! { + if #[cfg(target_arch = "x86_64")] { + // As per table 11-7 of the SDM, processors are not required to + // snoop UC mappings, so flush the target to memory. + // SAFETY: assumes that the caller has supplied a valid address. + unsafe { core::arch::x86_64::_mm_clflush(_addr) }; + Ok(()) + } else if #[cfg(target_arch = "aarch64")] { + // Data cache clean by VA to PoC. + std::arch::asm!("DC CVAC, {x}", x = in(reg) _addr); + Ok(()) + } else if #[cfg(target_arch = "arm")] { + Err(Error::NotImplemented("Userspace cannot flush to PoC")) + } else { + Err(Error::NotImplemented("Cache flush not implemented")) + } + } +} + impl MemoryMapping { pub fn write_slice(&self, buf: &[u8], offset: usize) -> Result<usize> { match self.mapping.size().checked_sub(offset) { @@ -294,42 +356,63 @@ impl MemoryMapping { self.mapping.msync() } - /// Flush memory which the guest may be accessing through an uncached mapping. + /// Flush a region of the MemoryMapping from the system's caching hierarchy. + /// There are several uses for flushing: /// - /// Reads via an uncached mapping can bypass the cache and directly access main - /// memory. This is outside the memory model of Rust, which means that even with - /// proper synchronization, guest reads via an uncached mapping might not see - /// updates from the host. As such, it is necessary to perform architectural - /// cache maintainance to flush the host writes to main memory. + /// * Cached memory which the guest may be reading through an uncached mapping: /// - /// Note that this does not support writable uncached guest mappings, as doing so - /// requires invalidating the cache, not flushing the cache. + /// Guest reads via an uncached mapping can bypass the cache and directly access main + /// memory. This is outside the memory model of Rust, which means that even with proper + /// synchronization, guest reads via an uncached mapping might not see updates from the + /// host. As such, it is necessary to perform architectural cache maintainance to flush the + /// host writes to main memory. + /// + /// Note that this does not support writable uncached guest mappings, as doing so + /// requires invalidating the cache, not flushing the cache. + /// + /// * Uncached memory which the guest may be writing through a cached mapping: + /// + /// Guest writes via a cached mapping of a host's uncached memory may never make it to + /// system/device memory prior to being read. In such cases, explicit flushing of the cached + /// writes is necessary, since other managers of the host's uncached mapping (e.g. DRM) see + /// no need to flush, as they believe all writes would explicitly bypass the caches. /// /// Currently only supported on x86_64 and aarch64. Cannot be supported on 32-bit arm. - pub fn flush_uncached_guest_mapping(&self, offset: usize) { - if offset > self.mapping.size() { - return; + pub fn flush_region(&self, offset: usize, len: usize) -> Result<()> { + let addr: *const u8 = self.as_ptr(); + let size = self.size(); + + // disallow overflow/wrapping ranges and subregion extending beyond mapped range + if usize::MAX - size < addr as usize || offset >= size || size - offset < len { + return Err(Error::InvalidRange(offset, len, size)); } - // SAFETY: We checked that offset is within the mapping, and flushing - // the cache doesn't affect any rust safety properties. - unsafe { - #[allow(unused)] - let target = self.mapping.as_ptr().add(offset); - cfg_if::cfg_if! { - if #[cfg(target_arch = "x86_64")] { - // As per table 11-7 of the SDM, processors are not required to - // snoop UC mappings, so flush the target to memory. - core::arch::x86_64::_mm_clflush(target); - } else if #[cfg(target_arch = "aarch64")] { - // Data cache clean by VA to PoC. - std::arch::asm!("DC CVAC, {x}", x = in(reg) target); - } else if #[cfg(target_arch = "arm")] { - panic!("Userspace cannot flush to PoC"); - } else { - unimplemented!("Cache flush not implemented") - } - } + + // SAFETY: + // Safe because already validated that `next` will be an address in the mapping: + // * mapped region is non-wrapping + // * subregion is bounded within the mapped region + let mut next: *const u8 = unsafe { addr.add(offset) }; + + let cacheline_size = get_cacheline_size(); + let cacheline_count = len.div_ceil(cacheline_size); + + for _ in 0..cacheline_count { + // SAFETY: + // Safe because `next` is guaranteed to be within the mapped region (see earlier + // validations), and flushing the cache doesn't affect any rust safety properties. + unsafe { flush_one(next)? }; + + // SAFETY: + // Safe because we never use next if it goes out of the mapped region or overflows its + // storage type (based on earlier validations and the loop bounds). + next = unsafe { next.add(cacheline_size) }; } + Ok(()) + } + + /// Flush all backing memory for a mapping in an arch-specific manner (see `flush_region()`). + pub fn flush_all(&self) -> Result<()> { + self.flush_region(0, self.size()) } } diff --git a/base/src/periodic_logger.rs b/base/src/periodic_logger.rs new file mode 100644 index 000000000..7fd1a045d --- /dev/null +++ b/base/src/periodic_logger.rs @@ -0,0 +1,232 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TODO(b/318439696): Remove once it is used +#![allow(dead_code)] + +use std::collections::HashMap; +use std::fmt::Write; +use std::sync::atomic::AtomicU32; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::sync::RwLock; +use std::time::Duration; + +use thiserror::Error as ThisError; + +use crate::EventToken; +use crate::Timer; +use crate::TimerTrait; +use crate::WaitContext; +use crate::WorkerThread; + +/// Utility class that helps count and log high frequency events periodically. +pub struct PeriodicLogger { + // Name that is printed out to differentiate between other `PeriodicLogger`s + name: String, + // Interval to log + interval: Duration, + // Map of event counters that are periodically logged + counters: Arc<RwLock<HashMap<String, AtomicU32>>>, + // The periodic logger thread + worker_thread: Option<WorkerThread<Result<(), PeriodicLoggerError>>>, +} + +impl PeriodicLogger { + pub fn new(name: String, interval: Duration) -> Self { + PeriodicLogger { + name, + interval, + counters: Arc::new(RwLock::new(HashMap::new())), + worker_thread: None, + } + } + + /// Add a new event item to be counted. + pub fn add_counter_item(&self, name: String) -> Result<(), PeriodicLoggerError> { + // This write lock will likely be acquired infrequently. + let mut counters_write_lock = self + .counters + .write() + .map_err(|e| PeriodicLoggerError::WriteLockError(e.to_string()))?; + + if counters_write_lock.contains_key(&name) { + return Err(PeriodicLoggerError::CounterAlreadyExist(name)); + } + + counters_write_lock.insert(name, AtomicU32::new(0)); + Ok(()) + } + + /// Increment event counter by an `amount` + pub fn increment_counter(&self, name: String, amount: u32) -> Result<(), PeriodicLoggerError> { + match self.counters.read() { + Ok(counters_map) => { + if let Some(atomic_counter) = counters_map.get(&name) { + atomic_counter.fetch_add(amount, Ordering::Relaxed); + Ok(()) + } else { + Err(PeriodicLoggerError::CounterDoesNotExist(name)) + } + } + Err(e) => Err(PeriodicLoggerError::ReadLockError(e.to_string())), + } + } + + /// Starts a thread that will log the count of events within a `self.interval` time period. + /// All counters will be reset to 0 after logging. + pub fn start_logging_thread(&mut self) -> Result<(), PeriodicLoggerError> { + if self.worker_thread.is_some() { + return Err(PeriodicLoggerError::ThreadAlreadyStarted); + } + + #[derive(EventToken)] + enum Token { + Exit, + PeriodicLog, + } + + let cloned_counter = self.counters.clone(); + let interval_copy = self.interval; + let name_copy = self.name.clone(); + self.worker_thread = Some(WorkerThread::start( + format!("PeriodicLogger_{}", self.name), + move |kill_evt| { + let mut timer = Timer::new().map_err(PeriodicLoggerError::TimerNewError)?; + timer + .reset(interval_copy, Some(interval_copy)) + .map_err(PeriodicLoggerError::TimerResetError)?; + + let wait_ctx = WaitContext::build_with(&[ + (&kill_evt, Token::Exit), + (&timer, Token::PeriodicLog), + ]) + .map_err(PeriodicLoggerError::WaitContextBuildError)?; + + 'outer: loop { + let events = wait_ctx.wait().expect("wait failed"); + for event in events.iter().filter(|e| e.is_readable) { + match event.token { + Token::Exit => { + break 'outer; + } + Token::PeriodicLog => { + let counter_map = cloned_counter.read().map_err(|e| { + PeriodicLoggerError::ReadLockError(e.to_string()) + })?; + + let mut logged_string = + format!("{} {:?}:", name_copy, interval_copy); + for (counter_name, counter_value) in counter_map.iter() { + let value = counter_value.swap(0, Ordering::Relaxed); + let _ = + write!(logged_string, "\n {}: {}", counter_name, value); + } + + // Log all counters + crate::info!("{}", logged_string); + } + } + } + } + Ok(()) + }, + )); + + Ok(()) + } +} + +#[derive(Debug, ThisError, PartialEq)] +pub enum PeriodicLoggerError { + #[error("Periodic logger thread already started.")] + ThreadAlreadyStarted, + #[error("Failed to acquire write lock: {0}")] + WriteLockError(String), + #[error("Failed to acquire read lock: {0}")] + ReadLockError(String), + #[error("Counter already exists: {0}")] + CounterAlreadyExist(String), + #[error("Counter does not exist: {0}")] + CounterDoesNotExist(String), + #[error("Failed to build WaitContext: {0}")] + WaitContextBuildError(crate::Error), + #[error("Failed to wait on WaitContext: {0}")] + WaitContextWaitError(crate::Error), + #[error("Failed to reset Timer: {0}")] + TimerResetError(crate::Error), + #[error("Failed initialize Timer: {0}")] + TimerNewError(crate::Error), +} + +#[cfg(test)] +mod tests { + use std::thread; + + use super::*; + + #[test] + fn periodic_add() { + let periodic_logger = PeriodicLogger::new("test".to_string(), Duration::from_secs(3)); + periodic_logger + .add_counter_item("counter_1".to_string()) + .unwrap(); + periodic_logger + .increment_counter("counter_1".to_string(), 2) + .unwrap(); + periodic_logger + .increment_counter("counter_1".to_string(), 5) + .unwrap(); + + assert_eq!(periodic_logger.counters.read().unwrap().len(), 1); + assert_eq!( + periodic_logger + .counters + .read() + .unwrap() + .get("counter_1") + .unwrap() + .load(Ordering::Relaxed), + 7 + ); + } + + #[test] + fn worker_thread_cannot_start_twice() { + let mut periodic_logger = PeriodicLogger::new("test".to_string(), Duration::from_secs(3)); + assert!(periodic_logger.start_logging_thread().is_ok()); + assert!(periodic_logger.start_logging_thread().is_err()); + } + + #[test] + fn add_same_counter_item_twice_return_err() { + let periodic_logger = PeriodicLogger::new("test".to_string(), Duration::from_secs(3)); + assert!(periodic_logger + .add_counter_item("counter_1".to_string()) + .is_ok()); + assert_eq!( + periodic_logger.add_counter_item("counter_1".to_string()), + Err(PeriodicLoggerError::CounterAlreadyExist( + "counter_1".to_string() + )) + ); + } + + /// Ignored because this is intended to be ran locally + #[ignore] + #[test] + fn periodic_logger_smoke_test() { + let mut periodic_logger = PeriodicLogger::new("test".to_string(), Duration::from_secs(3)); + periodic_logger + .add_counter_item("counter_1".to_string()) + .unwrap(); + + periodic_logger.start_logging_thread().unwrap(); + periodic_logger + .increment_counter("counter_1".to_string(), 5) + .unwrap(); + + thread::sleep(Duration::from_secs(5)); + } +} diff --git a/base/src/sys/unix/descriptor.rs b/base/src/sys/unix/descriptor.rs index 26a9cd2c3..fd97efe6e 100644 --- a/base/src/sys/unix/descriptor.rs +++ b/base/src/sys/unix/descriptor.rs @@ -32,24 +32,25 @@ pub type RawDescriptor = RawFd; pub const INVALID_DESCRIPTOR: RawDescriptor = -1; -/// Clones `descriptor`, returning a new `RawDescriptor` that refers to the same open file -/// description as `descriptor`. The cloned descriptor will have the `FD_CLOEXEC` flag set but will -/// not share any other file descriptor flags with `descriptor`. -pub fn clone_descriptor(descriptor: &dyn AsRawDescriptor) -> Result<RawDescriptor> { - clone_fd(&descriptor.as_raw_descriptor()) +/// Clones `descriptor`, returning a new `SafeDescriptor` that refers to the same file +/// `descriptor`. The cloned descriptor will have the `FD_CLOEXEC` flag set but will not share any +/// other file descriptor flags with `descriptor`. +pub fn clone_descriptor(descriptor: &(impl AsRawDescriptor + ?Sized)) -> Result<SafeDescriptor> { + clone_fd(descriptor.as_raw_descriptor()) } -/// Clones `fd`, returning a new file descriptor that refers to the same open file description as -/// `fd`. The cloned fd will have the `FD_CLOEXEC` flag set but will not share any other file -/// descriptor flags with `fd`. -fn clone_fd(fd: &dyn AsRawFd) -> Result<RawFd> { +/// Clones `fd`, returning a new file descriptor that refers to the same open file as `fd`. The +/// cloned fd will have the `FD_CLOEXEC` flag set but will not share any other file descriptor +/// flags with `fd`. +fn clone_fd(fd: RawFd) -> Result<SafeDescriptor> { // SAFETY: // Safe because this doesn't modify any memory and we check the return value. - let ret = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_DUPFD_CLOEXEC, 0) }; + let ret = unsafe { libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, 0) }; if ret < 0 { errno_result() } else { - Ok(ret) + // SAFETY: We just dup'd the FD and so have exclusive access. + Ok(unsafe { SafeDescriptor::from_raw_descriptor(ret) }) } } @@ -108,9 +109,7 @@ impl TryFrom<&dyn AsRawFd> for SafeDescriptor { type Error = std::io::Error; fn try_from(fd: &dyn AsRawFd) -> std::result::Result<Self, Self::Error> { - Ok(SafeDescriptor { - descriptor: clone_fd(fd)?, - }) + Ok(clone_fd(fd.as_raw_fd())?) } } diff --git a/base/src/sys/windows/tube.rs b/base/src/sys/windows/tube.rs index ed61c2f39..b65350837 100644 --- a/base/src/sys/windows/tube.rs +++ b/base/src/sys/windows/tube.rs @@ -384,6 +384,18 @@ impl AsRawDescriptor for RecvTube { } } +impl CloseNotifier for SendTube { + fn get_close_notifier(&self) -> &dyn AsRawDescriptor { + self.0.get_close_notifier() + } +} + +impl CloseNotifier for RecvTube { + fn get_close_notifier(&self) -> &dyn AsRawDescriptor { + self.0.get_close_notifier() + } +} + /// A request to duplicate a handle to a target process. #[derive(Serialize, Deserialize, Debug)] pub struct DuplicateHandleRequest { diff --git a/bit_field/Android.bp b/bit_field/Android.bp index d6f35fb37..3873a8fa5 100644 --- a/bit_field/Android.bp +++ b/bit_field/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/bit_field/bit_field_derive/Android.bp b/bit_field/bit_field_derive/Android.bp index 34c8592b0..fb5b59d15 100644 --- a/bit_field/bit_field_derive/Android.bp +++ b/bit_field/bit_field_derive/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/broker_ipc/Android.bp b/broker_ipc/Android.bp index d78d4688a..85409085e 100644 --- a/broker_ipc/Android.bp +++ b/broker_ipc/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/broker_ipc/src/lib.rs b/broker_ipc/src/lib.rs index 4f396615a..71a7313ee 100644 --- a/broker_ipc/src/lib.rs +++ b/broker_ipc/src/lib.rs @@ -17,7 +17,7 @@ use base::EnabledHighResTimer; use base::FromRawDescriptor; use base::IntoRawDescriptor; use base::SafeDescriptor; -use base::Tube; +use base::SendTube; #[cfg(feature = "process-invariants")] pub use broker_ipc_product::init_broker_process_invariants; use broker_ipc_product::init_child_crash_reporting; @@ -33,7 +33,7 @@ use serde::Serialize; pub struct CommonChildStartupArgs { log_args: LogArgs, syslog_file: Option<SafeDescriptor>, - metrics_tube: Option<Tube>, + metrics_tube: Option<SendTube>, product_attrs: ProductAttributes, } @@ -44,7 +44,7 @@ impl CommonChildStartupArgs { syslog_path: Option<PathBuf>, #[cfg(feature = "crash-report")] _crash_attrs: crash_report::CrashReportAttributes, #[cfg(feature = "process-invariants")] _process_invariants: EmulatorProcessInvariants, - metrics_tube: Option<Tube>, + metrics_tube: Option<SendTube>, ) -> anyhow::Result<Self> { Ok(Self { log_args: log_args.clone(), diff --git a/cargo_embargo.json b/cargo_embargo.json index 377728074..777ce8b6f 100644 --- a/cargo_embargo.json +++ b/cargo_embargo.json @@ -191,9 +191,6 @@ "kvm_sys": { "no_presubmit": true }, - "metrics": { - "copy_out": true - }, "net_util": { "no_presubmit": true }, diff --git a/common/audio_streams/Android.bp b/common/audio_streams/Android.bp index 1eed2c763..9d535c076 100644 --- a/common/audio_streams/Android.bp +++ b/common/audio_streams/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/common/balloon_control/Android.bp b/common/balloon_control/Android.bp index e6ddd56d8..561c23b2c 100644 --- a/common/balloon_control/Android.bp +++ b/common/balloon_control/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/common/data_model/Android.bp b/common/data_model/Android.bp index 78d8ba2d3..461e626ce 100644 --- a/common/data_model/Android.bp +++ b/common/data_model/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/common/sync/Android.bp b/common/sync/Android.bp index d683033ff..af6260cc9 100644 --- a/common/sync/Android.bp +++ b/common/sync/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/cros_async/Android.bp b/cros_async/Android.bp index 6acddafbb..a74446941 100644 --- a/cros_async/Android.bp +++ b/cros_async/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/cros_async/Cargo.toml b/cros_async/Cargo.toml index 3db488892..fb25c40ad 100644 --- a/cros_async/Cargo.toml +++ b/cros_async/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.1" authors = ["The ChromiumOS Authors"] edition = "2021" +[features] +tokio = ["dep:tokio"] + [dependencies] async-trait = "0.1.36" async-task = "4" @@ -23,6 +26,7 @@ anyhow = "1.0" serde = "*" serde_keyvalue = { path = "../serde_keyvalue", features = ["argh_derive"] } # provided by ebuild static_assertions = "1.1" +tokio = { version = "1.29.1", optional = true, features = ["net", "rt-multi-thread"] } [target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] io_uring = { path = "../io_uring" } # provided by ebuild diff --git a/cros_async/src/executor.rs b/cros_async/src/executor.rs index 2f1e7166b..35e409561 100644 --- a/cros_async/src/executor.rs +++ b/cros_async/src/executor.rs @@ -13,6 +13,10 @@ use base::AsRawDescriptors; #[cfg(any(target_os = "android", target_os = "linux"))] use base::RawDescriptor; use once_cell::sync::OnceCell; +use serde::Deserialize; +use serde_keyvalue::argh::FromArgValue; +use serde_keyvalue::ErrorKind; +use serde_keyvalue::KeyValueDeserializer; use crate::common_executor; use crate::common_executor::RawExecutor; @@ -25,19 +29,18 @@ use crate::AsyncResult; use crate::IntoAsync; use crate::IoSource; -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - serde::Serialize, - serde::Deserialize, - serde_keyvalue::FromKeyValues, -)] -#[serde(deny_unknown_fields, rename_all = "kebab-case", untagged)] +cfg_if::cfg_if! { + if #[cfg(feature = "tokio")] { + use crate::tokio_executor::TokioExecutor; + use crate::tokio_executor::TokioTaskHandle; + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ExecutorKind { SysVariants(ExecutorKindSys), + #[cfg(feature = "tokio")] + Tokio, } impl From<ExecutorKindSys> for ExecutorKind { @@ -46,16 +49,9 @@ impl From<ExecutorKindSys> for ExecutorKind { } } -// TODO: schuffelen - Remove after adding a platform-independent Executor -impl From<ExecutorKind> for ExecutorKindSys { - fn from(e: ExecutorKind) -> ExecutorKindSys { - match e { - ExecutorKind::SysVariants(inner) => inner, - } - } -} - -/// If set, [`Executor::new()`] is created with `ExecutorKindSys` of `DEFAULT_EXECUTOR_KIND`. +/// If set, [`ExecutorKind::default()`] returns the value of `DEFAULT_EXECUTOR_KIND`. +/// If not set, [`ExecutorKind::default()`] returns a statically-chosen default value, and +/// [`ExecutorKind::default()`] initializes `DEFAULT_EXECUTOR_KIND` with that value. static DEFAULT_EXECUTOR_KIND: OnceCell<ExecutorKind> = OnceCell::new(); impl Default for ExecutorKind { @@ -82,6 +78,76 @@ pub enum SetDefaultExecutorKindError { UringUnavailable(linux::uring_executor::Error), } +impl FromArgValue for ExecutorKind { + fn from_arg_value(value: &str) -> std::result::Result<ExecutorKind, String> { + // `from_arg_value` returns a `String` as error, but our deserializer API defines its own + // error type. Perform parsing from a closure so we can easily map returned errors. + let builder = move || { + let mut des = KeyValueDeserializer::from(value); + + let kind: ExecutorKind = match (des.parse_identifier()?, des.next_char()) { + #[cfg(any(target_os = "android", target_os = "linux"))] + ("epoll", None) => ExecutorKindSys::Fd.into(), + #[cfg(any(target_os = "android", target_os = "linux"))] + ("uring", None) => ExecutorKindSys::Uring.into(), + #[cfg(windows)] + ("handle", None) => ExecutorKindSys::Handle.into(), + #[cfg(windows)] + ("overlapped", None) => ExecutorKindSys::Overlapped { concurrency: None }.into(), + #[cfg(windows)] + ("overlapped", Some(',')) => { + if des.parse_identifier()? != "concurrency" { + let kind = ErrorKind::SerdeError("expected `concurrency`".to_string()); + return Err(des.error_here(kind)); + } + if des.next_char() != Some('=') { + return Err(des.error_here(ErrorKind::ExpectedEqual)); + } + let concurrency = des.parse_number()?; + ExecutorKindSys::Overlapped { + concurrency: Some(concurrency), + } + .into() + } + #[cfg(feature = "tokio")] + ("tokio", None) => ExecutorKind::Tokio, + (_identifier, _next) => { + let kind = ErrorKind::SerdeError("unexpected kind".to_string()); + return Err(des.error_here(kind)); + } + }; + des.finish()?; + Ok(kind) + }; + + builder().map_err(|e| e.to_string()) + } +} + +impl serde::Serialize for ExecutorKind { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + match self { + ExecutorKind::SysVariants(sv) => sv.serialize(serializer), + #[cfg(feature = "tokio")] + ExecutorKind::Tokio => "tokio".serialize(serializer), + } + } +} + +impl<'de> Deserialize<'de> for ExecutorKind { + fn deserialize<D>(deserializer: D) -> Result<ExecutorKind, D::Error> + where + D: serde::Deserializer<'de>, + { + base::error!("ExecutorKind::deserialize"); + let string = String::deserialize(deserializer)?; + ExecutorKind::from_arg_value(&string).map_err(serde::de::Error::custom) + } +} + /// Reference to a task managed by the executor. /// /// Dropping a `TaskHandle` attempts to cancel the associated task. Call `detach` to allow it to @@ -95,6 +161,8 @@ pub enum TaskHandle<R> { Uring(common_executor::RawTaskHandle<linux::UringReactor, R>), #[cfg(windows)] Handle(common_executor::RawTaskHandle<windows::HandleReactor, R>), + #[cfg(feature = "tokio")] + Tokio(TokioTaskHandle<R>), } impl<R: Send + 'static> TaskHandle<R> { @@ -106,6 +174,8 @@ impl<R: Send + 'static> TaskHandle<R> { TaskHandle::Uring(u) => u.detach(), #[cfg(windows)] TaskHandle::Handle(h) => h.detach(), + #[cfg(feature = "tokio")] + TaskHandle::Tokio(t) => t.detach(), } } @@ -119,6 +189,8 @@ impl<R: Send + 'static> TaskHandle<R> { TaskHandle::Uring(u) => u.cancel().await, #[cfg(windows)] TaskHandle::Handle(h) => h.cancel().await, + #[cfg(feature = "tokio")] + TaskHandle::Tokio(t) => t.cancel().await, } } } @@ -134,6 +206,8 @@ impl<R: 'static> Future for TaskHandle<R> { TaskHandle::Uring(u) => Pin::new(u).poll(cx), #[cfg(windows)] TaskHandle::Handle(h) => Pin::new(h).poll(cx), + #[cfg(feature = "tokio")] + TaskHandle::Tokio(t) => Pin::new(t).poll(cx), } } } @@ -265,6 +339,8 @@ pub enum Executor { Handle(Arc<RawExecutor<windows::HandleReactor>>), #[cfg(windows)] Overlapped(Arc<RawExecutor<windows::HandleReactor>>), + #[cfg(feature = "tokio")] + Tokio(TokioExecutor), } impl Executor { @@ -287,21 +363,15 @@ impl Executor { Executor::Handle(RawExecutor::new()?) } #[cfg(windows)] - ExecutorKind::SysVariants(ExecutorKindSys::Overlapped) => { - Executor::Overlapped(RawExecutor::new()?) - } - }) - } - - /// Create a new `Executor` of the given `ExecutorKind`. - pub fn with_kind_and_concurrency(kind: ExecutorKind, _concurrency: u32) -> AsyncResult<Self> { - Ok(match kind { - #[cfg(windows)] - ExecutorKind::SysVariants(ExecutorKindSys::Overlapped) => { - let reactor = windows::HandleReactor::new_with(_concurrency)?; + ExecutorKind::SysVariants(ExecutorKindSys::Overlapped { concurrency }) => { + let reactor = match concurrency { + Some(concurrency) => windows::HandleReactor::new_with(concurrency)?, + None => windows::HandleReactor::new()?, + }; Executor::Overlapped(RawExecutor::new_with(reactor)?) } - _ => Executor::with_executor_kind(kind)?, + #[cfg(feature = "tokio")] + ExecutorKind::Tokio => Executor::Tokio(TokioExecutor::new()?), }) } @@ -341,6 +411,8 @@ impl Executor { Executor::Handle(ex) => ex.async_from(f), #[cfg(windows)] Executor::Overlapped(ex) => ex.async_from(f), + #[cfg(feature = "tokio")] + Executor::Tokio(ex) => ex.async_from(f), } } @@ -402,6 +474,8 @@ impl Executor { Executor::Handle(ex) => ex.spawn(f), #[cfg(windows)] Executor::Overlapped(ex) => ex.spawn(f), + #[cfg(feature = "tokio")] + Executor::Tokio(ex) => ex.spawn(f), } } @@ -446,6 +520,8 @@ impl Executor { Executor::Handle(ex) => ex.spawn_local(f), #[cfg(windows)] Executor::Overlapped(ex) => ex.spawn_local(f), + #[cfg(feature = "tokio")] + Executor::Tokio(ex) => ex.spawn_local(f), } } @@ -492,6 +568,8 @@ impl Executor { Executor::Handle(ex) => ex.spawn_blocking(f), #[cfg(windows)] Executor::Overlapped(ex) => ex.spawn_blocking(f), + #[cfg(feature = "tokio")] + Executor::Tokio(ex) => ex.spawn_blocking(f), } } @@ -569,6 +647,8 @@ impl Executor { Executor::Handle(ex) => ex.run_until(f), #[cfg(windows)] Executor::Overlapped(ex) => ex.run_until(f), + #[cfg(feature = "tokio")] + Executor::Tokio(ex) => ex.run_until(f), } } } @@ -579,6 +659,8 @@ impl AsRawDescriptors for Executor { match self { Executor::Fd(ex) => ex.as_raw_descriptors(), Executor::Uring(ex) => ex.as_raw_descriptors(), + #[cfg(feature = "tokio")] + Executor::Tokio(ex) => ex.as_raw_descriptors(), } } } diff --git a/cros_async/src/io_source.rs b/cros_async/src/io_source.rs index efc4c03b5..b46b2aa0e 100644 --- a/cros_async/src/io_source.rs +++ b/cros_async/src/io_source.rs @@ -10,6 +10,8 @@ use base::AsRawDescriptor; use crate::sys::linux::PollSource; #[cfg(any(target_os = "android", target_os = "linux"))] use crate::sys::linux::UringSource; +#[cfg(feature = "tokio")] +use crate::sys::platform::tokio_source::TokioSource; #[cfg(windows)] use crate::sys::windows::HandleSource; #[cfg(windows)] @@ -29,6 +31,8 @@ pub enum IoSource<F: base::AsRawDescriptor> { Handle(HandleSource<F>), #[cfg(windows)] Overlapped(OverlappedSource<F>), + #[cfg(feature = "tokio")] + Tokio(TokioSource<F>), } static_assertions::assert_impl_all!(IoSource<std::fs::File>: Send, Sync); @@ -47,6 +51,8 @@ macro_rules! await_on_inner { IoSource::Handle(x) => HandleSource::$method(x, $($args),*).await, #[cfg(windows)] IoSource::Overlapped(x) => OverlappedSource::$method(x, $($args),*).await, + #[cfg(feature = "tokio")] + IoSource::Tokio(x) => TokioSource::$method(x, $($args),*).await, } }; } @@ -65,6 +71,8 @@ macro_rules! on_inner { IoSource::Handle(x) => HandleSource::$method(x, $($args),*), #[cfg(windows)] IoSource::Overlapped(x) => OverlappedSource::$method(x, $($args),*), + #[cfg(feature = "tokio")] + IoSource::Tokio(x) => TokioSource::$method(x, $($args),*), } }; } diff --git a/cros_async/src/lib.rs b/cros_async/src/lib.rs index 70ac127d1..071317d8b 100644 --- a/cros_async/src/lib.rs +++ b/cros_async/src/lib.rs @@ -57,6 +57,7 @@ mod async_types; pub mod audio_streams_async; mod blocking; +mod common_executor; mod complete; mod event; mod executor; @@ -67,10 +68,9 @@ mod queue; mod select; pub mod sync; pub mod sys; -#[cfg(any(target_os = "android", target_os = "linux"))] -pub use sys::linux::uring_executor::is_uring_stable; -mod common_executor; mod timer; +#[cfg(feature = "tokio")] +mod tokio_executor; mod waker; use std::future::Future; @@ -105,6 +105,8 @@ pub use mem::MemRegionIter; pub use mem::VecIoWrapper; use remain::sorted; pub use select::SelectResult; +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use sys::linux::uring_executor::is_uring_stable; use thiserror::Error as ThisError; pub use timer::TimerAsync; diff --git a/cros_async/src/sys/linux.rs b/cros_async/src/sys/linux.rs index fb1948c39..fcaf87ec9 100644 --- a/cros_async/src/sys/linux.rs +++ b/cros_async/src/sys/linux.rs @@ -9,6 +9,8 @@ pub mod executor; pub mod fd_executor; pub mod poll_source; mod timer; +#[cfg(feature = "tokio")] +pub mod tokio_source; pub mod uring_executor; pub mod uring_source; diff --git a/cros_async/src/sys/linux/error.rs b/cros_async/src/sys/linux/error.rs index 48e492cb3..83e946d3c 100644 --- a/cros_async/src/sys/linux/error.rs +++ b/cros_async/src/sys/linux/error.rs @@ -9,6 +9,9 @@ use std::io; pub enum AsyncErrorSys { #[error("Poll source error: {0}")] Poll(#[from] super::poll_source::Error), + #[cfg(feature = "tokio")] + #[error("Tokio source error: {0}")] + Tokio(#[from] super::tokio_source::Error), #[error("Uring source error: {0}")] Uring(#[from] super::uring_executor::Error), } @@ -17,6 +20,8 @@ impl From<AsyncErrorSys> for io::Error { fn from(err: AsyncErrorSys) -> Self { match err { AsyncErrorSys::Poll(e) => e.into(), + #[cfg(feature = "tokio")] + AsyncErrorSys::Tokio(e) => e.into(), AsyncErrorSys::Uring(e) => e.into(), } } diff --git a/cros_async/src/sys/linux/tokio_source.rs b/cros_async/src/sys/linux/tokio_source.rs new file mode 100644 index 000000000..4a8471296 --- /dev/null +++ b/cros_async/src/sys/linux/tokio_source.rs @@ -0,0 +1,405 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::io; +use std::os::fd::AsRawFd; +use std::os::fd::OwnedFd; +use std::os::fd::RawFd; +use std::sync::Arc; + +use base::add_fd_flags; +use base::clone_descriptor; +use base::linux::fallocate; +use base::linux::FallocateMode; +use base::AsRawDescriptor; +use base::VolatileSlice; +use tokio::io::unix::AsyncFd; + +use crate::mem::MemRegion; +use crate::AsyncError; +use crate::AsyncResult; +use crate::BackingMemory; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Failed to copy the FD for the polling context: '{0}'")] + DuplicatingFd(base::Error), + #[error("Failed to punch hole in file: '{0}'.")] + Fallocate(base::Error), + #[error("Failed to fdatasync: '{0}'")] + Fdatasync(io::Error), + #[error("Failed to fsync: '{0}'")] + Fsync(io::Error), + #[error("Failed to join task: '{0}'")] + Join(tokio::task::JoinError), + #[error("Cannot wait on file descriptor")] + NonWaitable, + #[error("Failed to read: '{0}'")] + Read(io::Error), + #[error("Failed to set nonblocking: '{0}'")] + SettingNonBlocking(base::Error), + #[error("Tokio Async FD error: '{0}'")] + TokioAsyncFd(io::Error), + #[error("Failed to write: '{0}'")] + Write(io::Error), +} + +impl From<Error> for io::Error { + fn from(e: Error) -> Self { + use Error::*; + match e { + DuplicatingFd(e) => e.into(), + Fallocate(e) => e.into(), + Fdatasync(e) => e, + Fsync(e) => e, + Join(e) => io::Error::new(io::ErrorKind::Other, e), + NonWaitable => io::Error::new(io::ErrorKind::Other, e), + Read(e) => e, + SettingNonBlocking(e) => e.into(), + TokioAsyncFd(e) => e, + Write(e) => e, + } + } +} + +enum FdType { + Async(AsyncFd<Arc<OwnedFd>>), + Blocking(Arc<OwnedFd>), +} + +impl AsRawFd for FdType { + fn as_raw_fd(&self) -> RawFd { + match self { + FdType::Async(async_fd) => async_fd.as_raw_fd(), + FdType::Blocking(blocking) => blocking.as_raw_fd(), + } + } +} + +impl From<Error> for AsyncError { + fn from(e: Error) -> AsyncError { + AsyncError::SysVariants(e.into()) + } +} + +fn do_fdatasync(raw: Arc<OwnedFd>) -> io::Result<()> { + let fd = raw.as_raw_fd(); + // SAFETY: we partially own `raw` + match unsafe { libc::fdatasync(fd) } { + 0 => Ok(()), + _ => Err(io::Error::last_os_error()), + } +} + +fn do_fsync(raw: Arc<OwnedFd>) -> io::Result<()> { + let fd = raw.as_raw_fd(); + // SAFETY: we partially own `raw` + match unsafe { libc::fsync(fd) } { + 0 => Ok(()), + _ => Err(io::Error::last_os_error()), + } +} + +fn do_read_to_mem( + raw: Arc<OwnedFd>, + file_offset: Option<u64>, + io_vecs: &Vec<VolatileSlice>, +) -> io::Result<usize> { + let ptr = io_vecs.as_ptr() as *const libc::iovec; + let len = io_vecs.len() as i32; + let fd = raw.as_raw_fd(); + let res = match file_offset { + // SAFETY: we partially own `raw`, `io_vecs` is validated + Some(off) => unsafe { libc::preadv64(fd, ptr, len, off as libc::off64_t) }, + // SAFETY: we partially own `raw`, `io_vecs` is validated + None => unsafe { libc::readv(fd, ptr, len) }, + }; + match res { + r if r >= 0 => Ok(res as usize), + _ => Err(io::Error::last_os_error()), + } +} +fn do_read_to_vec( + raw: Arc<OwnedFd>, + file_offset: Option<u64>, + vec: &mut Vec<u8>, +) -> io::Result<usize> { + let fd = raw.as_raw_fd(); + let ptr = vec.as_mut_ptr() as *mut libc::c_void; + let res = match file_offset { + // SAFETY: we partially own `raw`, `ptr` has space up to vec.len() + Some(off) => unsafe { libc::pread64(fd, ptr, vec.len(), off as libc::off64_t) }, + // SAFETY: we partially own `raw`, `ptr` has space up to vec.len() + None => unsafe { libc::read(fd, ptr, vec.len()) }, + }; + match res { + r if r >= 0 => Ok(res as usize), + _ => Err(io::Error::last_os_error()), + } +} + +fn do_write_from_vec( + raw: Arc<OwnedFd>, + file_offset: Option<u64>, + vec: &Vec<u8>, +) -> io::Result<usize> { + let fd = raw.as_raw_fd(); + let ptr = vec.as_ptr() as *const libc::c_void; + let res = match file_offset { + // SAFETY: we partially own `raw`, `ptr` has data up to vec.len() + Some(off) => unsafe { libc::pwrite64(fd, ptr, vec.len(), off as libc::off64_t) }, + // SAFETY: we partially own `raw`, `ptr` has data up to vec.len() + None => unsafe { libc::write(fd, ptr, vec.len()) }, + }; + match res { + r if r >= 0 => Ok(res as usize), + _ => Err(io::Error::last_os_error()), + } +} + +fn do_write_from_mem( + raw: Arc<OwnedFd>, + file_offset: Option<u64>, + io_vecs: &Vec<VolatileSlice>, +) -> io::Result<usize> { + let ptr = io_vecs.as_ptr() as *const libc::iovec; + let len = io_vecs.len() as i32; + let fd = raw.as_raw_fd(); + let res = match file_offset { + // SAFETY: we partially own `raw`, `io_vecs` is validated + Some(off) => unsafe { libc::pwritev64(fd, ptr, len, off as libc::off64_t) }, + // SAFETY: we partially own `raw`, `io_vecs` is validated + None => unsafe { libc::writev(fd, ptr, len) }, + }; + match res { + r if r >= 0 => Ok(res as usize), + _ => Err(io::Error::last_os_error()), + } +} + +pub struct TokioSource<T> { + fd: FdType, + inner: T, + runtime: tokio::runtime::Handle, +} +impl<T: AsRawDescriptor> TokioSource<T> { + pub fn new(inner: T, runtime: tokio::runtime::Handle) -> Result<TokioSource<T>, Error> { + let _guard = runtime.enter(); // Required for AsyncFd + let safe_fd = clone_descriptor(&inner).map_err(Error::DuplicatingFd)?; + let fd_arc: Arc<OwnedFd> = Arc::new(safe_fd.into()); + let fd = match AsyncFd::new(fd_arc.clone()) { + Ok(async_fd) => { + add_fd_flags(async_fd.get_ref().as_raw_descriptor(), libc::O_NONBLOCK) + .map_err(Error::SettingNonBlocking)?; + FdType::Async(async_fd) + } + Err(e) if e.kind() == io::ErrorKind::PermissionDenied => FdType::Blocking(fd_arc), + Err(e) => return Err(Error::TokioAsyncFd(e)), + }; + Ok(TokioSource { fd, inner, runtime }) + } + + pub fn as_source(&self) -> &T { + &self.inner + } + + pub fn as_source_mut(&mut self) -> &mut T { + &mut self.inner + } + + fn clone_fd(&self) -> Arc<OwnedFd> { + match &self.fd { + FdType::Async(async_fd) => async_fd.get_ref().clone(), + FdType::Blocking(blocking) => blocking.clone(), + } + } + + pub async fn fdatasync(&self) -> AsyncResult<()> { + let fd = self.clone_fd(); + Ok(self + .runtime + .spawn_blocking(move || do_fdatasync(fd)) + .await + .map_err(Error::Join)? + .map_err(Error::Fdatasync)?) + } + + pub async fn fsync(&self) -> AsyncResult<()> { + let fd = self.clone_fd(); + Ok(self + .runtime + .spawn_blocking(move || do_fsync(fd)) + .await + .map_err(Error::Join)? + .map_err(Error::Fsync)?) + } + + pub fn into_source(self) -> T { + self.inner + } + + pub async fn read_to_vec( + &self, + file_offset: Option<u64>, + mut vec: Vec<u8>, + ) -> AsyncResult<(usize, Vec<u8>)> { + Ok(match &self.fd { + FdType::Async(async_fd) => { + let res = async_fd + .async_io(tokio::io::Interest::READABLE, |fd| { + do_read_to_vec(fd.clone(), file_offset, &mut vec) + }) + .await + .map_err(AsyncError::Io)?; + (res, vec) + } + FdType::Blocking(blocking) => { + let fd = blocking.clone(); + self.runtime + .spawn_blocking(move || { + let size = do_read_to_vec(fd, file_offset, &mut vec)?; + Ok((size, vec)) + }) + .await + .map_err(Error::Join)? + .map_err(Error::Read)? + } + }) + } + + pub async fn read_to_mem( + &self, + file_offset: Option<u64>, + mem: Arc<dyn BackingMemory + Send + Sync>, + mem_offsets: impl IntoIterator<Item = MemRegion>, + ) -> AsyncResult<usize> { + let mem_offsets_vec: Vec<MemRegion> = mem_offsets.into_iter().collect(); + Ok(match &self.fd { + FdType::Async(async_fd) => { + let iovecs = mem_offsets_vec + .into_iter() + .filter_map(|mem_range| mem.get_volatile_slice(mem_range).ok()) + .collect::<Vec<VolatileSlice>>(); + async_fd + .async_io(tokio::io::Interest::READABLE, |fd| { + do_read_to_mem(fd.clone(), file_offset, &iovecs) + }) + .await + .map_err(AsyncError::Io)? + } + FdType::Blocking(blocking) => { + let fd = blocking.clone(); + self.runtime + .spawn_blocking(move || { + let iovecs = mem_offsets_vec + .into_iter() + .filter_map(|mem_range| mem.get_volatile_slice(mem_range).ok()) + .collect::<Vec<VolatileSlice>>(); + do_read_to_mem(fd, file_offset, &iovecs) + }) + .await + .map_err(Error::Join)? + .map_err(Error::Read)? + } + }) + } + + pub async fn punch_hole(&self, file_offset: u64, len: u64) -> AsyncResult<()> { + let fd = self.clone_fd(); + Ok(self + .runtime + .spawn_blocking(move || fallocate(&*fd, FallocateMode::PunchHole, file_offset, len)) + .await + .map_err(Error::Join)? + .map_err(Error::Fallocate)?) + } + + pub async fn wait_readable(&self) -> AsyncResult<()> { + match &self.fd { + FdType::Async(async_fd) => async_fd + .readable() + .await + .map_err(crate::AsyncError::Io)? + .retain_ready(), + FdType::Blocking(_) => return Err(Error::NonWaitable.into()), + } + Ok(()) + } + + pub async fn write_from_mem( + &self, + file_offset: Option<u64>, + mem: Arc<dyn BackingMemory + Send + Sync>, + mem_offsets: impl IntoIterator<Item = MemRegion>, + ) -> AsyncResult<usize> { + let mem_offsets_vec: Vec<MemRegion> = mem_offsets.into_iter().collect(); + Ok(match &self.fd { + FdType::Async(async_fd) => { + let iovecs = mem_offsets_vec + .into_iter() + .filter_map(|mem_range| mem.get_volatile_slice(mem_range).ok()) + .collect::<Vec<VolatileSlice>>(); + async_fd + .async_io(tokio::io::Interest::WRITABLE, |fd| { + do_write_from_mem(fd.clone(), file_offset, &iovecs) + }) + .await + .map_err(AsyncError::Io)? + } + FdType::Blocking(blocking) => { + let fd = blocking.clone(); + self.runtime + .spawn_blocking(move || { + let iovecs = mem_offsets_vec + .into_iter() + .filter_map(|mem_range| mem.get_volatile_slice(mem_range).ok()) + .collect::<Vec<VolatileSlice>>(); + do_write_from_mem(fd, file_offset, &iovecs.clone()) + }) + .await + .map_err(Error::Join)? + .map_err(Error::Read)? + } + }) + } + + pub async fn write_from_vec( + &self, + file_offset: Option<u64>, + vec: Vec<u8>, + ) -> AsyncResult<(usize, Vec<u8>)> { + Ok(match &self.fd { + FdType::Async(async_fd) => { + let res = async_fd + .async_io(tokio::io::Interest::WRITABLE, |fd| { + do_write_from_vec(fd.clone(), file_offset, &vec) + }) + .await + .map_err(AsyncError::Io)?; + (res, vec) + } + FdType::Blocking(blocking) => { + let fd = blocking.clone(); + self.runtime + .spawn_blocking(move || { + let size = do_write_from_vec(fd.clone(), file_offset, &vec)?; + Ok((size, vec)) + }) + .await + .map_err(Error::Join)? + .map_err(Error::Read)? + } + }) + } + + pub async fn write_zeroes_at(&self, file_offset: u64, len: u64) -> AsyncResult<()> { + let fd = self.clone_fd(); + Ok(self + .runtime + .spawn_blocking(move || fallocate(&*fd, FallocateMode::ZeroRange, file_offset, len)) + .await + .map_err(Error::Join)? + .map_err(Error::Fallocate)?) + } +} diff --git a/cros_async/src/sys/windows.rs b/cros_async/src/sys/windows.rs index b040309dd..315426f33 100644 --- a/cros_async/src/sys/windows.rs +++ b/cros_async/src/sys/windows.rs @@ -11,6 +11,8 @@ pub mod handle_source; mod io_completion_port; pub mod overlapped_source; mod timer; +#[cfg(feature = "tokio")] +pub mod tokio_source; pub mod wait_for_handle; pub use error::AsyncErrorSys; diff --git a/cros_async/src/sys/windows/error.rs b/cros_async/src/sys/windows/error.rs index f0437457a..74c4c04dc 100644 --- a/cros_async/src/sys/windows/error.rs +++ b/cros_async/src/sys/windows/error.rs @@ -13,6 +13,9 @@ pub enum AsyncErrorSys { HandleSource(#[from] super::handle_source::Error), #[error("An error with a handle source: {0}")] OverlappedSource(#[from] super::overlapped_source::Error), + #[cfg(feature = "tokio")] + #[error("Tokio source error: {0}")] + Tokio(#[from] super::tokio_source::Error), } impl From<AsyncErrorSys> for io::Error { @@ -21,6 +24,8 @@ impl From<AsyncErrorSys> for io::Error { AsyncErrorSys::HandleExecutor(e) => e.into(), AsyncErrorSys::HandleSource(e) => e.into(), AsyncErrorSys::OverlappedSource(e) => e.into(), + #[cfg(feature = "tokio")] + AsyncErrorSys::Tokio(e) => e.into(), } } } diff --git a/cros_async/src/sys/windows/executor.rs b/cros_async/src/sys/windows/executor.rs index 3d9053b22..31ba4bfa5 100644 --- a/cros_async/src/sys/windows/executor.rs +++ b/cros_async/src/sys/windows/executor.rs @@ -2,15 +2,25 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use serde::Deserialize; -use serde::Serialize; - /// An enum to express the kind of the backend of `Executor` -#[derive( - Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, serde_keyvalue::FromKeyValues, -)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Deserialize, serde_keyvalue::FromKeyValues)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] pub enum ExecutorKindSys { Handle, - Overlapped, + Overlapped { concurrency: Option<u32> }, +} + +impl serde::Serialize for ExecutorKindSys { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + serializer.serialize_str(&match self { + ExecutorKindSys::Handle => "handle".to_string(), + ExecutorKindSys::Overlapped { concurrency: None } => "overlapped".to_string(), + ExecutorKindSys::Overlapped { + concurrency: Some(n), + } => format!("overlapped,concurrency={}", n), + }) + } } diff --git a/cros_async/src/sys/windows/handle_executor.rs b/cros_async/src/sys/windows/handle_executor.rs index 614730801..c5c4fe522 100644 --- a/cros_async/src/sys/windows/handle_executor.rs +++ b/cros_async/src/sys/windows/handle_executor.rs @@ -95,7 +95,7 @@ impl HandleReactor { }) } - fn new() -> Result<Self> { + pub fn new() -> Result<Self> { Self::new_with(DEFAULT_IO_CONCURRENCY) } diff --git a/cros_async/src/sys/windows/tokio_source.rs b/cros_async/src/sys/windows/tokio_source.rs new file mode 100644 index 000000000..676edb7c2 --- /dev/null +++ b/cros_async/src/sys/windows/tokio_source.rs @@ -0,0 +1,291 @@ +// Copyright 2022 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::fs::File; +use std::io; +use std::io::Read; +use std::io::Seek; +use std::io::SeekFrom; +use std::io::Write; +use std::mem::ManuallyDrop; +use std::sync::Arc; + +use base::AsRawDescriptor; +use base::FileReadWriteAtVolatile; +use base::FileReadWriteVolatile; +use base::FromRawDescriptor; +use base::PunchHole; +use base::VolatileSlice; +use base::WriteZeroesAt; +use smallvec::SmallVec; +use sync::Mutex; + +use crate::mem::MemRegion; +use crate::AsyncError; +use crate::AsyncResult; +use crate::BackingMemory; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("An error occurred trying to seek: {0}.")] + IoSeekError(io::Error), + #[error("An error occurred trying to read: {0}.")] + IoReadError(io::Error), + #[error("An error occurred trying to write: {0}.")] + IoWriteError(io::Error), + #[error("An error occurred trying to flush: {0}.")] + IoFlushError(io::Error), + #[error("An error occurred trying to punch hole: {0}.")] + IoPunchHoleError(io::Error), + #[error("An error occurred trying to write zeroes: {0}.")] + IoWriteZeroesError(io::Error), + #[error("Failed to join task: '{0}'")] + Join(tokio::task::JoinError), + #[error("An error occurred trying to duplicate source handles: {0}.")] + HandleDuplicationFailed(io::Error), + #[error("An error occurred trying to wait on source handles: {0}.")] + HandleWaitFailed(base::Error), + #[error("An error occurred trying to get a VolatileSlice into BackingMemory: {0}.")] + BackingMemoryVolatileSliceFetchFailed(crate::mem::Error), + #[error("TokioSource is gone, so no handles are available to fulfill the IO request.")] + NoTokioSource, + #[error("Operation on TokioSource is cancelled.")] + OperationCancelled, + #[error("Operation on TokioSource was aborted (unexpected).")] + OperationAborted, +} + +impl From<Error> for AsyncError { + fn from(e: Error) -> AsyncError { + AsyncError::SysVariants(e.into()) + } +} + +impl From<Error> for io::Error { + fn from(e: Error) -> Self { + use Error::*; + match e { + IoSeekError(e) => e, + IoReadError(e) => e, + IoWriteError(e) => e, + IoFlushError(e) => e, + IoPunchHoleError(e) => e, + IoWriteZeroesError(e) => e, + Join(e) => io::Error::new(io::ErrorKind::Other, e), + HandleDuplicationFailed(e) => e, + HandleWaitFailed(e) => e.into(), + BackingMemoryVolatileSliceFetchFailed(e) => io::Error::new(io::ErrorKind::Other, e), + NoTokioSource => io::Error::new(io::ErrorKind::Other, NoTokioSource), + OperationCancelled => io::Error::new(io::ErrorKind::Interrupted, OperationCancelled), + OperationAborted => io::Error::new(io::ErrorKind::Interrupted, OperationAborted), + } + } +} + +pub type Result<T> = std::result::Result<T, Error>; + +pub struct TokioSource<T: AsRawDescriptor> { + source: Option<T>, + source_file: Arc<Mutex<Option<ManuallyDrop<File>>>>, + runtime: tokio::runtime::Handle, +} + +impl<T: AsRawDescriptor> TokioSource<T> { + pub(crate) fn new(source: T, runtime: tokio::runtime::Handle) -> Result<TokioSource<T>> { + let descriptor = source.as_raw_descriptor(); + // SAFETY: The Drop implementation makes sure `source` outlives `source_file`. + let source_file = unsafe { ManuallyDrop::new(File::from_raw_descriptor(descriptor)) }; + Ok(Self { + source: Some(source), + source_file: Arc::new(Mutex::new(Some(source_file))), + runtime, + }) + } + #[inline] + fn get_slices( + mem: &Arc<dyn BackingMemory + Send + Sync>, + mem_offsets: Vec<MemRegion>, + ) -> Result<SmallVec<[VolatileSlice<'_>; 16]>> { + mem_offsets + .into_iter() + .map(|region| { + mem.get_volatile_slice(region) + .map_err(Error::BackingMemoryVolatileSliceFetchFailed) + }) + .collect::<Result<SmallVec<[VolatileSlice; 16]>>>() + } + pub fn as_source(&self) -> &T { + self.source.as_ref().unwrap() + } + pub fn as_source_mut(&mut self) -> &mut T { + self.source.as_mut().unwrap() + } + pub async fn fdatasync(&self) -> AsyncResult<()> { + // TODO(b/282003931): Fall back to regular fsync. + self.fsync().await + } + pub async fn fsync(&self) -> AsyncResult<()> { + let source_file = self.source_file.clone(); + Ok(self + .runtime + .spawn_blocking(move || { + source_file + .lock() + .as_mut() + .ok_or(Error::OperationCancelled)? + .flush() + .map_err(Error::IoFlushError) + }) + .await + .map_err(Error::Join)??) + } + pub fn into_source(mut self) -> T { + self.source_file.lock().take(); + self.source.take().unwrap() + } + pub async fn punch_hole(&self, file_offset: u64, len: u64) -> AsyncResult<()> { + let source_file = self.source_file.clone(); + Ok(self + .runtime + .spawn_blocking(move || { + source_file + .lock() + .as_mut() + .ok_or(Error::OperationCancelled)? + .punch_hole(file_offset, len) + .map_err(Error::IoPunchHoleError) + }) + .await + .map_err(Error::Join)??) + } + pub async fn read_to_mem( + &self, + file_offset: Option<u64>, + mem: Arc<dyn BackingMemory + Send + Sync>, + mem_offsets: impl IntoIterator<Item = MemRegion>, + ) -> AsyncResult<usize> { + let mem_offsets = mem_offsets.into_iter().collect(); + let source_file = self.source_file.clone(); + Ok(self + .runtime + .spawn_blocking(move || { + let mut file_lock = source_file.lock(); + let file = file_lock.as_mut().ok_or(Error::OperationCancelled)?; + let memory_slices = Self::get_slices(&mem, mem_offsets)?; + match file_offset { + Some(file_offset) => file + .read_vectored_at_volatile(memory_slices.as_slice(), file_offset) + .map_err(Error::IoReadError), + None => file + .read_vectored_volatile(memory_slices.as_slice()) + .map_err(Error::IoReadError), + } + }) + .await + .map_err(Error::Join)??) + } + pub async fn read_to_vec( + &self, + file_offset: Option<u64>, + mut vec: Vec<u8>, + ) -> AsyncResult<(usize, Vec<u8>)> { + let source_file = self.source_file.clone(); + Ok(self + .runtime + .spawn_blocking(move || { + let mut file_lock = source_file.lock(); + let file = file_lock.as_mut().ok_or(Error::OperationCancelled)?; + if let Some(file_offset) = file_offset { + file.seek(SeekFrom::Start(file_offset)) + .map_err(Error::IoSeekError)?; + } + Ok::<(usize, Vec<u8>), Error>(( + file.read(vec.as_mut_slice()).map_err(Error::IoReadError)?, + vec, + )) + }) + .await + .map_err(Error::Join)??) + } + pub async fn wait_readable(&self) -> AsyncResult<()> { + unimplemented!(); + } + pub async fn wait_for_handle(&self) -> AsyncResult<()> { + let waiter = super::wait_for_handle::WaitForHandle::new(self.source.as_ref().unwrap()); + Ok(waiter.await?) + } + pub async fn write_from_mem( + &self, + file_offset: Option<u64>, + mem: Arc<dyn BackingMemory + Send + Sync>, + mem_offsets: impl IntoIterator<Item = MemRegion>, + ) -> AsyncResult<usize> { + let mem_offsets = mem_offsets.into_iter().collect(); + let source_file = self.source_file.clone(); + Ok(self + .runtime + .spawn_blocking(move || { + let mut file_lock = source_file.lock(); + let file = file_lock.as_mut().ok_or(Error::OperationCancelled)?; + let memory_slices = Self::get_slices(&mem, mem_offsets)?; + match file_offset { + Some(file_offset) => file + .write_vectored_at_volatile(memory_slices.as_slice(), file_offset) + .map_err(Error::IoWriteError), + None => file + .write_vectored_volatile(memory_slices.as_slice()) + .map_err(Error::IoWriteError), + } + }) + .await + .map_err(Error::Join)??) + } + pub async fn write_from_vec( + &self, + file_offset: Option<u64>, + vec: Vec<u8>, + ) -> AsyncResult<(usize, Vec<u8>)> { + let source_file = self.source_file.clone(); + Ok(self + .runtime + .spawn_blocking(move || { + let mut file_lock = source_file.lock(); + let file = file_lock.as_mut().ok_or(Error::OperationCancelled)?; + if let Some(file_offset) = file_offset { + file.seek(SeekFrom::Start(file_offset)) + .map_err(Error::IoSeekError)?; + } + Ok::<(usize, Vec<u8>), Error>(( + file.write(vec.as_slice()).map_err(Error::IoWriteError)?, + vec, + )) + }) + .await + .map_err(Error::Join)??) + } + pub async fn write_zeroes_at(&self, file_offset: u64, len: u64) -> AsyncResult<()> { + let source_file = self.source_file.clone(); + Ok(self + .runtime + .spawn_blocking(move || { + // ZeroRange calls `punch_hole` which doesn't extend the File size if it needs to. + // Will fix if it becomes a problem. + source_file + .lock() + .as_mut() + .ok_or(Error::OperationCancelled)? + .write_zeroes_at(file_offset, len as usize) + .map_err(Error::IoWriteZeroesError) + .map(|_| ()) + }) + .await + .map_err(Error::Join)??) + } +} +impl<T: AsRawDescriptor> Drop for TokioSource<T> { + fn drop(&mut self) { + let mut source_file = self.source_file.lock(); + source_file.take(); + } +} diff --git a/cros_async/src/tokio_executor.rs b/cros_async/src/tokio_executor.rs new file mode 100644 index 000000000..b84fccadb --- /dev/null +++ b/cros_async/src/tokio_executor.rs @@ -0,0 +1,170 @@ +// Copyright 2023 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::sync::OnceLock; + +use base::AsRawDescriptors; +use base::RawDescriptor; +use tokio::runtime::Runtime; +use tokio::task::LocalSet; + +use crate::sys::platform::tokio_source::TokioSource; +use crate::AsyncError; +use crate::AsyncResult; +use crate::ExecutorTrait; +use crate::IntoAsync; +use crate::IoSource; +use crate::TaskHandle; + +mod send_wrapper { + use std::thread; + + #[derive(Clone)] + pub(super) struct SendWrapper<T> { + instance: T, + thread_id: thread::ThreadId, + } + + impl<T> SendWrapper<T> { + pub(super) fn new(instance: T) -> SendWrapper<T> { + SendWrapper { + instance, + thread_id: thread::current().id(), + } + } + } + + // SAFETY: panics when the value is accessed on the wrong thread. + unsafe impl<T> Send for SendWrapper<T> {} + // SAFETY: panics when the value is accessed on the wrong thread. + unsafe impl<T> Sync for SendWrapper<T> {} + + impl<T> Drop for SendWrapper<T> { + fn drop(&mut self) { + if self.thread_id != thread::current().id() { + panic!("SendWrapper value was dropped on the wrong thread"); + } + } + } + + impl<T> std::ops::Deref for SendWrapper<T> { + type Target = T; + + fn deref(&self) -> &T { + if self.thread_id != thread::current().id() { + panic!("SendWrapper value was accessed on the wrong thread"); + } + &self.instance + } + } +} + +#[derive(Clone)] +pub struct TokioExecutor { + runtime: Arc<Runtime>, + local_set: Arc<OnceLock<send_wrapper::SendWrapper<LocalSet>>>, +} + +impl TokioExecutor { + pub fn new() -> AsyncResult<Self> { + Ok(TokioExecutor { + runtime: Arc::new(Runtime::new().map_err(AsyncError::Io)?), + local_set: Arc::new(OnceLock::new()), + }) + } +} + +impl ExecutorTrait for TokioExecutor { + fn async_from<'a, F: IntoAsync + 'a>(&self, f: F) -> AsyncResult<IoSource<F>> { + Ok(IoSource::Tokio(TokioSource::new( + f, + self.runtime.handle().clone(), + )?)) + } + + fn run_until<F: Future>(&self, f: F) -> AsyncResult<F::Output> { + let local_set = self + .local_set + .get_or_init(|| send_wrapper::SendWrapper::new(LocalSet::new())); + Ok(self + .runtime + .block_on(async { local_set.run_until(f).await })) + } + + fn spawn<F>(&self, f: F) -> TaskHandle<F::Output> + where + F: Future + Send + 'static, + F::Output: Send + 'static, + { + TaskHandle::Tokio(TokioTaskHandle { + join_handle: Some(self.runtime.spawn(f)), + }) + } + + fn spawn_blocking<F, R>(&self, f: F) -> TaskHandle<R> + where + F: FnOnce() -> R + Send + 'static, + R: Send + 'static, + { + TaskHandle::Tokio(TokioTaskHandle { + join_handle: Some(self.runtime.spawn_blocking(f)), + }) + } + + fn spawn_local<F>(&self, f: F) -> TaskHandle<F::Output> + where + F: Future + 'static, + F::Output: 'static, + { + let local_set = self + .local_set + .get_or_init(|| send_wrapper::SendWrapper::new(LocalSet::new())); + TaskHandle::Tokio(TokioTaskHandle { + join_handle: Some(local_set.spawn_local(f)), + }) + } +} + +impl AsRawDescriptors for TokioExecutor { + fn as_raw_descriptors(&self) -> Vec<RawDescriptor> { + todo!(); + } +} + +pub struct TokioTaskHandle<T> { + join_handle: Option<tokio::task::JoinHandle<T>>, +} +impl<R> TokioTaskHandle<R> { + pub async fn cancel(mut self) -> Option<R> { + match self.join_handle.take() { + Some(handle) => { + handle.abort(); + handle.await.ok() + } + None => None, + } + } + pub fn detach(mut self) { + self.join_handle.take(); + } +} +impl<R: 'static> Future for TokioTaskHandle<R> { + type Output = R; + fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context) -> std::task::Poll<Self::Output> { + let self_mut = self.get_mut(); + Pin::new(self_mut.join_handle.as_mut().unwrap()) + .poll(cx) + .map(|v| v.unwrap()) + } +} +impl<T> std::ops::Drop for TokioTaskHandle<T> { + fn drop(&mut self) { + if let Some(handle) = self.join_handle.take() { + handle.abort() + } + } +} diff --git a/cros_fdt/Android.bp b/cros_fdt/Android.bp index c99b584b0..73c4d15ae 100644 --- a/cros_fdt/Android.bp +++ b/cros_fdt/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/cros_tracing/Android.bp b/cros_tracing/Android.bp index ed18c8dcd..b132421c1 100644 --- a/cros_tracing/Android.bp +++ b/cros_tracing/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/cros_tracing_types/Android.bp b/cros_tracing_types/Android.bp index ca29fd7ec..fb9a25ba5 100644 --- a/cros_tracing_types/Android.bp +++ b/cros_tracing_types/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/crosvm_cli/Android.bp b/crosvm_cli/Android.bp index 3ce8499db..ba356984c 100644 --- a/crosvm_cli/Android.bp +++ b/crosvm_cli/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/crosvm_control/Android.bp b/crosvm_control/Android.bp index bf87a302b..abed499c9 100644 --- a/crosvm_control/Android.bp +++ b/crosvm_control/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/devices/Android.bp b/devices/Android.bp index 26c7ecc78..efed696f7 100644 --- a/devices/Android.bp +++ b/devices/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { @@ -67,6 +68,7 @@ rust_test { "liblibc", "liblinux_input_sys", "libmemoffset", + "libmetrics", "libminijail_rust", "libnamed_lock", "libnet_sys", @@ -159,6 +161,7 @@ rust_test { "liblibc", "liblinux_input_sys", "libmemoffset", + "libmetrics", "libminijail_rust", "libnamed_lock", "libnet_sys", @@ -243,6 +246,7 @@ rust_library { "liblibc", "liblinux_input_sys", "libmemoffset", + "libmetrics", "libminijail_rust", "libnet_sys", "libnet_util", diff --git a/devices/Cargo.toml b/devices/Cargo.toml index 6524dd7de..de3b798c4 100644 --- a/devices/Cargo.toml +++ b/devices/Cargo.toml @@ -66,6 +66,7 @@ libc = "*" libvda = { path = "../media/libvda", optional = true } linux_input_sys = { path = "../linux_input_sys" } memoffset = { version = "0.6" } +metrics = { path = "../metrics" } net_util = { path = "../net_util" } num-traits = "0.2" once_cell = "1.7.2" @@ -102,7 +103,6 @@ vhost = { path = "../vhost" } [target.'cfg(windows)'.dependencies] broker_ipc = { path = "../broker_ipc" } -metrics = { path = "../metrics" } tube_transporter = { path = "../tube_transporter" } win_audio = { path = "../win_audio"} win_util = { path = "../win_util"} diff --git a/devices/src/acpi.rs b/devices/src/acpi.rs index e4657dcca..7244e0290 100644 --- a/devices/src/acpi.rs +++ b/devices/src/acpi.rs @@ -7,6 +7,7 @@ use std::str::FromStr; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; use std::sync::Arc; +use std::time::Instant; use acpi_tables::aml; use acpi_tables::aml::Aml; @@ -23,6 +24,8 @@ use base::Tube; use base::VmEventType; use base::WaitContext; use base::WorkerThread; +use metrics::log_metric; +use metrics::MetricEventType; use serde::Deserialize; use serde::Serialize; use sync::Mutex; @@ -663,38 +666,50 @@ pub struct PmWakeupEvent { active: AtomicBool, vm_control_tube: Arc<Mutex<Tube>>, pm_config: Arc<Mutex<PmConfig>>, - debug_label: String, + metrics_event: MetricEventType, + armed_time: Arc<Mutex<Instant>>, } impl PmWakeupEvent { pub fn new( vm_control_tube: Arc<Mutex<Tube>>, pm_config: Arc<Mutex<PmConfig>>, - debug_label: String, + metrics_event: MetricEventType, ) -> Self { Self { active: AtomicBool::new(false), vm_control_tube, pm_config, - debug_label, + metrics_event, + // Not actually armed, but simpler than wrapping with an Option. + armed_time: Arc::new(Mutex::new(Instant::now())), } } pub fn trigger_wakeup(&self) -> anyhow::Result<()> { if self.active.load(Ordering::SeqCst) && self.pm_config.lock().should_trigger_pme() { + let elapsed = self.armed_time.lock().elapsed().as_millis(); + log_metric( + self.metrics_event.clone(), + elapsed.try_into().unwrap_or(i64::MAX), + ); + let tube = self.vm_control_tube.lock(); tube.send(&VmRequest::Gpe(PM_WAKEUP_GPIO)) - .with_context(|| format!("{} failed to send pme", self.debug_label))?; + .with_context(|| format!("{:?} failed to send pme", self.metrics_event))?; match tube.recv::<VmResponse>() { Ok(VmResponse::Ok) => (), - e => bail!("{} pme failure {:?}", self.debug_label, e), + e => bail!("{:?} pme failure {:?}", self.metrics_event, e), } } Ok(()) } pub fn set_active(&self, active: bool) { - self.active.store(active, Ordering::SeqCst) + self.active.store(active, Ordering::SeqCst); + if active { + *self.armed_time.lock() = Instant::now(); + } } } diff --git a/devices/src/cmos.rs b/devices/src/cmos.rs index 0c44ac1b0..0f99c1615 100644 --- a/devices/src/cmos.rs +++ b/devices/src/cmos.rs @@ -5,6 +5,7 @@ use std::cmp::min; use std::sync::Arc; use std::time::Duration; +use std::time::Instant; use anyhow::anyhow; use anyhow::Context; @@ -24,6 +25,8 @@ use chrono::Datelike; use chrono::TimeZone; use chrono::Timelike; use chrono::Utc; +use metrics::log_metric; +use metrics::MetricEventType; use serde::Deserialize; use serde::Serialize; use sync::Mutex; @@ -86,17 +89,35 @@ pub struct Cmos { alarm_fn: Option<AlarmFn>, #[serde(skip_serializing)] // skip serializing the worker thread worker: Option<WorkerThread<AlarmFn>>, + #[serde(skip_serializing)] // skip serializing the armed time + armed_time: Option<Arc<Mutex<Instant>>>, } struct AlarmFn { irq: IrqEdgeEvent, vm_control: Tube, + armed_time: Arc<Mutex<Instant>>, } impl AlarmFn { + fn new(irq: IrqEdgeEvent, vm_control: Tube) -> Self { + Self { + irq, + vm_control, + // Not actually armed, but simpler than wrapping with an Option. + armed_time: Arc::new(Mutex::new(Instant::now())), + } + } + fn fire(&self) -> anyhow::Result<()> { self.irq.trigger().context("failed to trigger irq")?; + let elapsed = self.armed_time.lock().elapsed().as_millis(); + log_metric( + MetricEventType::RtcWakeup, + elapsed.try_into().unwrap_or(i64::MAX), + ); + // The Linux kernel expects wakeups to come via ACPI when ACPI is enabled. There's // no real way to determine that here, so just send this unconditionally. self.vm_control @@ -125,7 +146,7 @@ impl Cmos { mem_below_4g, mem_above_4g, now_fn, - Some(AlarmFn { irq, vm_control }), + Some(AlarmFn::new(irq, vm_control)), ) } @@ -153,6 +174,7 @@ impl Cmos { data[0x5c] = (high_mem >> 8) as u8; data[0x5d] = (high_mem >> 16) as u8; + let armed_time = alarm_fn.as_ref().map(|a| a.armed_time.clone()); Ok(Cmos { index: 0, data, @@ -161,6 +183,7 @@ impl Cmos { alarm_time: None, alarm_fn, worker: None, + armed_time, }) } @@ -202,6 +225,9 @@ impl Cmos { if self.alarm_fn.is_some() { self.spawn_worker(); } + if let Some(armed_time) = self.armed_time.as_ref() { + *armed_time.lock() = Instant::now(); + } let duration = target .signed_duration_since(now) @@ -690,10 +716,7 @@ mod tests { fn cmos_sleep_wake() { // 2000-01-02T03:04:05+00:00 let now_fn = || timestamp_to_datetime(946782245); - let alarm_fn = AlarmFn { - irq: IrqEdgeEvent::new().unwrap(), - vm_control: Tube::pair().unwrap().0, - }; + let alarm_fn = AlarmFn::new(IrqEdgeEvent::new().unwrap(), Tube::pair().unwrap().0); let mut cmos = Cmos::new_inner(1024, 0, now_fn, Some(alarm_fn)).unwrap(); // A date later this year diff --git a/devices/src/pci/pci_configuration.rs b/devices/src/pci/pci_configuration.rs index 3559082e2..e7a1b9149 100644 --- a/devices/src/pci/pci_configuration.rs +++ b/devices/src/pci/pci_configuration.rs @@ -833,7 +833,12 @@ impl PciConfiguration { .write_obj_volatile(value, reg_offset) .expect("bad register offset"); } - mmio_mapping.flush_uncached_guest_mapping(reg_offset) + if let Err(err) = mmio_mapping.flush_region(reg_offset, 4) { + error!( + "failed to flush write to pci mmio register ({}): {}", + reg_idx, err + ); + } } } @@ -1017,7 +1022,12 @@ impl PciCapMapping { mapping .write_obj_volatile(new_val, offset) .expect("memcpy failed"); - mapping.flush_uncached_guest_mapping(offset); + if let Err(err) = mapping.flush_region(offset, 4) { + error!( + "failed to flush write to pci cap in mmio register ({}): {}", + reg_idx, err + ); + } } } diff --git a/devices/src/pci/pci_root.rs b/devices/src/pci/pci_root.rs index a029bae9a..b6d8d035c 100644 --- a/devices/src/pci/pci_root.rs +++ b/devices/src/pci/pci_root.rs @@ -579,7 +579,9 @@ impl PciRootMmioState { mapping .write_obj_volatile(val, reg_offset) .expect("memcpy failed"); - mapping.flush_uncached_guest_mapping(reg_offset); + if let Err(err) = mapping.flush_region(reg_offset, 4) { + error!("failed to flush write to mfd bit: {}", err); + } } } } diff --git a/devices/src/serial_device.rs b/devices/src/serial_device.rs index 20454b522..dd4771161 100644 --- a/devices/src/serial_device.rs +++ b/devices/src/serial_device.rs @@ -188,6 +188,7 @@ impl SerialParameters { let evt = evt.try_clone().map_err(Error::CloneEvent)?; keep_rds.push(evt.as_raw_descriptor()); cros_tracing::push_descriptors!(keep_rds); + metrics::push_descriptors(keep_rds); let input: Option<Box<dyn SerialInput>> = if let Some(input_path) = &self.input { let input_path = input_path.as_path(); diff --git a/devices/src/virtio/async_device.rs b/devices/src/virtio/async_device.rs index 5e38324c2..ff9c45434 100644 --- a/devices/src/virtio/async_device.rs +++ b/devices/src/virtio/async_device.rs @@ -78,6 +78,7 @@ impl<T: 'static> AsyncQueueState<T> { /// /// Returns `true` if the queue was running, `false` if it wasn't. pub fn stop(&mut self) -> AsyncResult<bool> { + // TODO: schuffelen - All callers should use stop_async instead. match std::mem::replace(self, AsyncQueueState::Broken) { AsyncQueueState::Running((task, ex, handle)) => { // Abort the task and run it to completion to retrieve the queue's resource. @@ -92,4 +93,25 @@ impl<T: 'static> AsyncQueueState<T> { } } } + /// Stops a previously started queue. + /// + /// The executor on which the task has been started will be run if needed in order to retrieve + /// the queue's resource. + /// + /// Returns `true` if the queue was running, `false` if it wasn't. + pub async fn stop_async(&mut self) -> AsyncResult<bool> { + match std::mem::replace(self, AsyncQueueState::Broken) { + AsyncQueueState::Running((task, _, handle)) => { + // Abort the task and run it to completion to retrieve the queue's resource. + handle.abort(); + let resource = task.await; + *self = AsyncQueueState::Stopped(resource); + Ok(true) + } + state => { + *self = state; + Ok(false) + } + } + } } diff --git a/devices/src/virtio/block/mod.rs b/devices/src/virtio/block/mod.rs index d52c4fe2b..b06f31115 100644 --- a/devices/src/virtio/block/mod.rs +++ b/devices/src/virtio/block/mod.rs @@ -102,6 +102,7 @@ pub struct DiskOption { deserialize_with = "deserialize_disk_id" )] pub id: Option<[u8; DISK_ID_LEN]>, + // Deprecated: Use async_executor=overlapped[concurrency=N]" // camel_case variant allowed for backward compatibility. #[cfg(windows)] #[serde( @@ -451,6 +452,51 @@ mod tests { pci_address: None, } ); + let params = from_block_arg("/some/path.img,async-executor=overlapped").unwrap(); + assert_eq!( + params, + DiskOption { + path: "/some/path.img".into(), + read_only: false, + root: false, + sparse: true, + direct: false, + block_size: 512, + id: None, + io_concurrency: NonZeroU32::new(1).unwrap(), + multiple_workers: false, + async_executor: Some(ExecutorKindSys::Overlapped { concurrency: None }.into()), + packed_queue: false, + bootindex: None, + pci_address: None, + } + ); + let params = + from_block_arg("/some/path.img,async-executor=\"overlapped,concurrency=4\"") + .unwrap(); + assert_eq!( + params, + DiskOption { + path: "/some/path.img".into(), + read_only: false, + root: false, + sparse: true, + direct: false, + block_size: 512, + id: None, + io_concurrency: NonZeroU32::new(1).unwrap(), + multiple_workers: false, + async_executor: Some( + ExecutorKindSys::Overlapped { + concurrency: Some(4) + } + .into() + ), + packed_queue: false, + bootindex: None, + pci_address: None, + } + ); } // id diff --git a/devices/src/virtio/block/sys/windows.rs b/devices/src/virtio/block/sys/windows.rs index 4c215290f..7b66a1ab6 100644 --- a/devices/src/virtio/block/sys/windows.rs +++ b/devices/src/virtio/block/sys/windows.rs @@ -10,6 +10,7 @@ use anyhow::Context; use base::warn; use cros_async::sys::windows::ExecutorKindSys; use cros_async::Executor; +use cros_async::ExecutorKind; use winapi::um::winbase::FILE_FLAG_NO_BUFFERING; use winapi::um::winbase::FILE_FLAG_OVERLAPPED; use winapi::um::winnt::FILE_SHARE_READ; @@ -38,7 +39,13 @@ impl DiskOption { flags |= FILE_FLAG_NO_BUFFERING; } - if self.async_executor == Some(ExecutorKindSys::Overlapped.into()) { + let is_overlapped = matches!( + self.async_executor, + Some(ExecutorKind::SysVariants( + ExecutorKindSys::Overlapped { .. } + )) + ); + if is_overlapped { warn!("Opening disk file for overlapped IO"); flags |= FILE_FLAG_OVERLAPPED; } @@ -50,10 +57,7 @@ impl DiskOption { let file = open_option .open(&self.path) .context("Failed to open disk file")?; - let image_type = disk::detect_image_type( - &file, - self.async_executor == Some(ExecutorKindSys::Overlapped.into()), - )?; + let image_type = disk::detect_image_type(&file, is_overlapped)?; Ok(disk::create_disk_file_of_type( file, self.sparse, @@ -66,7 +70,12 @@ impl DiskOption { impl BlockAsync { pub fn create_executor(&self) -> Executor { - Executor::with_kind_and_concurrency(self.executor_kind, self.io_concurrency) - .expect("Failed to create an executor") + let mut kind = self.executor_kind; + if let ExecutorKind::SysVariants(ExecutorKindSys::Overlapped { concurrency }) = &mut kind { + if concurrency.is_none() { + *concurrency = Some(self.io_concurrency); + } + } + Executor::with_executor_kind(kind).expect("Failed to create an executor") } } diff --git a/devices/src/virtio/console/asynchronous.rs b/devices/src/virtio/console/asynchronous.rs index 46ca606b4..b98480b98 100644 --- a/devices/src/virtio/console/asynchronous.rs +++ b/devices/src/virtio/console/asynchronous.rs @@ -534,9 +534,15 @@ impl VirtioDevice for AsyncConsole { async_utils::await_and_exit(&ex, kill_evt).await?; let port = &mut console.port0; if let Some(input) = port.input.as_mut() { - input.stop().context("failed to stop rx queue")?; + input + .stop_async() + .await + .context("failed to stop rx queue")?; } - port.output.stop().context("failed to stop tx queue")?; + port.output + .stop_async() + .await + .context("failed to stop tx queue")?; Ok(console) })? diff --git a/devices/src/virtio/gpu/mod.rs b/devices/src/virtio/gpu/mod.rs index 911deb1da..c26bc0a67 100644 --- a/devices/src/virtio/gpu/mod.rs +++ b/devices/src/virtio/gpu/mod.rs @@ -68,6 +68,7 @@ pub use self::protocol::VIRTIO_GPU_F_FENCE_PASSING; pub use self::protocol::VIRTIO_GPU_F_RESOURCE_BLOB; pub use self::protocol::VIRTIO_GPU_F_RESOURCE_UUID; pub use self::protocol::VIRTIO_GPU_F_VIRGL; +pub use self::protocol::VIRTIO_GPU_MAX_SCANOUTS; pub use self::protocol::VIRTIO_GPU_SHM_ID_HOST_VISIBLE; use self::protocol::*; use self::virtio_gpu::to_rutabaga_descriptor; @@ -1118,7 +1119,7 @@ pub enum DisplayBackend { Stub, #[cfg(windows)] /// Open a window using WinAPI. - WinApi(WinDisplayProperties), + WinApi, #[cfg(feature = "android_display")] /// The display buffer is backed by an Android surface. The surface is set via an AIDL service /// that the backend hosts. Currently, the AIDL service is registered to the service manager @@ -1140,11 +1141,10 @@ impl DisplayBackend { DisplayBackend::X(display) => GpuDisplay::open_x(display.as_deref()), DisplayBackend::Stub => GpuDisplay::open_stub(), #[cfg(windows)] - DisplayBackend::WinApi(display_properties) => match wndproc_thread.take() { + DisplayBackend::WinApi => match wndproc_thread.take() { Some(wndproc_thread) => GpuDisplay::open_winapi( wndproc_thread, /* win_metrics= */ None, - display_properties.clone(), gpu_display_wait_descriptor_ctrl, None, ), diff --git a/devices/src/virtio/gpu/parameters.rs b/devices/src/virtio/gpu/parameters.rs index bbca096f7..3e97627b3 100644 --- a/devices/src/virtio/gpu/parameters.rs +++ b/devices/src/virtio/gpu/parameters.rs @@ -16,6 +16,7 @@ use vm_control::gpu::DisplayParameters; use super::GpuMode; use super::GpuWsi; +use crate::virtio::gpu::VIRTIO_GPU_MAX_SCANOUTS; use crate::PciAddress; mod serde_capset_mask { @@ -41,6 +42,8 @@ mod serde_capset_mask { pub struct GpuParameters { #[serde(rename = "backend")] pub mode: GpuMode, + #[serde(default = "default_max_num_displays")] + pub max_num_displays: u32, #[serde(rename = "displays")] pub display_params: Vec<DisplayParameters>, // `width` and `height` are supported for CLI backwards compatibility. @@ -81,6 +84,7 @@ pub struct GpuParameters { impl Default for GpuParameters { fn default() -> Self { GpuParameters { + max_num_displays: default_max_num_displays(), display_params: vec![], __width_compat: None, __height_compat: None, @@ -109,6 +113,10 @@ impl Default for GpuParameters { } } +fn default_max_num_displays() -> u32 { + VIRTIO_GPU_MAX_SCANOUTS as u32 +} + #[cfg(test)] mod tests { use serde_json::*; diff --git a/devices/src/virtio/gpu/virtio_gpu.rs b/devices/src/virtio/gpu/virtio_gpu.rs index a296afbce..c453fcd07 100644 --- a/devices/src/virtio/gpu/virtio_gpu.rs +++ b/devices/src/virtio/gpu/virtio_gpu.rs @@ -46,6 +46,7 @@ use rutabaga_gfx::RUTABAGA_MEM_HANDLE_TYPE_OPAQUE_FD; use serde::Deserialize; use serde::Serialize; use sync::Mutex; +use vm_control::gpu::DisplayMode; use vm_control::gpu::DisplayParameters; use vm_control::gpu::GpuControlCommand; use vm_control::gpu::GpuControlResult; @@ -285,11 +286,17 @@ impl VirtioGpuScanout { let mut display = display.borrow_mut(); + let display_params = + self.display_params + .clone() + .unwrap_or(DisplayParameters::default_with_mode(DisplayMode::Windowed( + self.width, + self.height, + ))); let surface_id = display.create_surface( self.parent_surface_id, self.scanout_id, - self.width, - self.height, + &display_params, self.scanout_type, )?; diff --git a/devices/src/virtio/pvclock.rs b/devices/src/virtio/pvclock.rs index e4d23522f..a0820c5b4 100644 --- a/devices/src/virtio/pvclock.rs +++ b/devices/src/virtio/pvclock.rs @@ -553,7 +553,10 @@ impl PvClockWorker { // SAFETY: // Safe because _rdtsc takes no arguments, and we trust _rdtsc to not modify // any other memory. - unsafe { _rdtsc() } - suspend_time.tsc_value, + // NB: This calculation may wrap around, as TSC can be reset to zero when + // the device has resumed from the "deep" suspend state (it may not happen for + // s2idle cases). It also happens when the tsc value itself wraps. + unsafe { _rdtsc() }.wrapping_sub(suspend_time.tsc_value), ) } else { return Err(Error::new(libc::ENOTSUP)) @@ -561,7 +564,10 @@ impl PvClockWorker { }; // update the total tsc delta during all suspends - self.total_suspend_tsc_delta += this_suspend_tsc_delta; + // NB: This calculation may wrap around, as the suspend time can be bigger than u64 range. + self.total_suspend_tsc_delta = self + .total_suspend_tsc_delta + .wrapping_add(this_suspend_tsc_delta); // save tsc_suspended_delta to shared memory self.pvclock_shared_data diff --git a/devices/src/virtio/vhost/user/device/block/sys/windows.rs b/devices/src/virtio/vhost/user/device/block/sys/windows.rs index 058c823a7..0aa56707f 100644 --- a/devices/src/virtio/vhost/user/device/block/sys/windows.rs +++ b/devices/src/virtio/vhost/user/device/block/sys/windows.rs @@ -65,7 +65,7 @@ pub fn start_device(opts: Options) -> anyhow::Result<()> { let _raise_timer_resolution = enable_high_res_timers().context("failed to set timer resolution")?; - info!("using {} IO handles.", disk_option.io_concurrency.get()); + info!("using {:?} executor.", disk_option.async_executor); let kind = disk_option .async_executor diff --git a/devices/src/virtio/vhost/user/device/fs/sys/linux.rs b/devices/src/virtio/vhost/user/device/fs/sys/linux.rs index a00cbb5cb..7236bb1b8 100644 --- a/devices/src/virtio/vhost/user/device/fs/sys/linux.rs +++ b/devices/src/virtio/vhost/user/device/fs/sys/linux.rs @@ -104,6 +104,7 @@ pub fn start_device(opts: Options) -> anyhow::Result<()> { base::syslog::push_descriptors(&mut keep_rds); cros_tracing::push_descriptors!(&mut keep_rds); + metrics::push_descriptors(&mut keep_rds); let pid = jail_and_fork( keep_rds, diff --git a/devices/src/virtio/vhost/user/device/gpu/sys/linux.rs b/devices/src/virtio/vhost/user/device/gpu/sys/linux.rs index f58121d44..57df8cf47 100644 --- a/devices/src/virtio/vhost/user/device/gpu/sys/linux.rs +++ b/devices/src/virtio/vhost/user/device/gpu/sys/linux.rs @@ -12,7 +12,6 @@ use anyhow::Context; use argh::FromArgs; use base::clone_descriptor; use base::error; -use base::FromRawDescriptor; use base::SafeDescriptor; use base::Tube; use base::UnixSeqpacketListener; @@ -98,13 +97,7 @@ impl GpuBackend { // Start handling the display. let display = clone_descriptor(&*state.borrow_mut().display().borrow()) - .map(|fd| { - AsyncWrapper::new( - // SAFETY: - // Safe because we just created this fd. - unsafe { SafeDescriptor::from_raw_descriptor(fd) }, - ) - }) + .map(AsyncWrapper::new) .context("failed to clone inner WaitContext for gpu display") .and_then(|ctx| { self.ex diff --git a/devices/src/virtio/vhost/user/device/gpu/sys/windows.rs b/devices/src/virtio/vhost/user/device/gpu/sys/windows.rs index d37a564ee..f44562194 100644 --- a/devices/src/virtio/vhost/user/device/gpu/sys/windows.rs +++ b/devices/src/virtio/vhost/user/device/gpu/sys/windows.rs @@ -286,9 +286,7 @@ pub fn run_gpu_device_worker( .push(GpuDisplayParameters::default()); } - let display_backends = vec![virtio::DisplayBackend::WinApi( - (&config.params.display_params[0]).into(), - )]; + let display_backends = vec![virtio::DisplayBackend::WinApi]; let mut gpu_params = config.params.clone(); diff --git a/devices/src/virtio/vhost/user/device/handler.rs b/devices/src/virtio/vhost/user/device/handler.rs index 57e66b763..daa39a0c3 100644 --- a/devices/src/virtio/vhost/user/device/handler.rs +++ b/devices/src/virtio/vhost/user/device/handler.rs @@ -1142,11 +1142,12 @@ mod tests { let vmm_bar = Arc::new(Barrier::new(2)); let dev_bar = vmm_bar.clone(); - let (tx, rx) = channel(); + let (ready_tx, ready_rx) = channel(); + let (shutdown_tx, shutdown_rx) = channel(); std::thread::spawn(move || { // VMM side - rx.recv().unwrap(); // Ensure the device is ready. + ready_rx.recv().unwrap(); // Ensure the device is ready. let connection = test_helpers::connect(vmm); @@ -1198,6 +1199,9 @@ mod tests { println!("virtio_wake"); vmm_device.virtio_wake(None).unwrap(); + println!("wait for shutdown signal"); + shutdown_rx.recv().unwrap(); + // The VMM side is supposed to stop before the device side. println!("drop"); drop(vmm_device); @@ -1210,7 +1214,7 @@ mod tests { handler.as_mut().allow_backend_req = allow_backend_req; // Notify listener is ready. - tx.send(()).unwrap(); + ready_tx.send(()).unwrap(); let mut req_handler = test_helpers::listen(dev, handler); @@ -1285,8 +1289,11 @@ mod tests { .expect("send_config_changed failed"); } + // Ask the client to shutdown, then wait to it to finish. + shutdown_tx.send(()).unwrap(); dev_bar.wait(); + // Verify recv_header fails with `ClientExit` after the client has disconnected. match req_handler.recv_header() { Err(VhostError::ClientExit) => (), r => panic!("expected Err(ClientExit) but got {:?}", r), diff --git a/devices/src/virtio/vhost/user/device/wl.rs b/devices/src/virtio/vhost/user/device/wl.rs index 0287a6708..0d3cf9b12 100644 --- a/devices/src/virtio/vhost/user/device/wl.rs +++ b/devices/src/virtio/vhost/user/device/wl.rs @@ -18,7 +18,6 @@ use argh::FromArgs; use base::clone_descriptor; use base::error; use base::warn; -use base::FromRawDescriptor; use base::SafeDescriptor; use base::Tube; use base::UnixSeqpacket; @@ -268,11 +267,7 @@ impl VhostUserDevice for WlBackend { let queue_task = match idx { 0 => { let wlstate_ctx = clone_descriptor(wlstate.borrow().wait_ctx()) - .map(|fd| { - // SAFETY: - // Safe because we just created this fd. - AsyncWrapper::new(unsafe { SafeDescriptor::from_raw_descriptor(fd) }) - }) + .map(AsyncWrapper::new) .context("failed to clone inner WaitContext for WlState") .and_then(|ctx| { self.ex diff --git a/devices/src/virtio/video/decoder/backend/mod.rs b/devices/src/virtio/video/decoder/backend/mod.rs index 6e80f7626..cf8236e7d 100644 --- a/devices/src/virtio/video/decoder/backend/mod.rs +++ b/devices/src/virtio/video/decoder/backend/mod.rs @@ -153,10 +153,8 @@ pub enum DecoderEvent { mod tests { use std::time::Duration; - use base::FromRawDescriptor; use base::MappedRegion; use base::MemoryMappingBuilder; - use base::SafeDescriptor; use base::SharedMemory; use base::WaitContext; @@ -233,11 +231,7 @@ mod tests { #[allow(dead_code)] pub fn build_object_handle(mem: &SharedMemory) -> GuestResourceHandle { GuestResourceHandle::VirtioObject(VirtioObjectHandle { - // SAFETY: - // Safe because we are taking ownership of a just-duplicated FD. - desc: unsafe { - SafeDescriptor::from_raw_descriptor(base::clone_descriptor(mem).unwrap()) - }, + desc: base::clone_descriptor(mem).unwrap(), modifier: 0, }) } @@ -247,11 +241,7 @@ mod tests { #[allow(dead_code)] pub fn build_guest_mem_handle(mem: &SharedMemory) -> GuestResourceHandle { GuestResourceHandle::GuestPages(GuestMemHandle { - // SAFETY: - // Safe because we are taking ownership of a just-duplicated FD. - desc: unsafe { - SafeDescriptor::from_raw_descriptor(base::clone_descriptor(mem).unwrap()) - }, + desc: base::clone_descriptor(mem).unwrap(), mem_areas: vec![GuestMemArea { offset: 0, length: mem.size() as usize, diff --git a/devices/src/virtio/video/resource.rs b/devices/src/virtio/video/resource.rs index 00202ab77..4d5192a30 100644 --- a/devices/src/virtio/video/resource.rs +++ b/devices/src/virtio/video/resource.rs @@ -232,11 +232,8 @@ impl GuestResource { let guest_region = mem .shm_region(GuestAddress(addr)) .map_err(GuestMemResourceCreationError::CantGetShmRegion)?; - let desc = base::clone_descriptor(guest_region) - .map_err(GuestMemResourceCreationError::DescriptorCloneError)?; - // SAFETY: - // Safe because we are the sole owner of the duplicated descriptor. - unsafe { SafeDescriptor::from_raw_descriptor(desc) } + base::clone_descriptor(guest_region) + .map_err(GuestMemResourceCreationError::DescriptorCloneError)? } }; @@ -377,7 +374,6 @@ impl GuestResource { #[cfg(test)] mod tests { use base::MappedRegion; - use base::SafeDescriptor; use base::SharedMemory; use super::*; @@ -415,10 +411,7 @@ mod tests { // Create the `GuestMemHandle` we will try to map and retrieve the data from. let mem_handle = GuestResourceHandle::GuestPages(GuestMemHandle { - // SAFETY: descriptor is expected to be valid - desc: unsafe { - SafeDescriptor::from_raw_descriptor(base::clone_descriptor(&mem).unwrap()) - }, + desc: base::clone_descriptor(&mem).unwrap(), mem_areas: page_order .iter() .map(|&page| GuestMemArea { diff --git a/devices/src/virtio/video/worker.rs b/devices/src/virtio/video/worker.rs index 32bd15f15..aa40ef8fd 100644 --- a/devices/src/virtio/video/worker.rs +++ b/devices/src/virtio/video/worker.rs @@ -11,8 +11,6 @@ use base::clone_descriptor; use base::error; use base::info; use base::Event; -use base::FromRawDescriptor; -use base::SafeDescriptor; use base::WaitContext; use cros_async::select3; use cros_async::AsyncWrapper; @@ -403,12 +401,7 @@ impl Worker { let device_wait_ctx = WaitContext::new().map_err(Error::WaitContextCreationFailed)?; let device_evt = ex .async_from(AsyncWrapper::new( - clone_descriptor(&device_wait_ctx) - .map(|fd| - // SAFETY: - // Safe because we just created this fd. - unsafe { SafeDescriptor::from_raw_descriptor(fd) }) - .map_err(Error::CloneDescriptorFailed)?, + clone_descriptor(&device_wait_ctx).map_err(Error::CloneDescriptorFailed)?, )) .map_err(Error::EventAsyncCreationFailed)?; diff --git a/devices/src/virtio/virtio_pci_device.rs b/devices/src/virtio/virtio_pci_device.rs index 101698243..7dbbc74bd 100644 --- a/devices/src/virtio/virtio_pci_device.rs +++ b/devices/src/virtio/virtio_pci_device.rs @@ -23,6 +23,8 @@ use data_model::Le32; use hypervisor::Datamatch; use hypervisor::MemCacheType; use libc::ERANGE; +#[cfg(target_arch = "x86_64")] +use metrics::MetricEventType; use resources::Alloc; use resources::AllocOptions; use resources::SystemAllocator; @@ -548,7 +550,9 @@ impl VirtioPciDevice { Some(PmWakeupEvent::new( self.vm_control_tube.clone(), self.pm_config.clone(), - self.device.debug_label(), + MetricEventType::VirtioWakeup { + virtio_id: self.device.device_type() as u32, + }, )), ); self.interrupt = Some(interrupt.clone()); @@ -1312,7 +1316,9 @@ impl Suspendable for VirtioPciDevice { Some(PmWakeupEvent::new( self.vm_control_tube.clone(), self.pm_config.clone(), - self.device.debug_label(), + MetricEventType::VirtioWakeup { + virtio_id: self.device.device_type() as u32, + }, )), )); } diff --git a/devices/src/virtio/wl.rs b/devices/src/virtio/wl.rs index 22a9518e1..8f1e0d488 100644 --- a/devices/src/virtio/wl.rs +++ b/devices/src/virtio/wl.rs @@ -75,6 +75,10 @@ use base::EventType; use base::FromRawDescriptor; #[cfg(feature = "gpu")] use base::IntoRawDescriptor; +#[cfg(feature = "minigbm")] +use base::MemoryMappingBuilder; +#[cfg(feature = "minigbm")] +use base::MmapError; use base::Protection; use base::RawDescriptor; use base::Result; @@ -93,6 +97,8 @@ use hypervisor::MemCacheType; use libc::EBADF; #[cfg(feature = "minigbm")] use libc::EINVAL; +#[cfg(feature = "minigbm")] +use libc::ENOSYS; use remain::sorted; use resources::address_allocator::AddressAllocator; use resources::AddressRange; @@ -113,6 +119,10 @@ use rutabaga_gfx::RutabagaGralloc; use rutabaga_gfx::RutabagaGrallocFlags; #[cfg(feature = "minigbm")] use rutabaga_gfx::RutabagaIntoRawDescriptor; +#[cfg(feature = "minigbm")] +use rutabaga_gfx::RUTABAGA_MAP_CACHE_CACHED; +#[cfg(feature = "minigbm")] +use rutabaga_gfx::RUTABAGA_MAP_CACHE_MASK; use thiserror::Error as ThisError; use vm_control::VmMemorySource; use vm_memory::GuestAddress; @@ -190,6 +200,10 @@ const VIRTIO_WL_VFD_DMABUF_SYNC_VALID_FLAG_MASK: u32 = 0x7; #[cfg(feature = "minigbm")] const DMA_BUF_IOCTL_BASE: c_uint = 0x62; +#[cfg(feature = "minigbm")] +const DMA_BUF_SYNC_WRITE: c_uint = 0x2; +#[cfg(feature = "minigbm")] +const DMA_BUF_SYNC_END: c_uint = 0x4; #[cfg(feature = "minigbm")] #[repr(C)] @@ -797,6 +811,8 @@ struct WlVfd { slot: Option<(u64 /* offset */, VmRequester)>, #[cfg(feature = "minigbm")] is_dmabuf: bool, + #[cfg(feature = "minigbm")] + map_info: u32, fence: Option<File>, is_fence: bool, } @@ -820,6 +836,25 @@ impl fmt::Debug for WlVfd { } } +#[cfg(feature = "minigbm")] +fn flush_shared_memory(shared_memory: &SharedMemory) -> Result<()> { + let mmap = match MemoryMappingBuilder::new(shared_memory.size as usize) + .from_shared_memory(shared_memory) + .build() + { + Ok(v) => v, + Err(_) => return Err(Error::new(EINVAL)), + }; + if let Err(err) = mmap.flush_all() { + base::error!("failed to flush shared memory: {}", err); + return match err { + MmapError::NotImplemented(_) => Err(Error::new(ENOSYS)), + _ => Err(Error::new(EINVAL)), + }; + } + Ok(()) +} + impl WlVfd { fn connect<P: AsRef<Path>>(path: P) -> WlResult<WlVfd> { let socket = UnixStream::connect(path).map_err(WlError::SocketConnect)?; @@ -864,6 +899,7 @@ impl WlVfd { vfd.guest_shared_memory = Some(vfd_shm); vfd.slot = Some((offset, vm)); vfd.is_dmabuf = true; + vfd.map_info = reqs.map_info; Ok((vfd, desc)) } @@ -881,10 +917,25 @@ impl WlVfd { // SAFETY: // Safe as descriptor is a valid dmabuf and incorrect flags will return an error. if unsafe { ioctl_with_ref(descriptor, DMA_BUF_IOCTL_SYNC(), &sync) } < 0 { - Err(WlError::DmabufSync(io::Error::last_os_error())) - } else { - Ok(()) + return Err(WlError::DmabufSync(io::Error::last_os_error())); } + + // virtio-wl kernel driver always maps dmabufs with WB memory type, regardless of + // the host memory type (which is wrong). However, to avoid changing the protocol, + // assume that all guest writes are cached and ensure clflush-like ops on all mapped + // cachelines if the host mapping is not cached. + const END_WRITE_MASK: u32 = DMA_BUF_SYNC_WRITE | DMA_BUF_SYNC_END; + if (flags & END_WRITE_MASK) == END_WRITE_MASK + && (self.map_info & RUTABAGA_MAP_CACHE_MASK) != RUTABAGA_MAP_CACHE_CACHED + { + if let Err(err) = flush_shared_memory(descriptor) { + base::warn!("failed to flush cached dmabuf mapping: {:?}", err); + return Err(WlError::DmabufSync(io::Error::from_raw_os_error( + err.errno(), + ))); + } + } + Ok(()) } None => Err(WlError::DmabufSync(io::Error::from_raw_os_error(EBADF))), } diff --git a/devices/tests/irqchip/userspace.rs b/devices/tests/irqchip/userspace.rs index c4599bf11..7f521f691 100644 --- a/devices/tests/irqchip/userspace.rs +++ b/devices/tests/irqchip/userspace.rs @@ -4,6 +4,7 @@ #![cfg(target_arch = "x86_64")] +use std::collections::BTreeMap; use std::sync::Arc; use std::thread; use std::time::Duration; @@ -46,7 +47,6 @@ use hypervisor::IrqSource; use hypervisor::Level; use hypervisor::PicSelect; use hypervisor::PitRWMode; -use hypervisor::Register; use hypervisor::Regs; use hypervisor::Sregs; use hypervisor::TriggerMode; @@ -752,19 +752,19 @@ impl VcpuX86_64 for FakeVcpu { fn set_debugregs(&self, _debugregs: &DebugRegs) -> Result<()> { unimplemented!() } - fn get_xcrs(&self) -> Result<Vec<Register>> { + fn get_xcrs(&self) -> Result<BTreeMap<u32, u64>> { unimplemented!() } - fn set_xcrs(&self, _xcrs: &[Register]) -> Result<()> { + fn set_xcr(&self, _xcr_index: u32, _value: u64) -> Result<()> { unimplemented!() } - fn get_msrs(&self, _msrs: &mut Vec<Register>) -> Result<()> { + fn get_msr(&self, _msr_index: u32) -> Result<u64> { unimplemented!() } - fn get_all_msrs(&self) -> Result<Vec<Register>> { + fn get_all_msrs(&self) -> Result<BTreeMap<u32, u64>> { unimplemented!() } - fn set_msrs(&self, _msrs: &[Register]) -> Result<()> { + fn set_msr(&self, _msr_index: u32, _value: u64) -> Result<()> { unimplemented!() } fn set_cpuid(&self, _cpuid: &CpuId) -> Result<()> { @@ -779,15 +779,6 @@ impl VcpuX86_64 for FakeVcpu { fn set_guest_debug(&self, _addrs: &[GuestAddress], _enable_singlestep: bool) -> Result<()> { unimplemented!() } - fn get_tsc_offset(&self) -> Result<u64> { - unimplemented!() - } - fn set_tsc_offset(&self, _offset: u64) -> Result<()> { - unimplemented!() - } - fn set_tsc_value(&self, _value: u64) -> Result<()> { - unimplemented!() - } fn snapshot(&self) -> anyhow::Result<VcpuSnapshot> { unimplemented!() } diff --git a/disk/Android.bp b/disk/Android.bp index 4cbe5b38a..4e4b37f6d 100644 --- a/disk/Android.bp +++ b/disk/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/disk/src/qcow/mod.rs b/disk/src/qcow/mod.rs index e23a36274..96fbc0e45 100644 --- a/disk/src/qcow/mod.rs +++ b/disk/src/qcow/mod.rs @@ -287,10 +287,10 @@ impl QcowHeader { } // L2 blocks are always one cluster long. They contain cluster_size/sizeof(u64) addresses. let l2_size: u32 = cluster_size / size_of::<u64>() as u32; - let num_clusters: u32 = div_round_up_u64(size, u64::from(cluster_size)) as u32; - let num_l2_clusters: u32 = div_round_up_u32(num_clusters, l2_size); - let l1_clusters: u32 = div_round_up_u32(num_l2_clusters, cluster_size); - let header_clusters = div_round_up_u32(size_of::<QcowHeader>() as u32, cluster_size); + let num_clusters: u32 = size.div_ceil(u64::from(cluster_size)) as u32; + let num_l2_clusters: u32 = num_clusters.div_ceil(l2_size); + let l1_clusters: u32 = num_l2_clusters.div_ceil(cluster_size); + let header_clusters = (size_of::<QcowHeader>() as u32).div_ceil(cluster_size); Ok(QcowHeader { magic: QCOW_MAGIC, version: 3, @@ -317,10 +317,7 @@ impl QcowHeader { num_clusters + l1_clusters + num_l2_clusters + header_clusters, ) as u32; // The refcount table needs to store the offset of each refcount cluster. - div_round_up_u32( - max_refcount_clusters * size_of::<u64>() as u32, - cluster_size, - ) + (max_refcount_clusters * size_of::<u64>() as u32).div_ceil(cluster_size) }, nb_snapshots: 0, snapshots_offset: 0, @@ -389,8 +386,8 @@ impl QcowHeader { fn max_refcount_clusters(refcount_order: u32, cluster_size: u32, num_clusters: u32) -> u64 { // Use u64 as the product of the u32 inputs can overflow. let refcount_bytes = (0x01 << refcount_order as u64) / 8; - let for_data = div_round_up_u64(num_clusters as u64 * refcount_bytes, cluster_size as u64); - let for_refcounts = div_round_up_u64(for_data * refcount_bytes, cluster_size as u64); + let for_data = (u64::from(num_clusters) * refcount_bytes).div_ceil(u64::from(cluster_size)); + let for_refcounts = (for_data * refcount_bytes).div_ceil(u64::from(cluster_size)); for_data + for_refcounts } @@ -532,10 +529,10 @@ impl QcowFile { } let l2_size = cluster_size / size_of::<u64>() as u64; - let num_clusters = div_round_up_u64(header.size, cluster_size); - let num_l2_clusters = div_round_up_u64(num_clusters, l2_size); - let l1_clusters = div_round_up_u64(num_l2_clusters, cluster_size); - let header_clusters = div_round_up_u64(size_of::<QcowHeader>() as u64, cluster_size); + let num_clusters = header.size.div_ceil(cluster_size); + let num_l2_clusters = num_clusters.div_ceil(l2_size); + let l1_clusters = num_l2_clusters.div_ceil(cluster_size); + let header_clusters = (size_of::<QcowHeader>() as u64).div_ceil(cluster_size); if num_l2_clusters > MAX_RAM_POINTER_TABLE_SIZE { return Err(Error::TooManyL1Entries(num_l2_clusters)); } @@ -549,7 +546,7 @@ impl QcowFile { .map_err(Error::ReadingHeader)?, ); - let num_clusters = div_round_up_u64(header.size, cluster_size); + let num_clusters = header.size.div_ceil(cluster_size); let refcount_clusters = max_refcount_clusters( header.refcount_order, cluster_size as u32, @@ -737,7 +734,7 @@ impl QcowFile { header: QcowHeader, cluster_size: u64, ) -> Result<()> { - let l1_clusters = div_round_up_u64(header.l1_size as u64, cluster_size); + let l1_clusters = u64::from(header.l1_size).div_ceil(cluster_size); let l1_table_offset = header.l1_table_offset; for i in 0..l1_clusters { add_ref(refcounts, cluster_size, l1_table_offset + i * cluster_size)?; @@ -810,7 +807,7 @@ impl QcowFile { refblock_clusters: u64, pointers_per_cluster: u64, ) -> Result<Vec<u64>> { - let refcount_table_entries = div_round_up_u64(refblock_clusters, pointers_per_cluster); + let refcount_table_entries = refblock_clusters.div_ceil(pointers_per_cluster); let mut ref_table = vec![0; refcount_table_entries as usize]; let mut first_free_cluster: u64 = 0; for refblock_addr in &mut ref_table { @@ -899,24 +896,21 @@ impl QcowFile { .len(); let refcount_bits = 1u64 << header.refcount_order; - let refcount_bytes = div_round_up_u64(refcount_bits, 8); + let refcount_bytes = refcount_bits.div_ceil(8); let refcount_block_entries = cluster_size / refcount_bytes; let pointers_per_cluster = cluster_size / size_of::<u64>() as u64; - let data_clusters = div_round_up_u64(header.size, cluster_size); - let l2_clusters = div_round_up_u64(data_clusters, pointers_per_cluster); - let l1_clusters = div_round_up_u64(l2_clusters, cluster_size); - let header_clusters = div_round_up_u64(size_of::<QcowHeader>() as u64, cluster_size); + let data_clusters = header.size.div_ceil(cluster_size); + let l2_clusters = data_clusters.div_ceil(pointers_per_cluster); + let l1_clusters = l2_clusters.div_ceil(cluster_size); + let header_clusters = (size_of::<QcowHeader>() as u64).div_ceil(cluster_size); let max_clusters = data_clusters + l2_clusters + l1_clusters + header_clusters; let mut max_valid_cluster_index = max_clusters; - let refblock_clusters = div_round_up_u64(max_valid_cluster_index, refcount_block_entries); - let reftable_clusters = div_round_up_u64(refblock_clusters, pointers_per_cluster); + let refblock_clusters = max_valid_cluster_index.div_ceil(refcount_block_entries); + let reftable_clusters = refblock_clusters.div_ceil(pointers_per_cluster); // Account for refblocks and the ref table size needed to address them. - let refblocks_for_refs = div_round_up_u64( - refblock_clusters + reftable_clusters, - refcount_block_entries, - ); - let reftable_clusters_for_refs = - div_round_up_u64(refblocks_for_refs, refcount_block_entries); + let refblocks_for_refs = + (refblock_clusters + reftable_clusters).div_ceil(refcount_block_entries); + let reftable_clusters_for_refs = refblocks_for_refs.div_ceil(refcount_block_entries); max_valid_cluster_index += refblock_clusters + reftable_clusters; max_valid_cluster_index += refblocks_for_refs + reftable_clusters_for_refs; @@ -1630,16 +1624,6 @@ fn offset_is_cluster_boundary(offset: u64, cluster_bits: u32) -> Result<()> { Ok(()) } -// Ceiling of the division of `dividend`/`divisor`. -fn div_round_up_u64(dividend: u64, divisor: u64) -> u64 { - dividend / divisor + u64::from(dividend % divisor != 0) -} - -// Ceiling of the division of `dividend`/`divisor`. -fn div_round_up_u32(dividend: u32, divisor: u32) -> u32 { - dividend / divisor + u32::from(dividend % divisor != 0) -} - #[cfg(test)] mod tests { use std::fs::OpenOptions; diff --git a/e2e_tests/fixture/src/vm.rs b/e2e_tests/fixture/src/vm.rs index 483fd6ab0..ee5197832 100644 --- a/e2e_tests/fixture/src/vm.rs +++ b/e2e_tests/fixture/src/vm.rs @@ -406,7 +406,27 @@ impl TestVm { TestVm::new_generic(TestVmSys::append_config_args, cfg, false) } - pub fn new_cold_restore(cfg: Config) -> Result<TestVm> { + /// Create `TestVm` from a snapshot, using `--restore` but NOT `--suspended`. + pub fn new_restore(cfg: Config) -> Result<TestVm> { + let mut vm = TestVm::new_generic_restore(TestVmSys::append_config_args, cfg, false)?; + // Send a resume request to wait for the restore to finish. + // We don't want to return from this function until the restore is complete, otherwise it + // will be difficult to differentiate between a slow restore and a slow response from the + // guest. + let vm = run_with_timeout( + move || { + vm.resume_full().expect("failed to resume after VM restore"); + vm + }, + Duration::from_secs(60), + ) + .expect("VM restore timeout"); + + Ok(vm) + } + + /// Create `TestVm` from a snapshot, using `--restore` AND `--suspended`. + pub fn new_restore_suspended(cfg: Config) -> Result<TestVm> { TestVm::new_generic_restore(TestVmSys::append_config_args, cfg, false) } diff --git a/e2e_tests/guest_under_test/rootfs/readclock/Android.bp b/e2e_tests/guest_under_test/rootfs/readclock/Android.bp index af297e004..0cc1cc5f2 100644 --- a/e2e_tests/guest_under_test/rootfs/readclock/Android.bp +++ b/e2e_tests/guest_under_test/rootfs/readclock/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/e2e_tests/tests/pci_hotplug.rs b/e2e_tests/tests/pci_hotplug.rs index 5e313cbb1..b0371f7d4 100644 --- a/e2e_tests/tests/pci_hotplug.rs +++ b/e2e_tests/tests/pci_hotplug.rs @@ -201,6 +201,7 @@ fn tap_hotplug_add_remove_add_impl() { /// Checks tap hotplug works with a device added, removed, then added again. #[test] +#[ignore = "b/333090169 test is flaky"] fn tap_hotplug_add_remove_add() { call_test_with_sudo("tap_hotplug_add_remove_add_impl"); } @@ -262,6 +263,7 @@ fn tap_hotplug_add_remove_rapid_add_impl() { /// Checks tap hotplug works with a device added, removed, then rapidly added again. #[test] +#[ignore = "b/333090169 test is flaky"] fn tap_hotplug_add_remove_rapid_add() { call_test_with_sudo("tap_hotplug_add_remove_rapid_add_impl"); } diff --git a/e2e_tests/tests/suspend_resume.rs b/e2e_tests/tests/suspend_resume.rs index f893d2cf6..c814d39e8 100644 --- a/e2e_tests/tests/suspend_resume.rs +++ b/e2e_tests/tests/suspend_resume.rs @@ -119,7 +119,7 @@ fn suspend_resume_system(disabled_sandbox: bool) -> anyhow::Result<()> { // shut down VM drop(vm); // Start up VM with cold restore. - let mut vm = TestVm::new_cold_restore(new_config().extra_args(vec![ + let mut vm = TestVm::new_restore_suspended(new_config().extra_args(vec![ "--restore".to_string(), snap1_path.to_str().unwrap().to_string(), "--suspended".to_string(), @@ -213,7 +213,7 @@ fn snapshot_vhost_user() { snap_path.to_str().unwrap().to_string(), "--no-usb".to_string(), ]); - let _vm = TestVm::new_cold_restore(config).unwrap(); + let _vm = TestVm::new_restore(config).unwrap(); } fn create_net_config(socket: &Path) -> VuConfig { diff --git a/e2e_tests/tests/vsock.rs b/e2e_tests/tests/vsock.rs index 64154289d..dfdb13c00 100644 --- a/e2e_tests/tests/vsock.rs +++ b/e2e_tests/tests/vsock.rs @@ -89,7 +89,7 @@ fn host_to_guest_snapshot_restore() { ]) .with_stdout_hardware("legacy-virtio-console"); drop(vm); - vm = TestVm::new_cold_restore(config).unwrap(); + vm = TestVm::new_restore(config).unwrap(); host_to_guest_connection(&mut vm, guest_cid, guest_port); } @@ -119,7 +119,7 @@ fn host_to_guest_disable_sandbox_snapshot_restore() { ]) .with_stdout_hardware("legacy-virtio-console"); drop(vm); - vm = TestVm::new_cold_restore(config.disable_sandbox()).unwrap(); + vm = TestVm::new_restore(config.disable_sandbox()).unwrap(); host_to_guest_connection(&mut vm, guest_cid, guest_port); } @@ -199,7 +199,7 @@ fn guest_to_host_snapshot_restore() { ]) .with_stdout_hardware("legacy-virtio-console"); drop(vm); - vm = TestVm::new_cold_restore(config).unwrap(); + vm = TestVm::new_restore(config).unwrap(); guest_to_host_connection(&mut vm, host_port); } @@ -230,7 +230,7 @@ fn guest_to_host_disable_sandbox_snapshot_restore() { ]) .with_stdout_hardware("legacy-virtio-console"); drop(vm); - vm = TestVm::new_cold_restore(config.disable_sandbox()).unwrap(); + vm = TestVm::new_restore(config.disable_sandbox()).unwrap(); guest_to_host_connection(&mut vm, host_port); } @@ -277,6 +277,7 @@ fn create_vu_config(cmd_type: CmdType, socket: &Path, cid: u32) -> VuConfig { } #[test] +#[ignore = "b/333090069 test is flaky"] fn vhost_user_host_to_guest() { let guest_port = generate_vhost_port(); let guest_cid = generate_guest_cid(); @@ -295,6 +296,7 @@ fn vhost_user_host_to_guest() { } #[test] +#[ignore = "b/333090069 test is flaky"] fn vhost_user_host_to_guest_with_devices() { let guest_port = generate_vhost_port(); let guest_cid = generate_guest_cid(); diff --git a/fuse/Android.bp b/fuse/Android.bp index cb40a78d3..5d6c6fd4c 100644 --- a/fuse/Android.bp +++ b/fuse/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/gpu_display/Android.bp b/gpu_display/Android.bp index 741f34c7b..e96b71db4 100644 --- a/gpu_display/Android.bp +++ b/gpu_display/Android.bp @@ -1,5 +1,7 @@ // This file is generated by cargo_embargo. -// Do not modify this file as changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. +// Content before the first "rust_*" or "genrule" module is preserved. // cargo2android.py limitations: // does not handle "-l dylib=wayland-client" yet diff --git a/gpu_display/examples/simple.rs b/gpu_display/examples/simple.rs index 9741dc44a..afc5b1f38 100644 --- a/gpu_display/examples/simple.rs +++ b/gpu_display/examples/simple.rs @@ -9,6 +9,8 @@ mod platform { use anyhow::Context; use anyhow::Result; use gpu_display::*; + use vm_control::gpu::DisplayMode; + use vm_control::gpu::DisplayParameters; pub fn run() -> Result<()> { let mut disp = GpuDisplay::open_wayland(None::<&str>).context("open_wayland")?; @@ -16,8 +18,7 @@ mod platform { .create_surface( None, /* scanout_id= */ Some(0), - 1280, - 1024, + &DisplayParameters::default_with_mode(DisplayMode::Windowed(1280, 1024)), SurfaceType::Scanout, ) .context("create_surface")?; diff --git a/gpu_display/examples/simple_open.rs b/gpu_display/examples/simple_open.rs index 73bdef757..1bf96a068 100644 --- a/gpu_display/examples/simple_open.rs +++ b/gpu_display/examples/simple_open.rs @@ -8,6 +8,8 @@ use anyhow::Context; use anyhow::Result; use gpu_display::GpuDisplay; use gpu_display::SurfaceType; +use vm_control::gpu::DisplayMode; +use vm_control::gpu::DisplayParameters; fn run() -> Result<()> { let mut disp = GpuDisplay::open_x(None::<&str>).context("open_x")?; @@ -15,8 +17,7 @@ fn run() -> Result<()> { .create_surface( None, /* scanout_id= */ Some(0), - 1280, - 1024, + &DisplayParameters::default_with_mode(DisplayMode::Windowed(1280, 1024)), SurfaceType::Scanout, ) .context("create_surface")?; diff --git a/gpu_display/src/gpu_display_android.rs b/gpu_display/src/gpu_display_android.rs index 9fcd5e7bc..5ccef070a 100644 --- a/gpu_display/src/gpu_display_android.rs +++ b/gpu_display/src/gpu_display_android.rs @@ -16,6 +16,7 @@ use base::AsRawDescriptor; use base::Event; use base::RawDescriptor; use base::VolatileSlice; +use vm_control::gpu::DisplayParameters; use crate::DisplayT; use crate::GpuDisplayError; @@ -209,14 +210,14 @@ impl DisplayT for DisplayAndroid { parent_surface_id: Option<u32>, _surface_id: u32, _scanout_id: Option<u32>, - requested_width: u32, - requested_height: u32, + display_params: &DisplayParameters, _surf_type: SurfaceType, ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> { if parent_surface_id.is_some() { return Err(GpuDisplayError::Unsupported); } + let (requested_width, requested_height) = display_params.get_virtual_display_size(); // SAFETY: context is an opaque handle. let surface = NonNull::new(unsafe { create_android_surface(self.context.0.as_ptr(), requested_width, requested_height) diff --git a/gpu_display/src/gpu_display_stub.rs b/gpu_display/src/gpu_display_stub.rs index 53403561c..123ab7487 100644 --- a/gpu_display/src/gpu_display_stub.rs +++ b/gpu_display/src/gpu_display_stub.rs @@ -6,6 +6,7 @@ use base::AsRawDescriptor; use base::Event; use base::RawDescriptor; use base::VolatileSlice; +use vm_control::gpu::DisplayParameters; use crate::DisplayT; use crate::GpuDisplayError; @@ -103,14 +104,14 @@ impl DisplayT for DisplayStub { parent_surface_id: Option<u32>, _surface_id: u32, _scanout_id: Option<u32>, - width: u32, - height: u32, + display_params: &DisplayParameters, _surf_type: SurfaceType, ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> { if parent_surface_id.is_some() { return Err(GpuDisplayError::Unsupported); } + let (width, height) = display_params.get_virtual_display_size(); Ok(Box::new(StubSurface { width, height, diff --git a/gpu_display/src/gpu_display_win/mod.rs b/gpu_display/src/gpu_display_win/mod.rs index ebe2dd737..f88e86435 100644 --- a/gpu_display/src/gpu_display_win/mod.rs +++ b/gpu_display/src/gpu_display_win/mod.rs @@ -36,14 +36,10 @@ use base::EventWaitResult; use base::RawDescriptor; use base::ReadNotifier; use base::SendTube; -use euclid::size2; -use euclid::Size2D; -use math_util::Size2DCheckedCast; use metrics::sys::windows::Metrics; pub use surface::Surface; use sync::Mutex; use sync::Waitable; -use vm_control::gpu::DisplayMode; use vm_control::gpu::DisplayParameters; use vm_control::ModifyWaitContext; use window_message_processor::DisplaySendToWndProc; @@ -72,28 +68,6 @@ pub(crate) type ObjectId = NonZeroU32; pub struct VirtualDisplaySpace; pub struct HostWindowSpace; -#[derive(Clone)] -pub struct DisplayProperties { - pub start_hidden: bool, - pub is_fullscreen: bool, - pub window_width: u32, - pub window_height: u32, -} - -impl From<&DisplayParameters> for DisplayProperties { - fn from(params: &DisplayParameters) -> Self { - let is_fullscreen = matches!(params.mode, DisplayMode::BorderlessFullScreen(_)); - let (window_width, window_height) = params.get_window_size(); - - Self { - start_hidden: params.hidden, - is_fullscreen, - window_width, - window_height, - } - } -} - pub enum VulkanDisplayWrapper { Uninitialized, #[cfg(feature = "vulkan_display")] @@ -104,7 +78,6 @@ pub struct DisplayWin { wndproc_thread: Rc<WindowProcedureThread>, close_requested_event: Event, win_metrics: Option<Weak<Metrics>>, - display_properties: DisplayProperties, is_surface_created: bool, #[allow(dead_code)] gpu_display_wait_descriptor_ctrl: SendTube, @@ -118,7 +91,6 @@ impl DisplayWin { pub fn new( wndproc_thread: WindowProcedureThread, win_metrics: Option<Weak<Metrics>>, - display_properties: DisplayProperties, gpu_display_wait_descriptor_ctrl: SendTube, vulkan_display_create_params: Option<VulkanCreateParams>, ) -> Result<DisplayWin, GpuDisplayError> { @@ -133,7 +105,6 @@ impl DisplayWin { wndproc_thread: Rc::new(wndproc_thread), close_requested_event, win_metrics, - display_properties, is_surface_created: false, gpu_display_wait_descriptor_ctrl, event_device_wait_descriptor_requests: Vec::new(), @@ -148,10 +119,10 @@ impl DisplayWin { &mut self, surface_id: u32, scanout_id: u32, - virtual_display_size: Size2D<i32, VirtualDisplaySpace>, + display_params: &DisplayParameters, ) -> Result<Arc<Mutex<VulkanDisplayWrapper>>> { + let display_params_clone = display_params.clone(); let metrics = self.win_metrics.clone(); - let display_properties = self.display_properties.clone(); #[cfg(feature = "vulkan_display")] let vulkan_create_params = self.vulkan_display_create_params.clone(); // This function should not return until surface creation finishes. Besides, we would like @@ -216,9 +187,8 @@ impl DisplayWin { Surface::new( surface_id, window, - &virtual_display_size, metrics, - &display_properties, + &display_params_clone, display_event_dispatcher, vulkan_display, ) @@ -299,8 +269,7 @@ impl DisplayT for DisplayWin { parent_surface_id: Option<u32>, surface_id: u32, scanout_id: Option<u32>, - virtual_display_width: u32, - virtual_display_height: u32, + display_params: &DisplayParameters, surface_type: SurfaceType, ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> { if parent_surface_id.is_some() { @@ -316,7 +285,7 @@ impl DisplayT for DisplayWin { let vulkan_display = match self.create_surface_internal( surface_id, scanout_id.expect("scanout id is required"), - size2(virtual_display_width, virtual_display_height).checked_cast(), + display_params, ) { Err(e) => { error!("Failed to create surface: {:?}", e); @@ -562,6 +531,7 @@ mod tests { let wndproc_thread_builder = { let mut wndproc_thread_builder = wndproc_thread_builder; wndproc_thread_builder + .set_max_num_windows(1) .set_display_tube(None) .set_ime_tube(Some(_device_ime_tube)); wndproc_thread_builder diff --git a/gpu_display/src/gpu_display_win/surface.rs b/gpu_display/src/gpu_display_win/surface.rs index be5811825..6c7491f50 100644 --- a/gpu_display/src/gpu_display_win/surface.rs +++ b/gpu_display/src/gpu_display_win/surface.rs @@ -24,6 +24,8 @@ use euclid::Box2D; use euclid::Size2D; use metrics::sys::windows::Metrics; use sync::Mutex; +use vm_control::gpu::DisplayMode; +use vm_control::gpu::DisplayParameters; use win_util::keys_down; use winapi::shared::minwindef::HIWORD; use winapi::shared::minwindef::LOWORD; @@ -48,10 +50,8 @@ use super::window_message_processor::SurfaceResources; use super::window_message_processor::WindowMessage; use super::window_message_processor::WindowPosMessage; use super::window_message_processor::HANDLE_WINDOW_MESSAGE_TIMEOUT; -use super::DisplayProperties; use super::HostWindowSpace; use super::MouseMode; -use super::VirtualDisplaySpace; use super::VulkanDisplayWrapper; use crate::EventDeviceKind; @@ -102,6 +102,29 @@ fn update_virtual_display_projection( } } +#[allow(dead_code)] +#[derive(Clone)] +pub(crate) struct DisplayProperties { + pub start_hidden: bool, + pub is_fullscreen: bool, + pub window_width: u32, + pub window_height: u32, +} + +impl From<&DisplayParameters> for DisplayProperties { + fn from(params: &DisplayParameters) -> Self { + let is_fullscreen = matches!(params.mode, DisplayMode::BorderlessFullScreen(_)); + let (window_width, window_height) = params.get_window_size(); + + Self { + start_hidden: params.hidden, + is_fullscreen, + window_width, + window_height, + } + } +} + pub struct Surface { surface_id: u32, mouse_input: MouseInputManager, @@ -116,9 +139,8 @@ impl Surface { pub fn new( surface_id: u32, window: &GuiWindow, - virtual_display_size: &Size2D<i32, VirtualDisplaySpace>, _metrics: Option<Weak<Metrics>>, - display_properties: &DisplayProperties, + display_params: &DisplayParameters, resources: SurfaceResources, vulkan_display: Arc<Mutex<VulkanDisplayWrapper>>, ) -> Result<Self> { @@ -130,8 +152,12 @@ impl Surface { ); let initial_host_viewport_size = window.get_client_rect().context(CONTEXT_MESSAGE)?.size; + let virtual_display_size = { + let (width, height) = display_params.get_virtual_display_size(); + size2(width, height).checked_cast() + }; let virtual_display_manager = - VirtualDisplayManager::new(&initial_host_viewport_size, virtual_display_size); + VirtualDisplayManager::new(&initial_host_viewport_size, &virtual_display_size); // This will make gfxstream initialize the child window to which it will render. update_virtual_display_projection( vulkan_display.lock(), @@ -156,7 +182,7 @@ impl Surface { mouse_input, window_manager: WindowManager::new( window, - display_properties, + &display_params.into(), initial_host_viewport_size, gpu_main_display_tube.clone(), ) diff --git a/gpu_display/src/gpu_display_win/window_manager.rs b/gpu_display/src/gpu_display_win/window_manager.rs index 006718503..f9ca33d84 100644 --- a/gpu_display/src/gpu_display_win/window_manager.rs +++ b/gpu_display/src/gpu_display_win/window_manager.rs @@ -8,9 +8,9 @@ use anyhow::Result; use base::Tube; use super::math_util::Size; +use super::surface::DisplayProperties; use super::window::GuiWindow; use super::window_message_processor::WindowPosMessage; -use super::DisplayProperties; pub(crate) struct NoopWindowManager {} diff --git a/gpu_display/src/gpu_display_win/window_procedure_thread.rs b/gpu_display/src/gpu_display_win/window_procedure_thread.rs index b84d69bf3..84253e762 100644 --- a/gpu_display/src/gpu_display_win/window_procedure_thread.rs +++ b/gpu_display/src/gpu_display_win/window_procedure_thread.rs @@ -62,11 +62,6 @@ use super::window_message_processor::*; // The default app icon id, which is defined in crosvm-manifest.rc. const APP_ICON_ID: u16 = 1; -// The number of GUI windows to pre-create when booting KiwiVM. At most this -// number of guest displays and host windows can be used concurrently. -// TODO(b/314984693): The service or the config file should specify this number. -const MAX_NUM_WINDOWS: usize = 1; - #[derive(Debug)] enum MessageLoopState { /// The initial state. @@ -241,13 +236,14 @@ impl WindowProcedureThread { // We don't implement Default for WindowProcedureThreadBuilder so that the builder function // is the only way to create WindowProcedureThreadBuilder. WindowProcedureThreadBuilder { + max_num_windows: 1, display_tube: None, #[cfg(feature = "kiwi")] ime_tube: None, } } - fn start_thread(gpu_main_display_tube: Option<Tube>) -> Result<Self> { + fn start_thread(max_num_windows: u32, gpu_main_display_tube: Option<Tube>) -> Result<Self> { let (message_router_handle_sender, message_router_handle_receiver) = channel(); let message_loop_state = Arc::new(AtomicI32::new(MessageLoopState::NotStarted as i32)); let close_requested_event = Event::new().unwrap(); @@ -262,6 +258,7 @@ impl WindowProcedureThread { .spawn(move || { match close_requested_event_clone.try_clone() { Ok(close_requested_event) => Self::run_message_loop( + max_num_windows, message_router_handle_sender, message_loop_state_clone, gpu_main_display_tube, @@ -337,6 +334,7 @@ impl WindowProcedureThread { } fn run_message_loop( + max_num_windows: u32, message_router_handle_sender: Sender<Result<u32>>, message_loop_state: Arc<AtomicI32>, gpu_main_display_tube: Option<Tube>, @@ -346,14 +344,16 @@ impl WindowProcedureThread { // SAFETY: // Safe because the dispatcher will take care of the lifetime of the `MessageOnlyWindow` and // `GuiWindow` objects. - match unsafe { Self::create_windows() }.and_then(|(message_router_window, gui_windows)| { - WindowMessageDispatcher::new( - message_router_window, - gui_windows, - gpu_main_display_tube.clone(), - close_requested_event, - ) - }) { + match unsafe { Self::create_windows(max_num_windows) }.and_then( + |(message_router_window, gui_windows)| { + WindowMessageDispatcher::new( + message_router_window, + gui_windows, + gpu_main_display_tube.clone(), + close_requested_event, + ) + }, + ) { Ok(dispatcher) => { info!("WndProc thread entering message loop"); message_loop_state.store(MessageLoopState::Running as i32, Ordering::SeqCst); @@ -542,7 +542,7 @@ impl WindowProcedureThread { /// # Safety /// The owner of the returned window objects is responsible for dropping them before we finish /// processing `WM_NCDESTROY`, because the window handle will become invalid afterwards. - unsafe fn create_windows() -> Result<(MessageOnlyWindow, Vec<GuiWindow>)> { + unsafe fn create_windows(max_num_windows: u32) -> Result<(MessageOnlyWindow, Vec<GuiWindow>)> { let message_router_window = MessageOnlyWindow::new( /* class_name */ Self::get_window_class_name::<MessageOnlyWindow>() @@ -559,10 +559,10 @@ impl WindowProcedureThread { // window may use the background brush to clear the gfxstream window client area when // drawing occurs. This caused the screen flickering issue during resizing. // See b/197786842 for details. - let mut gui_windows = Vec::with_capacity(MAX_NUM_WINDOWS); - for scanout_id in 0..MAX_NUM_WINDOWS { + let mut gui_windows = Vec::with_capacity(max_num_windows as usize); + for scanout_id in 0..max_num_windows { gui_windows.push(GuiWindow::new( - scanout_id as u32, + scanout_id, /* class_name */ Self::get_window_class_name::<GuiWindow>() .with_context(|| { @@ -650,12 +650,18 @@ unsafe impl Send for WindowProcedureThread {} #[derive(Deserialize, Serialize)] pub struct WindowProcedureThreadBuilder { + max_num_windows: u32, display_tube: Option<Tube>, #[cfg(feature = "kiwi")] ime_tube: Option<Tube>, } impl WindowProcedureThreadBuilder { + pub fn set_max_num_windows(&mut self, max_num_windows: u32) -> &mut Self { + self.max_num_windows = max_num_windows; + self + } + pub fn set_display_tube(&mut self, display_tube: Option<Tube>) -> &mut Self { self.display_tube = display_tube; self @@ -675,10 +681,16 @@ impl WindowProcedureThreadBuilder { pub fn start_thread(self) -> Result<WindowProcedureThread> { cfg_if::cfg_if! { if #[cfg(feature = "kiwi")] { - let ime_tube = self.ime_tube.ok_or_else(|| anyhow!("The ime tube is not set."))?; - WindowProcedureThread::start_thread(self.display_tube, ime_tube) + let ime_tube = self + .ime_tube + .ok_or_else(|| anyhow!("The ime tube is not set."))?; + WindowProcedureThread::start_thread( + self.max_num_windows, + self.display_tube, + ime_tube, + ) } else { - WindowProcedureThread::start_thread(None) + WindowProcedureThread::start_thread(self.max_num_windows, None) } } } diff --git a/gpu_display/src/gpu_display_wl.rs b/gpu_display/src/gpu_display_wl.rs index 61a7f9b9b..c163ee7ed 100644 --- a/gpu_display/src/gpu_display_wl.rs +++ b/gpu_display/src/gpu_display_wl.rs @@ -32,6 +32,7 @@ use base::VolatileMemory; use dwl::*; use linux_input_sys::virtio_input_event; use sync::Waitable; +use vm_control::gpu::DisplayParameters; use crate::DisplayExternalResourceImport; use crate::DisplayT; @@ -376,12 +377,12 @@ impl DisplayT for DisplayWl { parent_surface_id: Option<u32>, surface_id: u32, scanout_id: Option<u32>, - width: u32, - height: u32, + display_params: &DisplayParameters, surf_type: SurfaceType, ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> { let parent_id = parent_surface_id.unwrap_or(0); + let (width, height) = display_params.get_virtual_display_size(); let row_size = width * BYTES_PER_PIXEL; let fb_size = row_size * height; let buffer_size = round_up_to_page_size(fb_size as usize * BUFFER_COUNT); diff --git a/gpu_display/src/gpu_display_x.rs b/gpu_display/src/gpu_display_x.rs index 0cd720c94..f7560a1e9 100644 --- a/gpu_display/src/gpu_display_x.rs +++ b/gpu_display/src/gpu_display_x.rs @@ -34,6 +34,7 @@ use libc::IPC_CREAT; use libc::IPC_PRIVATE; use libc::IPC_RMID; use linux_input_sys::virtio_input_event; +use vm_control::gpu::DisplayParameters; use crate::keycode_converter::KeycodeTranslator; use crate::keycode_converter::KeycodeTypes; @@ -699,8 +700,7 @@ impl DisplayT for DisplayX { parent_surface_id: Option<u32>, _surface_id: u32, _scanout_id: Option<u32>, - width: u32, - height: u32, + display_params: &DisplayParameters, _surf_type: SurfaceType, ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>> { if parent_surface_id.is_some() { @@ -710,6 +710,7 @@ impl DisplayT for DisplayX { // TODO(b/315870313): Add safety comment #[allow(clippy::undocumented_unsafe_blocks)] unsafe { + let (width, height) = display_params.get_virtual_display_size(); let depth = xlib::XDefaultDepthOfScreen(self.screen.as_ptr()) as u32; let black_pixel = xlib::XBlackPixelOfScreen(self.screen.as_ptr()); diff --git a/gpu_display/src/lib.rs b/gpu_display/src/lib.rs index 662575fed..49656fbb5 100644 --- a/gpu_display/src/lib.rs +++ b/gpu_display/src/lib.rs @@ -22,6 +22,7 @@ use serde::Deserialize; use serde::Serialize; use sync::Waitable; use thiserror::Error; +use vm_control::gpu::DisplayParameters; use vm_control::gpu::MouseMode; #[cfg(feature = "vulkan_display")] use vulkano::VulkanLibrary; @@ -47,8 +48,6 @@ pub mod vulkan; pub use event_device::EventDevice; pub use event_device::EventDeviceKind; #[cfg(windows)] -pub use gpu_display_win::DisplayProperties as WinDisplayProperties; -#[cfg(windows)] pub use gpu_display_win::WindowProcedureThread; #[cfg(windows)] pub use gpu_display_win::WindowProcedureThreadBuilder; @@ -319,8 +318,7 @@ trait DisplayT: AsRawDescriptor { parent_surface_id: Option<u32>, surface_id: u32, scanout_id: Option<u32>, - width: u32, - height: u32, + display_params: &DisplayParameters, surf_type: SurfaceType, ) -> GpuDisplayResult<Box<dyn GpuDisplaySurface>>; @@ -564,8 +562,7 @@ impl GpuDisplay { &mut self, parent_surface_id: Option<u32>, scanout_id: Option<u32>, - width: u32, - height: u32, + display_params: &DisplayParameters, surf_type: SurfaceType, ) -> GpuDisplayResult<u32> { if let Some(parent_id) = parent_surface_id { @@ -579,8 +576,7 @@ impl GpuDisplay { parent_surface_id, new_surface_id, scanout_id, - width, - height, + display_params, surf_type, )?; diff --git a/gpu_display/src/sys/windows.rs b/gpu_display/src/sys/windows.rs index 96ad10693..bb9feb44c 100644 --- a/gpu_display/src/sys/windows.rs +++ b/gpu_display/src/sys/windows.rs @@ -11,7 +11,6 @@ use base::SendTube; use base::WaitContext; use metrics::sys::windows::Metrics; -use crate::gpu_display_win::DisplayProperties; use crate::gpu_display_win::DisplayWin; use crate::DisplayEventToken; use crate::DisplayT; @@ -65,7 +64,6 @@ pub trait WinGpuDisplayExt { fn open_winapi( wndproc_thread: WindowProcedureThread, win_metrics: Option<Weak<Metrics>>, - display_properties: DisplayProperties, gpu_display_wait_descriptor_ctrl: SendTube, vulkan_display_create_params: Option<VulkanCreateParams>, ) -> GpuDisplayResult<GpuDisplay>; @@ -75,14 +73,12 @@ impl WinGpuDisplayExt for GpuDisplay { fn open_winapi( wndproc_thread: WindowProcedureThread, win_metrics: Option<Weak<Metrics>>, - display_properties: DisplayProperties, gpu_display_wait_descriptor_ctrl: SendTube, vulkan_display_create_params: Option<VulkanCreateParams>, ) -> GpuDisplayResult<GpuDisplay> { let display = DisplayWin::new( wndproc_thread, win_metrics, - display_properties, gpu_display_wait_descriptor_ctrl, vulkan_display_create_params, )?; diff --git a/hypervisor/Android.bp b/hypervisor/Android.bp index a4409ba10..896f91eb9 100644 --- a/hypervisor/Android.bp +++ b/hypervisor/Android.bp @@ -1,14 +1,11 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "external_crosvm_license" - // to get the below license kinds: - // SPDX-license-identifier-BSD default_applicable_licenses: ["external_crosvm_license"], + default_team: "trendy_team_foundation_security_rust_pkvm_", } rust_test { @@ -103,6 +100,52 @@ rust_test { } rust_test { + name: "hypervisor_test_tests_hypervisor_virtualization", + defaults: ["crosvm_inner_defaults"], + host_supported: true, + crate_name: "hypervisor_virtualization", + cargo_env_compat: true, + cargo_pkg_version: "0.1.0", + srcs: ["tests/hypervisor_virtualization.rs"], + test_suites: ["general-tests"], + auto_gen_config: true, + test_options: { + unit_test: false, + }, + edition: "2021", + features: [ + "gdb", + "gdbstub", + "gdbstub_arch", + "geniezone", + "gunyah", + ], + rustlibs: [ + "libanyhow", + "libbase_rust", + "libbit_field", + "libbitflags", + "libcros_fdt", + "libdata_model", + "libdowncast_rs", + "libfnv", + "libgdbstub", + "libgdbstub_arch", + "libhypervisor", + "libkvm", + "libkvm_sys", + "liblibc", + "libmemoffset", + "libonce_cell", + "libserde", + "libserde_json", + "libsync_rust", + "libvm_memory", + ], + proc_macros: ["libenumn"], +} + +rust_test { name: "hypervisor_test_tests_kvm_main", defaults: ["crosvm_inner_defaults"], host_supported: true, diff --git a/hypervisor/src/aarch64.rs b/hypervisor/src/aarch64.rs index 2caa0ae0c..c838196b3 100644 --- a/hypervisor/src/aarch64.rs +++ b/hypervisor/src/aarch64.rs @@ -114,6 +114,12 @@ pub trait VcpuAArch64: Vcpu { /// Gets the value of a register on this VCPU. fn get_one_reg(&self, reg_id: VcpuRegAArch64) -> Result<u64>; + /// Sets the value of a Neon vector register (V0-V31) on this VCPU. + fn set_vector_reg(&self, reg_num: u8, data: u128) -> Result<()>; + + /// Gets the value of a Neon vector register (V0-V31) on this VCPU. + fn get_vector_reg(&self, reg_num: u8) -> Result<u128>; + /// Gets the value of MPIDR_EL1 on this VCPU. fn get_mpidr(&self) -> Result<u64> { const RES1: u64 = 1 << 31; diff --git a/hypervisor/src/geniezone/mod.rs b/hypervisor/src/geniezone/mod.rs index 07bdeeb2e..3440b6393 100644 --- a/hypervisor/src/geniezone/mod.rs +++ b/hypervisor/src/geniezone/mod.rs @@ -445,6 +445,14 @@ impl VcpuAArch64 for GeniezoneVcpu { self.get_one_geniezone_reg_u64(GeniezoneVcpuRegister::from(reg_id)) } + fn set_vector_reg(&self, _reg_num: u8, _data: u128) -> Result<()> { + unimplemented!() + } + + fn get_vector_reg(&self, _reg_num: u8) -> Result<u128> { + unimplemented!() + } + fn get_psci_version(&self) -> Result<PsciVersion> { Ok(PSCI_0_2) } diff --git a/hypervisor/src/gunyah/aarch64.rs b/hypervisor/src/gunyah/aarch64.rs index 3133d01ce..c40c36394 100644 --- a/hypervisor/src/gunyah/aarch64.rs +++ b/hypervisor/src/gunyah/aarch64.rs @@ -213,6 +213,14 @@ impl VcpuAArch64 for GunyahVcpu { Err(Error::new(ENOTSUP)) } + fn set_vector_reg(&self, _reg_num: u8, _data: u128) -> Result<()> { + unimplemented!() + } + + fn get_vector_reg(&self, _reg_num: u8) -> Result<u128> { + unimplemented!() + } + fn get_psci_version(&self) -> Result<PsciVersion> { Ok(PSCI_0_2) } diff --git a/hypervisor/src/haxm/vcpu.rs b/hypervisor/src/haxm/vcpu.rs index 91618999b..700cbb02f 100644 --- a/hypervisor/src/haxm/vcpu.rs +++ b/hypervisor/src/haxm/vcpu.rs @@ -5,6 +5,7 @@ use core::ffi::c_void; use std::arch::x86_64::CpuidResult; use std::cmp::min; +use std::collections::BTreeMap; use std::intrinsics::copy_nonoverlapping; use std::mem::size_of; @@ -27,9 +28,6 @@ use libc::EOPNOTSUPP; use vm_memory::GuestAddress; use super::*; -use crate::get_tsc_offset_from_msr; -use crate::set_tsc_offset_via_msr; -use crate::set_tsc_value_via_msr; use crate::CpuId; use crate::CpuIdEntry; use crate::DebugRegs; @@ -38,7 +36,6 @@ use crate::Fpu; use crate::HypervHypercall; use crate::IoOperation; use crate::IoParams; -use crate::Register; use crate::Regs; use crate::Segment; use crate::Sregs; @@ -112,13 +109,7 @@ impl HaxmVcpu { } // Also read efer MSR - let mut efer = vec![Register { - id: IA32_EFER, - value: 0, - }]; - - self.get_msrs(&mut efer)?; - state._efer = efer[0].value as u32; + state._efer = self.get_msr(IA32_EFER)? as u32; Ok(VcpuState { state }) } @@ -131,12 +122,7 @@ impl HaxmVcpu { } // Also set efer MSR - let efer = vec![Register { - id: IA32_EFER, - value: state.state._efer as u64, - }]; - - self.set_msrs(&efer) + self.set_msr(IA32_EFER, state.state._efer as u64) } } @@ -481,78 +467,53 @@ impl VcpuX86_64 for HaxmVcpu { } /// Gets the VCPU extended control registers. - fn get_xcrs(&self) -> Result<Vec<Register>> { - // Haxm does not support setting XCRs + fn get_xcrs(&self) -> Result<BTreeMap<u32, u64>> { + // Haxm does not support getting XCRs Err(Error::new(libc::ENXIO)) } - /// Sets the VCPU extended control registers. - fn set_xcrs(&self, _xcrs: &[Register]) -> Result<()> { + /// Sets a VCPU extended control register. + fn set_xcr(&self, _xcr_index: u32, _value: u64) -> Result<()> { // Haxm does not support setting XCRs Err(Error::new(libc::ENXIO)) } - /// Gets the model-specific registers. `msrs` specifies the MSR indexes to be queried, and - /// on success contains their indexes and values. - fn get_msrs(&self, msrs: &mut Vec<Register>) -> Result<()> { - // HAX_VCPU_IOCTL_GET_MSRS only allows you to set HAX_MAX_MSR_ARRAY-1 msrs at a time - // TODO (b/163811378): the fact that you can only set HAX_MAX_MSR_ARRAY-1 seems like a - // bug with HAXM, since the entries array itself is HAX_MAX_MSR_ARRAY long - for chunk in msrs.chunks_mut((HAX_MAX_MSR_ARRAY - 1) as usize) { - let chunk_size = chunk.len(); - let hax_chunk: Vec<vmx_msr> = chunk.iter().map(vmx_msr::from).collect(); - - let mut msr_data = hax_msr_data { - nr_msr: chunk_size as u16, - ..Default::default() - }; - - // Copy chunk into msr_data - msr_data.entries[..chunk_size].copy_from_slice(&hax_chunk); - - // TODO(b/315998194): Add safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let ret = unsafe { ioctl_with_mut_ref(self, HAX_VCPU_IOCTL_GET_MSRS(), &mut msr_data) }; - if ret != 0 { - return errno_result(); - } + /// Gets the value of one model-specific register. + fn get_msr(&self, msr_index: u32) -> Result<u64> { + let mut msr_data = hax_msr_data { + nr_msr: 1, + ..Default::default() + }; + msr_data.entries[0].entry = u64::from(msr_index); - // copy values we got from kernel - for (i, item) in chunk.iter_mut().enumerate().take(chunk_size) { - item.value = msr_data.entries[i].value; - } + // TODO(b/315998194): Add safety comment + #[allow(clippy::undocumented_unsafe_blocks)] + let ret = unsafe { ioctl_with_mut_ref(self, HAX_VCPU_IOCTL_GET_MSRS(), &mut msr_data) }; + if ret != 0 { + return errno_result(); } - Ok(()) + Ok(msr_data.entries[0].value) } - fn get_all_msrs(&self) -> Result<Vec<Register>> { + fn get_all_msrs(&self) -> Result<BTreeMap<u32, u64>> { Err(Error::new(EOPNOTSUPP)) } - /// Sets the model-specific registers. - fn set_msrs(&self, msrs: &[Register]) -> Result<()> { - // HAX_VCPU_IOCTL_GET_MSRS only allows you to set HAX_MAX_MSR_ARRAY-1 msrs at a time - // TODO (b/163811378): the fact that you can only set HAX_MAX_MSR_ARRAY-1 seems like a - // bug with HAXM, since the entries array itself is HAX_MAX_MSR_ARRAY long - for chunk in msrs.chunks((HAX_MAX_MSR_ARRAY - 1) as usize) { - let chunk_size = chunk.len(); - let hax_chunk: Vec<vmx_msr> = chunk.iter().map(vmx_msr::from).collect(); - - let mut msr_data = hax_msr_data { - nr_msr: chunk_size as u16, - ..Default::default() - }; - - // Copy chunk into msr_data - msr_data.entries[..chunk_size].copy_from_slice(&hax_chunk); - - // TODO(b/315998194): Add safety comment - #[allow(clippy::undocumented_unsafe_blocks)] - let ret = unsafe { ioctl_with_mut_ref(self, HAX_VCPU_IOCTL_SET_MSRS(), &mut msr_data) }; - if ret != 0 { - return errno_result(); - } + /// Sets the value of one model-specific register. + fn set_msr(&self, msr_index: u32, value: u64) -> Result<()> { + let mut msr_data = hax_msr_data { + nr_msr: 1, + ..Default::default() + }; + msr_data.entries[0].entry = u64::from(msr_index); + msr_data.entries[0].value = value; + + // TODO(b/315998194): Add safety comment + #[allow(clippy::undocumented_unsafe_blocks)] + let ret = unsafe { ioctl_with_mut_ref(self, HAX_VCPU_IOCTL_SET_MSRS(), &mut msr_data) }; + if ret != 0 { + return errno_result(); } Ok(()) @@ -606,16 +567,6 @@ impl VcpuX86_64 for HaxmVcpu { Err(Error::new(ENOENT)) } - fn get_tsc_offset(&self) -> Result<u64> { - // Use the default MSR-based implementation - get_tsc_offset_from_msr(self) - } - - fn set_tsc_offset(&self, offset: u64) -> Result<()> { - // Use the default MSR-based implementation - set_tsc_offset_via_msr(self, offset) - } - fn restore_timekeeping(&self, _host_tsc_reference_moment: u64, tsc_offset: u64) -> Result<()> { // HAXM sets TSC_OFFSET based on what we set TSC to; however, it does // not yet handle syncing. This means it computes @@ -630,10 +581,6 @@ impl VcpuX86_64 for HaxmVcpu { // offset directly.) self.set_tsc_offset(tsc_offset) } - - fn set_tsc_value(&self, value: u64) -> Result<()> { - set_tsc_value_via_msr(self, value) - } } struct VcpuState { @@ -1004,24 +951,6 @@ impl From<&CpuIdEntry> for hax_cpuid_entry { } } -impl From<&vmx_msr> for Register { - fn from(item: &vmx_msr) -> Register { - Register { - id: item.entry as u32, - value: item.value, - } - } -} - -impl From<&Register> for vmx_msr { - fn from(item: &Register) -> vmx_msr { - vmx_msr { - entry: item.id as u64, - value: item.value, - } - } -} - // TODO(b:241252288): Enable tests disabled with dummy feature flag - enable_haxm_tests. #[cfg(test)] #[cfg(feature = "enable_haxm_tests")] @@ -1063,42 +992,25 @@ mod tests { } #[test] - fn set_many_msrs() { + fn set_msr() { let haxm = Haxm::new().expect("failed to instantiate HAXM"); let mem = GuestMemory::new(&[(GuestAddress(0), 0x1000)]).expect("failed to create guest memory"); let vm = HaxmVm::new(&haxm, mem).expect("failed to create vm"); let vcpu = vm.create_vcpu(0).expect("failed to create vcpu"); - let mut registers: Vec<Register> = Vec::new(); - for id in 0x300..0x3ff { - registers.push(Register { - id: 38, - value: id as u64, - }); - } - - vcpu.set_msrs(®isters).expect("failed to set registers"); + vcpu.set_msr(38, 0x300).expect("failed to set MSR"); } #[test] - fn get_many_msrs() { + fn get_msr() { let haxm = Haxm::new().expect("failed to instantiate HAXM"); let mem = GuestMemory::new(&[(GuestAddress(0), 0x1000)]).expect("failed to create guest memory"); let vm = HaxmVm::new(&haxm, mem).expect("failed to create vm"); let vcpu = vm.create_vcpu(0).expect("failed to create vcpu"); - let mut registers: Vec<Register> = Vec::new(); - for id in 0x300..0x3ff { - registers.push(Register { - id: 38, - value: id as u64, - }); - } - - vcpu.get_msrs(&mut registers) - .expect("failed to get registers"); + let _value = vcpu.get_msr(38).expect("failed to get MSR"); } #[test] @@ -1148,21 +1060,17 @@ mod tests { assert_eq!(sregs.efer, EFER_LMA | EFER_LME); // IA32_EFER register value should match - let mut efer_reg = vec![Register { - id: IA32_EFER, - value: 0, - }]; - vcpu.get_msrs(&mut efer_reg).expect("failed to get msrs"); - assert_eq!(efer_reg[0].value, EFER_LMA | EFER_LME); + let efer = vcpu.get_msr(IA32_EFER).expect("failed to get msr"); + assert_eq!(efer, EFER_LMA | EFER_LME); // Enable SCE via set_msrs - efer_reg[0].value |= EFER_SCE; - vcpu.set_msrs(&efer_reg).expect("failed to set msrs"); + vcpu.set_msr(IA32_EFER, efer | EFER_SCE) + .expect("failed to set msr"); // Verify that setting stuck let sregs = vcpu.get_sregs().expect("failed to get sregs"); assert_eq!(sregs.efer, EFER_SCE | EFER_LME | EFER_LMA); - vcpu.get_msrs(&mut efer_reg).expect("failed to get msrs"); - assert_eq!(efer_reg[0].value, EFER_SCE | EFER_LME | EFER_LMA); + let new_efer = vcpu.get_msr(IA32_EFER).expect("failed to get msrs"); + assert_eq!(new_efer, EFER_SCE | EFER_LME | EFER_LMA); } } diff --git a/hypervisor/src/kvm/aarch64.rs b/hypervisor/src/kvm/aarch64.rs index 803bd6db4..5d1a2b8ed 100644 --- a/hypervisor/src/kvm/aarch64.rs +++ b/hypervisor/src/kvm/aarch64.rs @@ -257,6 +257,10 @@ impl KvmVcpu { self.set_one_kvm_reg(kvm_reg_id, data.to_ne_bytes().as_slice()) } + fn set_one_kvm_reg_u128(&self, kvm_reg_id: KvmVcpuRegister, data: u128) -> Result<()> { + self.set_one_kvm_reg(kvm_reg_id, data.to_ne_bytes().as_slice()) + } + fn set_one_kvm_reg(&self, kvm_reg_id: KvmVcpuRegister, data: &[u8]) -> Result<()> { let onereg = kvm_one_reg { id: kvm_reg_id.into(), @@ -281,6 +285,12 @@ impl KvmVcpu { Ok(u64::from_ne_bytes(bytes)) } + fn get_one_kvm_reg_u128(&self, kvm_reg_id: KvmVcpuRegister) -> Result<u128> { + let mut bytes = 0u128.to_ne_bytes(); + self.get_one_kvm_reg(kvm_reg_id, bytes.as_mut_slice())?; + Ok(u128::from_ne_bytes(bytes)) + } + fn get_one_kvm_reg(&self, kvm_reg_id: KvmVcpuRegister, data: &mut [u8]) -> Result<()> { let onereg = kvm_one_reg { id: kvm_reg_id.into(), @@ -307,22 +317,12 @@ impl KvmVcpu { self.set_one_kvm_reg(kvm_reg_id, data.to_ne_bytes().as_slice()) } - fn set_one_kvm_reg_u128(&self, kvm_reg_id: KvmVcpuRegister, data: u128) -> Result<()> { - self.set_one_kvm_reg(kvm_reg_id, data.to_ne_bytes().as_slice()) - } - fn get_one_kvm_reg_u32(&self, kvm_reg_id: KvmVcpuRegister) -> Result<u32> { let mut bytes = 0u32.to_ne_bytes(); self.get_one_kvm_reg(kvm_reg_id, bytes.as_mut_slice())?; Ok(u32::from_ne_bytes(bytes)) } - fn get_one_kvm_reg_u128(&self, kvm_reg_id: KvmVcpuRegister) -> Result<u128> { - let mut bytes = 0u128.to_ne_bytes(); - self.get_one_kvm_reg(kvm_reg_id, bytes.as_mut_slice())?; - Ok(u128::from_ne_bytes(bytes)) - } - /// Retrieves the value of the currently active "version" of a multiplexed registers. fn demux_register(&self, reg: &<GdbArch as Arch>::RegId) -> Result<Option<KvmVcpuRegister>> { match *reg { @@ -692,6 +692,20 @@ impl VcpuAArch64 for KvmVcpu { self.get_one_kvm_reg_u64(KvmVcpuRegister::from(reg_id)) } + fn set_vector_reg(&self, reg_num: u8, data: u128) -> Result<()> { + if reg_num > 31 { + return Err(Error::new(EINVAL)); + } + self.set_one_kvm_reg_u128(KvmVcpuRegister::V(reg_num), data) + } + + fn get_vector_reg(&self, reg_num: u8) -> Result<u128> { + if reg_num > 31 { + return Err(Error::new(EINVAL)); + } + self.get_one_kvm_reg_u128(KvmVcpuRegister::V(reg_num)) + } + fn get_mpidr(&self) -> Result<u64> { self.get_one_kvm_reg_u64(KvmVcpuRegister::MPIDR_EL1) } @@ -798,7 +812,7 @@ impl VcpuAArch64 for KvmVcpu { self.set_one_kvm_reg_u64(KvmVcpuRegister::Pstate, pstate)?; for (i, reg) in regs.v.iter().enumerate() { let n = u8::try_from(i).expect("invalid Vn general purpose register index"); - self.set_one_kvm_reg_u128(KvmVcpuRegister::V(n), *reg)?; + self.set_vector_reg(n, *reg)?; } self.set_one_kvm_reg_u32(KvmVcpuRegister::Fpcr, regs.fpcr)?; self.set_one_kvm_reg_u32(KvmVcpuRegister::Fpsr, regs.fpsr)?; @@ -822,7 +836,7 @@ impl VcpuAArch64 for KvmVcpu { regs.cpsr = self.get_one_kvm_reg_u64(KvmVcpuRegister::Pstate)? as u32; for (i, reg) in regs.v.iter_mut().enumerate() { let n = u8::try_from(i).expect("invalid Vn general purpose register index"); - *reg = self.get_one_kvm_reg_u128(KvmVcpuRegister::V(n))?; + *reg = self.get_vector_reg(n)?; } regs.fpcr = self.get_one_kvm_reg_u32(KvmVcpuRegister::Fpcr)?; regs.fpsr = self.get_one_kvm_reg_u32(KvmVcpuRegister::Fpsr)?; diff --git a/hypervisor/src/kvm/x86_64.rs b/hypervisor/src/kvm/x86_64.rs index 3ccb123c2..f9a3b2626 100644 --- a/hypervisor/src/kvm/x86_64.rs +++ b/hypervisor/src/kvm/x86_64.rs @@ -3,6 +3,7 @@ // found in the LICENSE file. use std::arch::x86_64::CpuidResult; +use std::collections::BTreeMap; use base::errno_result; use base::error; @@ -30,10 +31,7 @@ use super::Config; use super::Kvm; use super::KvmVcpu; use super::KvmVm; -use crate::get_tsc_offset_from_msr; use crate::host_phys_addr_bits; -use crate::set_tsc_offset_via_msr; -use crate::set_tsc_value_via_msr; use crate::ClockState; use crate::CpuId; use crate::CpuIdEntry; @@ -51,7 +49,6 @@ use crate::PicState; use crate::PitChannelState; use crate::PitState; use crate::ProtectionType; -use crate::Register; use crate::Regs; use crate::Segment; use crate::Sregs; @@ -883,25 +880,36 @@ impl VcpuX86_64 for KvmVcpu { } } - fn get_xcrs(&self) -> Result<Vec<Register>> { + fn get_xcrs(&self) -> Result<BTreeMap<u32, u64>> { let mut regs: kvm_xcrs = Default::default(); // SAFETY: // Safe because we know that our file is a VCPU fd, we know the kernel will only write the // correct amount of memory to our pointer, and we verify the return result. let ret = unsafe { ioctl_with_mut_ref(self, KVM_GET_XCRS(), &mut regs) }; - if ret == 0 { - Ok(from_kvm_xcrs(®s)) - } else { - errno_result() + if ret < 0 { + return errno_result(); } + + Ok(regs + .xcrs + .iter() + .take(regs.nr_xcrs as usize) + .map(|kvm_xcr| (kvm_xcr.xcr, kvm_xcr.value)) + .collect()) } - fn set_xcrs(&self, xcrs: &[Register]) -> Result<()> { - let xcrs = to_kvm_xcrs(xcrs); + fn set_xcr(&self, xcr_index: u32, value: u64) -> Result<()> { + let mut kvm_xcr = kvm_xcrs { + nr_xcrs: 1, + ..Default::default() + }; + kvm_xcr.xcrs[0].xcr = xcr_index; + kvm_xcr.xcrs[0].value = value; + let ret = { // SAFETY: // Here we trust the kernel not to read past the end of the kvm_xcrs struct. - unsafe { ioctl_with_ref(self, KVM_SET_XCRS(), &xcrs) } + unsafe { ioctl_with_ref(self, KVM_SET_XCRS(), &kvm_xcr) } }; if ret == 0 { Ok(()) @@ -910,78 +918,117 @@ impl VcpuX86_64 for KvmVcpu { } } - fn get_msrs(&self, vec: &mut Vec<Register>) -> Result<()> { - let msrs = to_kvm_msrs(vec); + fn get_msr(&self, msr_index: u32) -> Result<u64> { + let mut msrs = vec_with_array_field::<kvm_msrs, kvm_msr_entry>(1); + msrs[0].nmsrs = 1; + + // SAFETY: We initialize a one-element array using `vec_with_array_field` above. + unsafe { + let msr_entries = msrs[0].entries.as_mut_slice(1); + msr_entries[0].index = msr_index; + } + let ret = { // SAFETY: // Here we trust the kernel not to read or write past the end of the kvm_msrs struct. unsafe { ioctl_with_ref(self, KVM_GET_MSRS(), &msrs[0]) } }; - // KVM_GET_MSRS actually returns the number of msr entries written. if ret < 0 { return errno_result(); } + + // KVM_GET_MSRS returns the number of msr entries written. + if ret != 1 { + return Err(base::Error::new(libc::ENOENT)); + } + // SAFETY: // Safe because we trust the kernel to return the correct array length on success. - let entries = unsafe { - let count = ret as usize; - assert!(count <= vec.len()); - msrs[0].entries.as_slice(count) + let value = unsafe { + let msr_entries = msrs[0].entries.as_slice(1); + msr_entries[0].data }; - vec.truncate(0); - vec.extend(entries.iter().map(|e| Register { - id: e.index, - value: e.data, - })); - Ok(()) + + Ok(value) } - fn get_all_msrs(&self) -> Result<Vec<Register>> { - let mut msrs: Vec<_> = self - .kvm - .get_msr_index_list()? - .into_iter() - .map(|i| Register { id: i, value: 0 }) - .collect(); - let count = msrs.len(); - self.get_msrs(&mut msrs)?; - if msrs.len() != count { + fn get_all_msrs(&self) -> Result<BTreeMap<u32, u64>> { + let msr_index_list = self.kvm.get_msr_index_list()?; + let mut kvm_msrs = vec_with_array_field::<kvm_msrs, kvm_msr_entry>(msr_index_list.len()); + kvm_msrs[0].nmsrs = msr_index_list.len() as u32; + // SAFETY: + // Mapping the unsized array to a slice is unsafe because the length isn't known. + // Providing the length used to create the struct guarantees the entire slice is valid. + unsafe { + kvm_msrs[0] + .entries + .as_mut_slice(msr_index_list.len()) + .iter_mut() + .zip(msr_index_list.iter()) + .for_each(|(msr_entry, msr_index)| msr_entry.index = *msr_index); + } + + let ret = { + // SAFETY: + // Here we trust the kernel not to read or write past the end of the kvm_msrs struct. + unsafe { ioctl_with_ref(self, KVM_GET_MSRS(), &kvm_msrs[0]) } + }; + if ret < 0 { + return errno_result(); + } + + // KVM_GET_MSRS returns the number of msr entries written. + let count = ret as usize; + if count != msr_index_list.len() { error!( "failed to get all MSRs: requested {}, got {}", + msr_index_list.len(), count, - msrs.len() ); return Err(base::Error::new(libc::EPERM)); } + + // SAFETY: + // Safe because we trust the kernel to return the correct array length on success. + let msrs = unsafe { + BTreeMap::from_iter( + kvm_msrs[0] + .entries + .as_slice(count) + .iter() + .map(|kvm_msr| (kvm_msr.index, kvm_msr.data)), + ) + }; + Ok(msrs) } - fn set_msrs(&self, vec: &[Register]) -> Result<()> { - let msrs = to_kvm_msrs(vec); + fn set_msr(&self, msr_index: u32, value: u64) -> Result<()> { + let mut kvm_msrs = vec_with_array_field::<kvm_msrs, kvm_msr_entry>(1); + kvm_msrs[0].nmsrs = 1; + + // SAFETY: We initialize a one-element array using `vec_with_array_field` above. + unsafe { + let msr_entries = kvm_msrs[0].entries.as_mut_slice(1); + msr_entries[0].index = msr_index; + msr_entries[0].data = value; + } + let ret = { // SAFETY: // Here we trust the kernel not to read past the end of the kvm_msrs struct. - unsafe { ioctl_with_ref(self, KVM_SET_MSRS(), &msrs[0]) } + unsafe { ioctl_with_ref(self, KVM_SET_MSRS(), &kvm_msrs[0]) } }; - // KVM_SET_MSRS actually returns the number of msr entries written. if ret < 0 { return errno_result(); } - let num_set = ret as usize; - if num_set != vec.len() { - if let Some(register) = vec.get(num_set) { - error!( - "failed to set MSR {:#x?} to {:#x?}", - register.id, register.value - ); - } else { - error!( - "unexpected KVM_SET_MSRS return value {num_set} (nmsrs={})", - vec.len() - ); - } + + // KVM_SET_MSRS returns the number of msr entries written. + if ret != 1 { + error!("failed to set MSR {:#x} to {:#x}", msr_index, value); return Err(base::Error::new(libc::EPERM)); } + Ok(()) } @@ -1049,10 +1096,6 @@ impl VcpuX86_64 for KvmVcpu { Err(Error::new(ENXIO)) } - fn set_tsc_value(&self, value: u64) -> Result<()> { - set_tsc_value_via_msr(self, value) - } - fn restore_timekeeping(&self, _host_tsc_reference_moment: u64, tsc_offset: u64) -> Result<()> { // In theory, KVM requires no extra handling beyond restoring the TSC // MSR, which happens separately because TSC is in the all MSR list for @@ -1065,16 +1108,6 @@ impl VcpuX86_64 for KvmVcpu { // research is required.) self.set_tsc_offset(tsc_offset) } - - fn get_tsc_offset(&self) -> Result<u64> { - // Use the default MSR-based implementation - get_tsc_offset_from_msr(self) - } - - fn set_tsc_offset(&self, offset: u64) -> Result<()> { - // Use the default MSR-based implementation - set_tsc_offset_via_msr(self, offset) - } } impl KvmVcpu { @@ -1117,25 +1150,14 @@ impl KvmVcpu { /// /// See the documentation for The kvm_run structure, and for KVM_GET_LAPIC. pub fn get_apic_base(&self) -> Result<u64> { - let mut apic_base = vec![Register { - id: MSR_IA32_APICBASE, - value: 0, - }]; - self.get_msrs(&mut apic_base)?; - match apic_base.get(0) { - Some(base) => Ok(base.value), - None => Err(Error::new(EIO)), - } + self.get_msr(MSR_IA32_APICBASE) } /// X86 specific call to set the value of the APIC_BASE MSR. /// /// See the documentation for The kvm_run structure, and for KVM_GET_LAPIC. pub fn set_apic_base(&self, apic_base: u64) -> Result<()> { - self.set_msrs(&[Register { - id: MSR_IA32_APICBASE, - value: apic_base, - }]) + self.set_msr(MSR_IA32_APICBASE, apic_base) } /// Call to get pending interrupts acknowledged by the APIC but not yet injected into the CPU. @@ -1786,53 +1808,6 @@ impl From<&DebugRegs> for kvm_debugregs { } } -fn from_kvm_xcrs(r: &kvm_xcrs) -> Vec<Register> { - r.xcrs - .iter() - .take(r.nr_xcrs as usize) - .map(|x| Register { - id: x.xcr, - value: x.value, - }) - .collect() -} - -fn to_kvm_xcrs(r: &[Register]) -> kvm_xcrs { - let mut kvm = kvm_xcrs { - nr_xcrs: r.len() as u32, - ..Default::default() - }; - for (i, &xcr) in r.iter().enumerate() { - kvm.xcrs[i].xcr = xcr.id; - kvm.xcrs[i].value = xcr.value; - } - kvm -} - -fn to_kvm_msrs(vec: &[Register]) -> Vec<kvm_msrs> { - let vec: Vec<kvm_msr_entry> = vec - .iter() - .map(|e| kvm_msr_entry { - index: e.id, - data: e.value, - ..Default::default() - }) - .collect(); - - let mut msrs = vec_with_array_field::<kvm_msrs, kvm_msr_entry>(vec.len()); - // SAFETY: - // Mapping the unsized array to a slice is unsafe because the length isn't known. - // Providing the length used to create the struct guarantees the entire slice is valid. - unsafe { - msrs[0] - .entries - .as_mut_slice(vec.len()) - .copy_from_slice(&vec); - } - msrs[0].nmsrs = vec.len() as u32; - msrs -} - #[cfg(test)] mod tests { use super::*; diff --git a/hypervisor/src/whpx/vcpu.rs b/hypervisor/src/whpx/vcpu.rs index b3826ef13..fd3e93bb2 100644 --- a/hypervisor/src/whpx/vcpu.rs +++ b/hypervisor/src/whpx/vcpu.rs @@ -4,6 +4,7 @@ use core::ffi::c_void; use std::arch::x86_64::CpuidResult; +use std::collections::BTreeMap; use std::convert::TryInto; use std::mem::size_of; use std::sync::Arc; @@ -20,9 +21,6 @@ use windows::Win32::Foundation::WHV_E_INSUFFICIENT_BUFFER; use super::types::*; use super::*; -use crate::get_tsc_offset_from_msr; -use crate::set_tsc_offset_via_msr; -use crate::set_tsc_value_via_msr; use crate::CpuId; use crate::CpuIdEntry; use crate::DebugRegs; @@ -30,7 +28,6 @@ use crate::Fpu; use crate::HypervHypercall; use crate::IoOperation; use crate::IoParams; -use crate::Register; use crate::Regs; use crate::Sregs; use crate::Vcpu; @@ -1083,83 +1080,71 @@ impl VcpuX86_64 for WhpxVcpu { } /// Gets the VCPU extended control registers. - fn get_xcrs(&self) -> Result<Vec<Register>> { - const REG_NAMES: [WHV_REGISTER_NAME; 1] = [WHV_REGISTER_NAME_WHvX64RegisterXCr0]; - let mut xcrs: [WHV_REGISTER_VALUE; 1] = Default::default(); + fn get_xcrs(&self) -> Result<BTreeMap<u32, u64>> { + const REG_NAME: WHV_REGISTER_NAME = WHV_REGISTER_NAME_WHvX64RegisterXCr0; + let mut reg_value = WHV_REGISTER_VALUE::default(); // safe because we have enough space for all the registers in whpx_regs check_whpx!(unsafe { WHvGetVirtualProcessorRegisters( self.vm_partition.partition, self.index, - ®_NAMES as *const WHV_REGISTER_NAME, - REG_NAMES.len() as u32, - xcrs.as_mut_ptr(), + ®_NAME, + /* RegisterCount */ 1, + &mut reg_value, ) })?; - let reg = Register { - id: 0, // whpx only supports xcr0 - // safe because the union value, reg64, is safe to pull out assuming - // kernel filled in the xcrs properly. - value: unsafe { xcrs[0].Reg64 }, - }; - Ok(vec![reg]) + + // safe because the union value, reg64, is safe to pull out assuming + // kernel filled in the xcrs properly. + let xcr0 = unsafe { reg_value.Reg64 }; + + // whpx only supports xcr0 + let xcrs = BTreeMap::from([(0, xcr0)]); + Ok(xcrs) } - /// Sets the VCPU extended control registers. - fn set_xcrs(&self, xcrs: &[Register]) -> Result<()> { - const REG_NAMES: [WHV_REGISTER_NAME; 1] = [WHV_REGISTER_NAME_WHvX64RegisterXCr0]; - let whpx_xcrs = xcrs - .iter() - .filter_map(|reg| match reg.id { - 0 => Some(WHV_REGISTER_VALUE { Reg64: reg.value }), - _ => None, - }) - .collect::<Vec<WHV_REGISTER_VALUE>>(); - if !whpx_xcrs.is_empty() { - // safe because we have enough space for all the registers in whpx_xcrs - check_whpx!(unsafe { - WHvSetVirtualProcessorRegisters( - self.vm_partition.partition, - self.index, - ®_NAMES as *const WHV_REGISTER_NAME, - REG_NAMES.len() as u32, - whpx_xcrs.as_ptr(), - ) - }) - } else { + /// Sets a VCPU extended control register. + fn set_xcr(&self, xcr_index: u32, value: u64) -> Result<()> { + if xcr_index != 0 { // invalid xcr register provided - Err(Error::new(ENXIO)) + return Err(Error::new(EINVAL)); } + + const REG_NAME: WHV_REGISTER_NAME = WHV_REGISTER_NAME_WHvX64RegisterXCr0; + let reg_value = WHV_REGISTER_VALUE { Reg64: value }; + // safe because we have enough space for all the registers in whpx_xcrs + check_whpx!(unsafe { + WHvSetVirtualProcessorRegisters( + self.vm_partition.partition, + self.index, + ®_NAME, + /* RegisterCount */ 1, + ®_value, + ) + }) } - /// Gets the model-specific registers. `msrs` specifies the MSR indexes to be queried, and - /// on success contains their indexes and values. - fn get_msrs(&self, msrs: &mut Vec<Register>) -> Result<()> { - let msr_names = get_msr_names(msrs); - let mut buffer: Vec<WHV_REGISTER_VALUE> = vec![Default::default(); msr_names.len()]; + /// Gets the value of a single model-specific register. + fn get_msr(&self, msr_index: u32) -> Result<u64> { + let msr_name = get_msr_name(msr_index).ok_or(Error::new(libc::ENOENT))?; + let mut msr_value = WHV_REGISTER_VALUE::default(); // safe because we have enough space for all the registers in whpx_regs check_whpx!(unsafe { WHvGetVirtualProcessorRegisters( self.vm_partition.partition, self.index, - msr_names.as_ptr(), - msr_names.len() as u32, - buffer.as_mut_ptr(), + &msr_name, + /* RegisterCount */ 1, + &mut msr_value, ) })?; - msrs.retain(|&msr| VALID_MSRS.contains_key(&msr.id)); - if buffer.len() != msrs.len() { - panic!("mismatch of valid whpx msr registers and returned registers"); - } - for (i, msr) in msrs.iter_mut().enumerate() { - // safe because Reg64 will be a valid union value for all msrs - msr.value = unsafe { buffer[i].Reg64 }; - } - Ok(()) + // safe because Reg64 will be a valid union value + let value = unsafe { msr_value.Reg64 }; + Ok(value) } - fn get_all_msrs(&self) -> Result<Vec<Register>> { + fn get_all_msrs(&self) -> Result<BTreeMap<u32, u64>> { // Note that some members of VALID_MSRS cannot be fetched from WHPX with // WHvGetVirtualProcessorRegisters per the HTLFS, so we enumerate all of // permitted MSRs here. @@ -1172,73 +1157,41 @@ impl VcpuX86_64 for WhpxVcpu { // handled by the generic x86_64 VCPU snapshot/restore. Non snapshot // consumers should use get/set_tsc_adjust to access the adjust register // if needed. - let mut registers = vec![ - Register { - id: MSR_EFER, - ..Default::default() - }, - Register { - id: MSR_KERNEL_GS_BASE, - ..Default::default() - }, - Register { - id: MSR_APIC_BASE, - ..Default::default() - }, - Register { - id: MSR_SYSENTER_CS, - ..Default::default() - }, - Register { - id: MSR_SYSENTER_EIP, - ..Default::default() - }, - Register { - id: MSR_SYSENTER_ESP, - ..Default::default() - }, - Register { - id: MSR_STAR, - ..Default::default() - }, - Register { - id: MSR_LSTAR, - ..Default::default() - }, - Register { - id: MSR_CSTAR, - ..Default::default() - }, - Register { - id: MSR_SFMASK, - ..Default::default() - }, + const MSRS_TO_SAVE: &[u32] = &[ + MSR_EFER, + MSR_KERNEL_GS_BASE, + MSR_APIC_BASE, + MSR_SYSENTER_CS, + MSR_SYSENTER_EIP, + MSR_SYSENTER_ESP, + MSR_STAR, + MSR_LSTAR, + MSR_CSTAR, + MSR_SFMASK, ]; - self.get_msrs(&mut registers)?; - Ok(registers) - } - /// Sets the model-specific registers. - fn set_msrs(&self, msrs: &[Register]) -> Result<()> { - let msr_names = get_msr_names(msrs); - let whpx_msrs = msrs + let registers = MSRS_TO_SAVE .iter() - .filter_map(|msr| { - if VALID_MSRS.contains_key(&msr.id) { - Some(WHV_REGISTER_VALUE { Reg64: msr.value }) - } else { - None - } + .map(|msr_index| { + let value = self.get_msr(*msr_index)?; + Ok((*msr_index, value)) }) - .collect::<Vec<WHV_REGISTER_VALUE>>(); + .collect::<Result<BTreeMap<u32, u64>>>()?; + Ok(registers) + } + + /// Sets the value of a single model-specific register. + fn set_msr(&self, msr_index: u32, value: u64) -> Result<()> { + let msr_name = get_msr_name(msr_index).ok_or(Error::new(libc::ENOENT))?; + let msr_value = WHV_REGISTER_VALUE { Reg64: value }; check_whpx!(unsafe { WHvSetVirtualProcessorRegisters( self.vm_partition.partition, self.index, - msr_names.as_ptr(), - msr_names.len() as u32, - whpx_msrs.as_ptr(), + &msr_name, + /* RegisterCount */ 1, + &msr_value, ) }) } @@ -1311,23 +1264,6 @@ impl VcpuX86_64 for WhpxVcpu { Err(Error::new(ENOENT)) } - fn get_tsc_offset(&self) -> Result<u64> { - // Note: WHV_REGISTER_NAME_WHvX64RegisterTscVirtualOffset register appears to no longer be - // supported, so we use the MSR path. (It also didn't work in 19H2 either, always returning - // zero on get.) - get_tsc_offset_from_msr(self) - } - - fn set_tsc_offset(&self, offset: u64) -> Result<()> { - // Note: WHV_REGISTER_NAME_WHvX64RegisterTscVirtualOffset register appears to no longer be - // supported, so we use the MSR path. - set_tsc_offset_via_msr(self, offset) - } - - fn set_tsc_value(&self, value: u64) -> Result<()> { - set_tsc_value_via_msr(self, value) - } - fn restore_timekeeping(&self, host_tsc_reference_moment: u64, tsc_offset: u64) -> Result<()> { // Set the guest TSC such that it has the same TSC_OFFSET as it did at // the moment it was snapshotted. This is required for virtio-pvclock @@ -1338,10 +1274,8 @@ impl VcpuX86_64 for WhpxVcpu { } } -fn get_msr_names(msrs: &[Register]) -> Vec<WHV_REGISTER_NAME> { - msrs.iter() - .filter_map(|reg| VALID_MSRS.get(®.id).copied()) - .collect::<Vec<WHV_REGISTER_NAME>>() +fn get_msr_name(msr_index: u32) -> Option<WHV_REGISTER_NAME> { + VALID_MSRS.get(&msr_index).copied() } // run calls are tested with the integration tests since the full vcpu needs to be setup for it. @@ -1503,15 +1437,14 @@ mod tests { return; } - let mut xcrs = vcpu.get_xcrs().unwrap(); - xcrs[0].value = 1; - vcpu.set_xcrs(&xcrs).unwrap(); - let xcrs2 = vcpu.get_xcrs().unwrap(); - assert_eq!(xcrs[0].value, xcrs2[0].value); + vcpu.set_xcr(0, 1).unwrap(); + let xcrs = vcpu.get_xcrs().unwrap(); + let xcr0 = xcrs.get(&0).unwrap(); + assert_eq!(*xcr0, 1); } #[test] - fn set_msrs() { + fn set_msr() { if !Whpx::is_enabled() { return; } @@ -1521,21 +1454,14 @@ mod tests { let vm = new_vm(cpu_count, mem); let vcpu = vm.create_vcpu(0).expect("failed to create vcpu"); - let mut msrs = vec![Register { - id: MSR_KERNEL_GS_BASE, - value: 42, - }]; - vcpu.set_msrs(&msrs).unwrap(); + vcpu.set_msr(MSR_KERNEL_GS_BASE, 42).unwrap(); - msrs[0].value = 0; - vcpu.get_msrs(&mut msrs).unwrap(); - assert_eq!(msrs.len(), 1); - assert_eq!(msrs[0].id, MSR_KERNEL_GS_BASE); - assert_eq!(msrs[0].value, 42); + let gs_base = vcpu.get_msr(MSR_KERNEL_GS_BASE).unwrap(); + assert_eq!(gs_base, 42); } #[test] - fn get_msrs() { + fn get_msr() { if !Whpx::is_enabled() { return; } @@ -1545,20 +1471,12 @@ mod tests { let vm = new_vm(cpu_count, mem); let vcpu = vm.create_vcpu(0).expect("failed to create vcpu"); - let mut msrs = vec![ - // This one should succeed - Register { - id: MSR_TSC, - ..Default::default() - }, - // This one will fail to fetch - Register { - id: MSR_TSC + 1, - ..Default::default() - }, - ]; - vcpu.get_msrs(&mut msrs).unwrap(); - assert_eq!(msrs.len(), 1); + // This one should succeed + let _value = vcpu.get_msr(MSR_TSC).unwrap(); + + // This one will fail to fetch + vcpu.get_msr(MSR_TSC + 1) + .expect_err("invalid MSR index should fail"); } #[test] @@ -1598,22 +1516,18 @@ mod tests { assert_eq!(sregs.cr0 & X86_CR0_PG, X86_CR0_PG); assert_eq!(sregs.cr4 & X86_CR4_PAE, X86_CR4_PAE); - let mut efer_reg = vec![Register { - id: MSR_EFER, - value: 0, - }]; - vcpu.get_msrs(&mut efer_reg).expect("failed to get msrs"); - assert_eq!(efer_reg[0].value, EFER_LMA | EFER_LME); + let efer = vcpu.get_msr(MSR_EFER).expect("failed to get msr"); + assert_eq!(efer, EFER_LMA | EFER_LME); // Enable SCE via set_msrs - efer_reg[0].value |= EFER_SCE; - vcpu.set_msrs(&efer_reg).expect("failed to set msrs"); + vcpu.set_msr(MSR_EFER, efer | EFER_SCE) + .expect("failed to set msr"); // Verify that setting stuck let sregs = vcpu.get_sregs().expect("failed to get sregs"); assert_eq!(sregs.efer, EFER_SCE | EFER_LME | EFER_LMA); - vcpu.get_msrs(&mut efer_reg).expect("failed to get msrs"); - assert_eq!(efer_reg[0].value, EFER_SCE | EFER_LME | EFER_LMA); + let new_efer = vcpu.get_msr(MSR_EFER).expect("failed to get msr"); + assert_eq!(new_efer, EFER_SCE | EFER_LME | EFER_LMA); } #[test] @@ -1665,7 +1579,7 @@ mod tests { // Our MSR buffer is init'ed to zeros in the registers. The APIC base will be non-zero, so // by asserting that we know the MSR fetch actually did get us data. - let apic_base = all_msrs.iter().find(|reg| reg.id == MSR_APIC_BASE).unwrap(); - assert_ne!(apic_base.value, 0); + let apic_base = all_msrs.get(&MSR_APIC_BASE).unwrap(); + assert_ne!(*apic_base, 0); } } diff --git a/hypervisor/src/x86_64.rs b/hypervisor/src/x86_64.rs index 20a99c301..fb8a77489 100644 --- a/hypervisor/src/x86_64.rs +++ b/hypervisor/src/x86_64.rs @@ -5,9 +5,8 @@ use std::arch::x86_64::CpuidResult; #[cfg(any(unix, feature = "haxm", feature = "whpx"))] use std::arch::x86_64::__cpuid; -#[cfg(any(unix, feature = "haxm", feature = "whpx"))] use std::arch::x86_64::_rdtsc; -use std::collections::HashMap; +use std::collections::BTreeMap; use std::collections::HashSet; use anyhow::Context; @@ -111,10 +110,10 @@ pub trait VcpuX86_64: Vcpu { fn set_debugregs(&self, debugregs: &DebugRegs) -> Result<()>; /// Gets the VCPU extended control registers. - fn get_xcrs(&self) -> Result<Vec<Register>>; + fn get_xcrs(&self) -> Result<BTreeMap<u32, u64>>; - /// Sets the VCPU extended control registers. - fn set_xcrs(&self, xcrs: &[Register]) -> Result<()>; + /// Sets a VCPU extended control register. + fn set_xcr(&self, xcr: u32, value: u64) -> Result<()>; /// Gets the VCPU x87 FPU, MMX, XMM, YMM and MXCSR registers. fn get_xsave(&self) -> Result<Xsave>; @@ -130,15 +129,14 @@ pub trait VcpuX86_64: Vcpu { /// snapshotting. fn set_interrupt_state(&self, data: serde_json::Value) -> Result<()>; - /// Gets the model-specific registers. `msrs` specifies the MSR indexes to be queried, and - /// on success contains their indexes and values. - fn get_msrs(&self, msrs: &mut Vec<Register>) -> Result<()>; + /// Gets a single model-specific register's value. + fn get_msr(&self, msr_index: u32) -> Result<u64>; /// Gets the model-specific registers. Returns all the MSRs for the VCPU. - fn get_all_msrs(&self) -> Result<Vec<Register>>; + fn get_all_msrs(&self) -> Result<BTreeMap<u32, u64>>; - /// Sets the model-specific registers. - fn set_msrs(&self, msrs: &[Register]) -> Result<()>; + /// Sets a single model-specific register's value. + fn set_msr(&self, msr_index: u32, value: u64) -> Result<()>; /// Sets up the data returned by the CPUID instruction. fn set_cpuid(&self, cpuid: &CpuId) -> Result<()>; @@ -154,15 +152,62 @@ pub trait VcpuX86_64: Vcpu { /// will then set the appropriate registers on the vcpu. fn handle_cpuid(&mut self, entry: &CpuIdEntry) -> Result<()>; - /// Get the guest->host TSC offset - fn get_tsc_offset(&self) -> Result<u64>; + /// Gets the guest->host TSC offset. + /// + /// The default implementation uses [`VcpuX86_64::get_msr()`] to read the guest TSC. + fn get_tsc_offset(&self) -> Result<u64> { + // SAFETY: + // Safe because _rdtsc takes no arguments + let host_before_tsc = unsafe { _rdtsc() }; + + // get guest TSC value from our hypervisor + let guest_tsc = self.get_msr(crate::MSR_IA32_TSC)?; + + // SAFETY: + // Safe because _rdtsc takes no arguments + let host_after_tsc = unsafe { _rdtsc() }; - /// Set the guest->host TSC offset - fn set_tsc_offset(&self, offset: u64) -> Result<()>; + // Average the before and after host tsc to get the best value + let host_tsc = ((host_before_tsc as u128 + host_after_tsc as u128) / 2) as u64; + + Ok(guest_tsc.wrapping_sub(host_tsc)) + } + + /// Sets the guest->host TSC offset. + /// + /// The default implementation uses [`VcpuX86_64::set_tsc_value()`] to set the TSC value. + /// + /// It sets TSC_OFFSET (VMCS / CB field) by setting the TSC MSR to the current + /// host TSC value plus the desired offset. We rely on the fact that hypervisors + /// determine the value of TSC_OFFSET by computing TSC_OFFSET = new_tsc_value + /// - _rdtsc() = _rdtsc() + offset - _rdtsc() ~= offset. Note that the ~= is + /// important: this is an approximate operation, because the two _rdtsc() calls + /// are separated by at least a few ticks. + /// + /// Note: TSC_OFFSET, host TSC, guest TSC, and TSC MSR are all different + /// concepts. + /// * When a guest executes rdtsc, the value (guest TSC) returned is host_tsc * TSC_MULTIPLIER + + /// TSC_OFFSET + TSC_ADJUST. + /// * The TSC MSR is a special MSR that when written to by the host, will cause TSC_OFFSET to be + /// set accordingly by the hypervisor. + /// * When the guest *writes* to TSC MSR, it actually changes the TSC_ADJUST MSR *for the + /// guest*. Generally this is only happens if the guest is trying to re-zero or synchronize + /// TSCs. + fn set_tsc_offset(&self, offset: u64) -> Result<()> { + // SAFETY: _rdtsc takes no arguments. + let host_tsc = unsafe { _rdtsc() }; + self.set_tsc_value(host_tsc.wrapping_add(offset)) + } /// Sets the guest TSC exactly to the provided value. - /// Required for snapshotting. - fn set_tsc_value(&self, value: u64) -> Result<()>; + /// + /// The default implementation sets the guest's TSC by writing the value to the MSR directly. + /// + /// See [`VcpuX86_64::set_tsc_offset()`] for an explanation of how this value is actually read + /// by the guest after being set. + fn set_tsc_value(&self, value: u64) -> Result<()> { + self.set_msr(crate::MSR_IA32_TSC, value) + } /// Some hypervisors require special handling to restore timekeeping when /// a snapshot is restored. They are provided with a host TSC reference @@ -213,19 +258,16 @@ pub trait VcpuX86_64: Vcpu { self.set_regs(&snapshot.regs)?; self.set_sregs(&snapshot.sregs)?; self.set_debugregs(&snapshot.debug_regs)?; - self.set_xcrs(&snapshot.xcrs)?; - - let mut msrs = HashMap::new(); - for reg in self.get_all_msrs()? { - msrs.insert(reg.id, reg.value); + for (xcr_index, value) in &snapshot.xcrs { + self.set_xcr(*xcr_index, *value)?; } - for &msr in snapshot.msrs.iter() { - if Some(&msr.value) == msrs.get(&msr.id) { + for (msr_index, value) in snapshot.msrs.iter() { + if self.get_msr(*msr_index) == Ok(*value) { continue; // no need to set MSR since the values are the same. } - if let Err(e) = self.set_msrs(&[msr]) { - if msr_allowlist.contains(&msr.id) { + if let Err(e) = self.set_msr(*msr_index, *value) { + if msr_allowlist.contains(msr_index) { warn!( "Failed to set MSR. MSR might not be supported in this kernel. Err: {}", e @@ -252,8 +294,8 @@ pub struct VcpuSnapshot { regs: Regs, sregs: Sregs, debug_regs: DebugRegs, - xcrs: Vec<Register>, - msrs: Vec<Register>, + xcrs: BTreeMap<u32, u64>, + msrs: BTreeMap<u32, u64>, xsave: Xsave, hypervisor_data: serde_json::Value, tsc_offset: u64, @@ -264,69 +306,6 @@ impl_downcast!(VcpuX86_64); // TSC MSR pub const MSR_IA32_TSC: u32 = 0x00000010; -/// Implementation of get_tsc_offset that uses VcpuX86_64::get_msrs. -#[cfg(any(unix, feature = "haxm", feature = "whpx"))] -pub(crate) fn get_tsc_offset_from_msr(vcpu: &impl VcpuX86_64) -> Result<u64> { - let mut regs = vec![Register { - id: crate::MSR_IA32_TSC, - value: 0, - }]; - - // SAFETY: - // Safe because _rdtsc takes no arguments - let host_before_tsc = unsafe { _rdtsc() }; - - // get guest TSC value from our hypervisor - vcpu.get_msrs(&mut regs)?; - - // SAFETY: - // Safe because _rdtsc takes no arguments - let host_after_tsc = unsafe { _rdtsc() }; - - // Average the before and after host tsc to get the best value - let host_tsc = ((host_before_tsc as u128 + host_after_tsc as u128) / 2) as u64; - - Ok(regs[0].value.wrapping_sub(host_tsc)) -} - -/// Implementation of set_tsc_offset that uses VcpuX86_64::get_msrs. -/// -/// It sets TSC_OFFSET (VMCS / CB field) by setting the TSC MSR to the current -/// host TSC value plus the desired offset. We rely on the fact that hypervisors -/// determine the value of TSC_OFFSET by computing TSC_OFFSET = new_tsc_value -/// - _rdtsc() = _rdtsc() + offset - _rdtsc() ~= offset. Note that the ~= is -/// important: this is an approximate operation, because the two _rdtsc() calls -/// are separated by at least a few ticks. -/// -/// Note: TSC_OFFSET, host TSC, guest TSC, and TSC MSR are all different -/// concepts. -/// * When a guest executes rdtsc, the value (guest TSC) returned is host_tsc * TSC_MULTIPLIER + -/// TSC_OFFSET + TSC_ADJUST. -/// * The TSC MSR is a special MSR that when written to by the host, will cause TSC_OFFSET to be set -/// accordingly by the hypervisor. -/// * When the guest *writes* to TSC MSR, it actually changes the TSC_ADJUST MSR *for the guest*. -/// Generally this is only happens if the guest is trying to re-zero or synchronize TSCs. -#[cfg(any(unix, feature = "haxm", feature = "whpx"))] -pub(crate) fn set_tsc_offset_via_msr(vcpu: &impl VcpuX86_64, offset: u64) -> Result<()> { - // SAFETY: _rdtsc takes no arguments. - let host_tsc = unsafe { _rdtsc() }; - set_tsc_value_via_msr(vcpu, host_tsc.wrapping_add(offset)) -} - -/// Sets the guest's TSC by writing the value to the MSR directly. See -/// [`set_tsc_offset_via_msr`] for an explanation of how this value is actually -/// read by the guest after being set. -#[cfg(any(unix, feature = "haxm", feature = "whpx"))] -pub(crate) fn set_tsc_value_via_msr(vcpu: &impl VcpuX86_64, value: u64) -> Result<()> { - let regs = vec![Register { - id: crate::MSR_IA32_TSC, - value, - }]; - - // set guest TSC value from our hypervisor - vcpu.set_msrs(®s) -} - /// Gets host cpu max physical address bits. #[cfg(any(unix, feature = "haxm", feature = "whpx"))] pub(crate) fn host_phys_addr_bits() -> u8 { @@ -355,7 +334,7 @@ pub struct VcpuInitX86_64 { pub fpu: Fpu, /// Machine-specific registers. - pub msrs: Vec<Register>, + pub msrs: BTreeMap<u32, u64>, } /// Hold the CPU feature configurations that are needed to setup a vCPU. @@ -1002,13 +981,6 @@ pub struct DebugRegs { pub dr7: u64, } -/// State of one VCPU register. Currently used for MSRs and XCRs. -#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)] -pub struct Register { - pub id: u32, - pub value: u64, -} - /// The hybrid type for intel hybrid CPU. #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum CpuHybridType { diff --git a/hypervisor/tests/hypervisor_virtualization.rs b/hypervisor/tests/hypervisor_virtualization.rs new file mode 100644 index 000000000..3a1440aaf --- /dev/null +++ b/hypervisor/tests/hypervisor_virtualization.rs @@ -0,0 +1,111 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#![cfg(target_arch = "x86_64")] +#![cfg(any(feature = "whpx", feature = "gvm", feature = "haxm", unix))] + +use hypervisor::*; +use vm_memory::GuestAddress; +use vm_memory::GuestMemory; + +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn test_kvm_minimal_virtualization() { + use hypervisor::kvm::*; + test_minimal_virtualization(|guest_mem| { + let kvm = Kvm::new().expect("failed to create kvm"); + let vm = KvmVm::new(&kvm, guest_mem, Default::default()).expect("failed to create vm"); + (kvm, vm) + }); +} + +#[test] +#[cfg(all(windows, feature = "haxm"))] +fn test_haxm_minimal_virtualization() { + use hypervisor::haxm::*; + test_minimal_virtualization(|guest_mem| { + let haxm = Haxm::new().expect("failed to create haxm"); + let vm = HaxmVm::new(&haxm, guest_mem).expect("failed to create vm"); + (haxm, vm) + }); +} + +#[test] +#[cfg(feature = "gvm")] +fn test_gvm_minimal_virtualization() { + use hypervisor::gvm::*; + test_minimal_virtualization(|guest_mem| { + let gvm = Gvm::new().expect("failed to create gvm"); + let vm = GvmVm::new(&gvm, guest_mem).expect("failed to create vm"); + (gvm, vm) + }); +} + +#[test] +#[cfg(all(windows, feature = "whpx"))] +fn test_whpx_minimal_virtualization() { + use hypervisor::whpx::*; + test_minimal_virtualization(|guest_mem| { + let whpx = Whpx::new().expect("failed to create whpx"); + let vm = WhpxVm::new(&whpx, 1, guest_mem, CpuId::new(0), false, None) + .expect("failed to create vm"); + (whpx, vm) + }); +} + +// This runs a minimal program under virtualization. +// It should require only the ability to execute instructions under virtualization, physical +// memory, the ability to get and set some guest VM registers, and intercepting HLT. +fn test_minimal_virtualization<CreateVm, HypervisorT, VmT>(create_vm: CreateVm) +where + CreateVm: FnOnce(GuestMemory) -> (HypervisorT, VmT), + HypervisorT: Hypervisor, + VmT: VmX86_64, +{ + /* + 0x0000000000000002: 01 D8 add ax, bx + 0x0000000000000002: F4 hlt + */ + + let code: [u8; 3] = [0x01, 0xd8, 0xf4]; + let mem_size = 0x2000; + let load_addr = GuestAddress(0x1000); + + let guest_mem = + GuestMemory::new(&[(GuestAddress(0), mem_size)]).expect("failed to create guest mem"); + guest_mem + .write_at_addr(&code[..], load_addr) + .expect("failed to write to guest memory"); + + let (_, vm) = create_vm(guest_mem); + let mut vcpu = vm.create_vcpu(0).expect("new vcpu failed"); + let mut vcpu_sregs = vcpu.get_sregs().expect("get sregs failed"); + vcpu_sregs.cs.base = 0; + vcpu_sregs.cs.selector = 0; + + vcpu.set_sregs(&vcpu_sregs).expect("set sregs failed"); + + let vcpu_regs = Regs { + rip: load_addr.offset(), + rflags: 2, + rax: 1, + rbx: 2, + ..Default::default() + }; + vcpu.set_regs(&vcpu_regs).expect("set regs failed"); + + loop { + match vcpu.run().expect("run failed") { + VcpuExit::Hlt => { + break; + } + // Continue on external interrupt or signal + VcpuExit::Intr => continue, + r => panic!("unexpected exit reason: {:?}", r), + } + } + + let regs: Regs = vcpu.get_regs().expect("failed to get regs"); + assert_eq!(regs.rax, 3); +} diff --git a/hypervisor/tests/kvm/x86_64.rs b/hypervisor/tests/kvm/x86_64.rs index c83d5de17..b8b45154a 100644 --- a/hypervisor/tests/kvm/x86_64.rs +++ b/hypervisor/tests/kvm/x86_64.rs @@ -25,7 +25,6 @@ use hypervisor::PitChannelState; use hypervisor::PitRWMode; use hypervisor::PitRWState; use hypervisor::PitState; -use hypervisor::Register; use hypervisor::TriggerMode; use hypervisor::Vm; use hypervisor::VmCap; @@ -365,68 +364,51 @@ fn xcrs() { let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap(); let vm = KvmVm::new(&kvm, gm, Default::default()).unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - let mut xcrs = vcpu.get_xcrs().unwrap(); - xcrs[0].value = 1; - vcpu.set_xcrs(&xcrs).unwrap(); - let xcrs2 = vcpu.get_xcrs().unwrap(); - assert_eq!(xcrs[0].value, xcrs2[0].value); + vcpu.set_xcr(0, 1).unwrap(); + let xcrs = vcpu.get_xcrs().unwrap(); + let xcr0 = xcrs.get(&0).unwrap(); + assert_eq!(*xcr0, 1); } #[test] -fn get_msrs() { +fn get_msr() { let kvm = Kvm::new().unwrap(); let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap(); let vm = KvmVm::new(&kvm, gm, Default::default()).unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - let mut msrs = vec![ - // This one should succeed - Register { - id: 0x0000011e, - ..Default::default() - }, - // This one will fail to fetch - Register { - id: 0xffffffff, - ..Default::default() - }, - ]; - vcpu.get_msrs(&mut msrs).unwrap(); - assert_eq!(msrs.len(), 1); + + // This one should succeed + let _value = vcpu.get_msr(0x0000011e).unwrap(); + + // This one will fail to fetch + vcpu.get_msr(0xffffffff) + .expect_err("invalid MSR index should fail"); } #[test] -fn set_msrs() { +fn set_msr() { let kvm = Kvm::new().unwrap(); let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap(); let vm = KvmVm::new(&kvm, gm, Default::default()).unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); const MSR_TSC_AUX: u32 = 0xc0000103; - let mut msrs = vec![Register { - id: MSR_TSC_AUX, - value: 42, - }]; - vcpu.set_msrs(&msrs).unwrap(); - - msrs[0].value = 0; - vcpu.get_msrs(&mut msrs).unwrap(); - assert_eq!(msrs.len(), 1); - assert_eq!(msrs[0].id, MSR_TSC_AUX); - assert_eq!(msrs[0].value, 42); + vcpu.set_msr(MSR_TSC_AUX, 42).unwrap(); + let msr_tsc_aux = vcpu.get_msr(MSR_TSC_AUX).unwrap(); + assert_eq!(msr_tsc_aux, 42); } #[test] -fn set_msrs_unsupported() { +fn set_msr_unsupported() { let kvm = Kvm::new().unwrap(); let gm = GuestMemory::new(&[(GuestAddress(0), 0x10000)]).unwrap(); let vm = KvmVm::new(&kvm, gm, Default::default()).unwrap(); let vcpu = vm.create_vcpu(0).unwrap(); - let msrs = vec![Register { - id: u32::MAX, - value: u64::MAX, - }]; - assert_eq!(vcpu.set_msrs(&msrs), Err(base::Error::new(libc::EPERM))); + assert_eq!( + vcpu.set_msr(u32::MAX, u64::MAX), + Err(base::Error::new(libc::EPERM)) + ); } #[test] diff --git a/hypervisor/tests/tsc_offsets.rs b/hypervisor/tests/tsc_offsets.rs index f27048617..d35e3272a 100644 --- a/hypervisor/tests/tsc_offsets.rs +++ b/hypervisor/tests/tsc_offsets.rs @@ -177,11 +177,8 @@ fn test_tsc_offset_run( vcpu.set_regs(&vcpu_regs).expect("set regs failed"); if let Some(value) = set_msr { - vcpu.set_msrs(&[Register { - id: 0x00000010, - value, - }]) - .expect("set_msrs should not fail"); + vcpu.set_msr(0x00000010, value) + .expect("set_msr should not fail"); } if let Some(offset) = set_offset { diff --git a/infra/README.recipes.md b/infra/README.recipes.md index fd96b5720..d85ef1593 100644 --- a/infra/README.recipes.md +++ b/infra/README.recipes.md @@ -174,19 +174,19 @@ This recipe requires ambient luci authentication. To test locally run: — **def [RunSteps](/infra/recipes/update_chromeos_merges.py#14)(api):** -[depot_tools/recipe_modules/bot_update]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/e545830db2fb823da1217b142cb5be10bf45d575/recipes/README.recipes.md#recipe_modules-bot_update -[depot_tools/recipe_modules/depot_tools]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/e545830db2fb823da1217b142cb5be10bf45d575/recipes/README.recipes.md#recipe_modules-depot_tools -[depot_tools/recipe_modules/gclient]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/e545830db2fb823da1217b142cb5be10bf45d575/recipes/README.recipes.md#recipe_modules-gclient -[depot_tools/recipe_modules/git]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/e545830db2fb823da1217b142cb5be10bf45d575/recipes/README.recipes.md#recipe_modules-git -[depot_tools/recipe_modules/gsutil]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/e545830db2fb823da1217b142cb5be10bf45d575/recipes/README.recipes.md#recipe_modules-gsutil -[recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-buildbucket -[recipe_engine/recipe_modules/cipd]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-cipd -[recipe_engine/recipe_modules/context]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-context -[recipe_engine/recipe_modules/file]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-file -[recipe_engine/recipe_modules/json]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-json -[recipe_engine/recipe_modules/path]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-path -[recipe_engine/recipe_modules/platform]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-platform -[recipe_engine/recipe_modules/properties]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-properties -[recipe_engine/recipe_modules/raw_io]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-raw_io -[recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/README.recipes.md#recipe_modules-step -[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/3868908180614e35f4b57e321e5e6f1de6adf7dc/recipe_engine/recipe_api.py#471 +[depot_tools/recipe_modules/bot_update]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/5a584405cae6e1fd08a6054ce9ba61b645b4d591/recipes/README.recipes.md#recipe_modules-bot_update +[depot_tools/recipe_modules/depot_tools]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/5a584405cae6e1fd08a6054ce9ba61b645b4d591/recipes/README.recipes.md#recipe_modules-depot_tools +[depot_tools/recipe_modules/gclient]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/5a584405cae6e1fd08a6054ce9ba61b645b4d591/recipes/README.recipes.md#recipe_modules-gclient +[depot_tools/recipe_modules/git]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/5a584405cae6e1fd08a6054ce9ba61b645b4d591/recipes/README.recipes.md#recipe_modules-git +[depot_tools/recipe_modules/gsutil]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/5a584405cae6e1fd08a6054ce9ba61b645b4d591/recipes/README.recipes.md#recipe_modules-gsutil +[recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-buildbucket +[recipe_engine/recipe_modules/cipd]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-cipd +[recipe_engine/recipe_modules/context]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-context +[recipe_engine/recipe_modules/file]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-file +[recipe_engine/recipe_modules/json]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-json +[recipe_engine/recipe_modules/path]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-path +[recipe_engine/recipe_modules/platform]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-platform +[recipe_engine/recipe_modules/properties]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-properties +[recipe_engine/recipe_modules/raw_io]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-raw_io +[recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/README.recipes.md#recipe_modules-step +[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/f967fa68fd4bd3e76140de7ffa449fa3a45a72f4/recipe_engine/recipe_api.py#471 diff --git a/infra/config/generated/realms.cfg b/infra/config/generated/realms.cfg index 029481579..ad6a1a903 100644 --- a/infra/config/generated/realms.cfg +++ b/infra/config/generated/realms.cfg @@ -7,6 +7,10 @@ realms { name: "@root" bindings { + role: "role/buildbucket.owner" + principals: "group:mdb/crosvm-acl-luci-admin" + } + bindings { role: "role/buildbucket.reader" principals: "group:all" } diff --git a/infra/config/main.star b/infra/config/main.star index 9885642a4..fa6f316f2 100755 --- a/infra/config/main.star +++ b/infra/config/main.star @@ -28,6 +28,7 @@ luci.project( "role/swarming.poolOwner", "role/swarming.poolUser", "role/swarming.taskTriggerer", + "role/buildbucket.owner", ], groups = "mdb/crosvm-acl-luci-admin", ), diff --git a/infra/config/recipes.cfg b/infra/config/recipes.cfg index fd728dca2..ec7d3746e 100644 --- a/infra/config/recipes.cfg +++ b/infra/config/recipes.cfg @@ -20,12 +20,12 @@ "deps": { "depot_tools": { "branch": "refs/heads/main", - "revision": "e545830db2fb823da1217b142cb5be10bf45d575", + "revision": "5a584405cae6e1fd08a6054ce9ba61b645b4d591", "url": "https://chromium.googlesource.com/chromium/tools/depot_tools.git" }, "recipe_engine": { "branch": "refs/heads/main", - "revision": "3868908180614e35f4b57e321e5e6f1de6adf7dc", + "revision": "f967fa68fd4bd3e76140de7ffa449fa3a45a72f4", "url": "https://chromium.googlesource.com/infra/luci/recipes-py.git" } }, diff --git a/io_uring/Android.bp b/io_uring/Android.bp index 9786b3485..dc4efe19d 100644 --- a/io_uring/Android.bp +++ b/io_uring/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/jail/Android.bp b/jail/Android.bp index 765f02703..dd72dcb45 100644 --- a/jail/Android.bp +++ b/jail/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/kernel_cmdline/Android.bp b/kernel_cmdline/Android.bp index eb80e5895..7fcb23798 100644 --- a/kernel_cmdline/Android.bp +++ b/kernel_cmdline/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/kernel_loader/Android.bp b/kernel_loader/Android.bp index 0499835e6..d3fc0df56 100644 --- a/kernel_loader/Android.bp +++ b/kernel_loader/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/kvm/Android.bp b/kvm/Android.bp index 4d2ad431c..d87e60fcc 100644 --- a/kvm/Android.bp +++ b/kvm/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/kvm_sys/Android.bp b/kvm_sys/Android.bp index a0d53573d..e693e6b33 100644 --- a/kvm_sys/Android.bp +++ b/kvm_sys/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/libcras_stub/Android.bp b/libcras_stub/Android.bp index 3f8f7d43b..dd00bdabb 100644 --- a/libcras_stub/Android.bp +++ b/libcras_stub/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/linux_input_sys/Android.bp b/linux_input_sys/Android.bp index 3e8284e15..44ee82c5a 100644 --- a/linux_input_sys/Android.bp +++ b/linux_input_sys/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/metrics/Android.bp b/metrics/Android.bp index 87c2d10ff..275ec22f8 100644 --- a/metrics/Android.bp +++ b/metrics/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { @@ -24,6 +25,7 @@ rust_library { "libanyhow", "libbase_rust", "libcfg_if", + "libmetrics_events", "libmetrics_generic", "libserde", "libsync_rust", @@ -49,6 +51,7 @@ rust_test { "libanyhow", "libbase_rust", "libcfg_if", + "libmetrics_events", "libmetrics_generic", "libserde", "libsync_rust", diff --git a/metrics/Cargo.toml b/metrics/Cargo.toml index dfb9b5e39..078509cdd 100644 --- a/metrics/Cargo.toml +++ b/metrics/Cargo.toml @@ -10,6 +10,7 @@ base = { path = "../base" } cfg-if = "*" serde = { version = "1", features = ["derive"] } sync = { path = "../common/sync" } +metrics_events = { path = "../metrics_events" } metrics_product = { path = "../vendor/generic/metrics", package = "metrics_generic" } [target.'cfg(windows)'.dependencies] diff --git a/metrics/src/controller.rs b/metrics/src/controller.rs index c3eb6fd24..ac64e9269 100644 --- a/metrics/src/controller.rs +++ b/metrics/src/controller.rs @@ -6,16 +6,14 @@ use anyhow::Result; use base::info; -use base::warn; use base::EventToken; -use base::Tube; +use base::RecvTube; -use crate::MetricsRequest; use crate::RequestHandler; /// Runs the metrics controller. pub struct MetricsController { - pub(crate) agents: Vec<Tube>, + pub(crate) agents: Vec<RecvTube>, handler: RequestHandler, pub(crate) closed_tubes: usize, } @@ -30,7 +28,7 @@ pub(crate) enum MetricsControllerToken { } impl MetricsController { - pub fn new(agents: Vec<Tube>) -> Self { + pub fn new(agents: Vec<RecvTube>) -> Self { Self { agents, handler: RequestHandler::new(), @@ -46,13 +44,8 @@ impl MetricsController { } /// Handles a tube that has indicated it has data ready to read. - pub(crate) fn on_tube_readable(&self, client: &Tube) { - match client.recv::<MetricsRequest>() { - Ok(req) => self.handler.handle_request(req), - Err(e) => { - warn!("unexpected error receiving agent metrics request: {}", e) - } - } + pub(crate) fn on_tube_readable(&self, client: &RecvTube) { + self.handler.handle_tube_readable(client) } /// Handles a closed connection, and returns a bool indicating diff --git a/metrics/src/lib.rs b/metrics/src/lib.rs index dd561ca11..a996d82f3 100644 --- a/metrics/src/lib.rs +++ b/metrics/src/lib.rs @@ -16,7 +16,7 @@ mod local_stats; pub mod sys; pub use controller::MetricsController; -pub use metrics_product::MetricEventType; +pub use metrics_events::MetricEventType; pub use metrics_product::*; pub type RequestHandler = MetricsRequestHandler; diff --git a/metrics/src/sys.rs b/metrics/src/sys.rs index e0696cf17..64f09e4e2 100644 --- a/metrics/src/sys.rs +++ b/metrics/src/sys.rs @@ -6,6 +6,7 @@ cfg_if::cfg_if! { if #[cfg(windows)] { pub mod windows; pub use windows::*; + pub use metrics_events::sys::windows::*; } else if #[cfg(any(target_os = "android", target_os = "linux"))] { pub(crate) mod linux; } diff --git a/metrics_events/Android.bp b/metrics_events/Android.bp new file mode 100644 index 000000000..87a087eb8 --- /dev/null +++ b/metrics_events/Android.bp @@ -0,0 +1,26 @@ +// This file is generated by cargo_embargo. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. +// Content before the first "rust_*" or "genrule" module is preserved. + +package { + default_applicable_licenses: ["external_crosvm_license"], +} + +rust_library { + name: "libmetrics_events", + defaults: ["crosvm_inner_defaults"], + host_supported: true, + crate_name: "metrics_events", + cargo_env_compat: true, + cargo_pkg_version: "0.1.0", + srcs: ["src/lib.rs"], + edition: "2021", + rustlibs: [ + "libanyhow", + "libcfg_if", + "libmetrics_events_generic", + "libserde", + ], + aliases: ["metrics_events_generic:metrics_events_product"], +} diff --git a/metrics_events/Cargo.toml b/metrics_events/Cargo.toml new file mode 100644 index 000000000..498b09a71 --- /dev/null +++ b/metrics_events/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "metrics_events" +version = "0.1.0" +authors = ["The ChromiumOS Authors"] +edition = "2021" + +[dependencies] +anyhow = "*" +cfg-if = "*" +serde = { version = "1", features = ["derive"] } +metrics_events_product = { path = "../vendor/generic/metrics_events", package = "metrics_events_generic" } + +[target.'cfg(windows)'.dependencies] +win_util = { path = "../win_util" } diff --git a/metrics_events/src/event_types.rs b/metrics_events/src/event_types.rs new file mode 100644 index 000000000..f1fde88c4 --- /dev/null +++ b/metrics_events/src/event_types.rs @@ -0,0 +1,54 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use metrics_events_product::MetricEventType as VendorMetricEventType; +use serde::Deserialize; +use serde::Serialize; + +#[cfg(windows)] +use crate::sys::windows::WaveFormatDetails; + +// TODO(mikehoyle): Create a way to generate these directly from the +// proto for a single source-of-truth. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum MetricEventType { + CpuUsage, + MemoryUsage, + Fps, + JankyFps, + NetworkTxRate, + NetworkRxRate, + Interrupts, + FrameTime, + EmulatorGraphicsFreeze, + EmulatorGraphicsUnfreeze, + EmulatorGfxstreamVkAbortReason, + ChildProcessExit { + exit_code: u32, + #[cfg(windows)] + process_type: win_util::ProcessType, + }, + ReadIo, + WriteIo, + #[cfg(windows)] + AudioFormatRequestOk(WaveFormatDetails), + #[cfg(windows)] + AudioFormatModifiedOk(WaveFormatDetails), + #[cfg(windows)] + AudioFormatFailed(WaveFormatDetails), + TscCoresOutOfSync, + NetworkTxRateSummarized, + NetworkRxRateSummarized, + DllLoaded(String), + GraphicsHangRenderThread, + GraphicsHangSyncThread, + AudioNoopStreamForced, + AudioPlaybackError, + RtcWakeup, + VirtioWakeup { + virtio_id: u32, + }, + Other(i64), + Vendor(VendorMetricEventType), +} diff --git a/metrics_events/src/lib.rs b/metrics_events/src/lib.rs new file mode 100644 index 000000000..3411cfb7d --- /dev/null +++ b/metrics_events/src/lib.rs @@ -0,0 +1,10 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +mod event_types; +pub mod sys; + +pub use event_types::MetricEventType; +pub use metrics_events_product::MetricEventType as VendorMetricEventType; +pub use metrics_events_product::RecordDetails; diff --git a/vendor/generic/metrics/src/sys.rs b/metrics_events/src/sys.rs index 10b70883f..10b70883f 100644 --- a/vendor/generic/metrics/src/sys.rs +++ b/metrics_events/src/sys.rs diff --git a/metrics_events/src/sys/windows.rs b/metrics_events/src/sys/windows.rs new file mode 100644 index 000000000..0b972f988 --- /dev/null +++ b/metrics_events/src/sys/windows.rs @@ -0,0 +1,76 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use serde::Deserialize; +use serde::Serialize; + +#[derive(Clone, Debug, Serialize, Deserialize, Default)] +pub struct WaveFormatDetails { + // Format requested by WASAPI `GetMixFormat` system call. + pub requested: Option<WaveFormat>, + // Originally the requested wave format that's modified by the emulator. Only + // populated if the emulator decides the requested wave format should not be + // used. + pub modified: Option<WaveFormat>, + // Format that is valid and closest matching to the modified format, if the + // modified was rejected. Should only be populated if modified is also + // non-null and was rejected by WASAPI `IsFormatSupported` system call. + pub closest_matched: Option<WaveFormat>, +} + +// Defines the format of waveformat audio data. This information is used by +// WASAPI to determine how to process the audio playback data coming from the +// emulator. +// +// The fields in the structure come from WAVEFORMATEXTENSIBLE of win32 api. +// https://docs.microsoft.com/en-us/windows/win32/api/mmreg/ns-mmreg-waveformatextensible +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub struct WaveFormat { + // Ex. 65534 (Maps to WAVE_FORMAT_EXTENSIBLE) + pub format_tag: i32, + // Number of channels. + pub channels: i32, + // Sample rate in Hz. Ex: 48000 + pub samples_per_sec: i32, + // Required average data-transfer rate for the format tag. Usually this will + // be samples_per_sec * block_align, since the format tag is usually + // WAVE_FORMAT_IEEE_FLOAT or it's extensible and SubFormat is + // KSDATAFORMAT_SUBTYPE_IEEE_FLOAT. + pub avg_bytes_per_sec: i32, + // Minimum atomic unit of data based on the format_tag. Usually this will + // just be bits_per_samples * channels. + pub block_align: i32, + // Bits used per sample. Must be a multiple of 8. + pub bits_per_sample: i32, + // Size in bytes of extra information appended to WAVEFORMATEX struct. + pub size_bytes: i32, + + // The next fields are part of the WAVEFORMATEXTENSIBLE struct. They will only + // be non-null if format_tag is WAVE_FORMAT_EXTENSIBLE. + + // Bit depth. Can be any value. Ex. bits_per_sample is 24, + // but samples is 20. Note: This value is a union, so it could mean something + // slightly different, but most likely won't. Refer to doc for more info. + pub samples: Option<i32>, + // Bitmask mapping channels in stream to speaker positions. + // Ex. 3 ( SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT ) + pub channel_mask: Option<i64>, + // Similar to format_tag, but for WAVEFORMATEXTENSIBLE structs. + pub sub_format: Option<WaveFormatSubFormat>, +} + +// Subformat GUID mapping: +// https://github.com/retep998/winapi-rs/blob/2f76bdea3a79817ccfab496fbd1786d5a697387b/src/shared/ksmedia.rs +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +pub enum WaveFormatSubFormat { + Invalid, + Analog, + Pcm, + IeeeFloat, + Drm, + ALaw, + MuLaw, + Adpcm, + Mpeg, +} diff --git a/net_sys/Android.bp b/net_sys/Android.bp index c874f12dc..be808c61f 100644 --- a/net_sys/Android.bp +++ b/net_sys/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/net_util/Android.bp b/net_util/Android.bp index ded81b091..af80e9383 100644 --- a/net_util/Android.bp +++ b/net_util/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/power_monitor/Android.bp b/power_monitor/Android.bp index 84d59fd61..6dad7a4c2 100644 --- a/power_monitor/Android.bp +++ b/power_monitor/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/protos/Android.bp b/protos/Android.bp index 3014b47c8..3c72b0e76 100644 --- a/protos/Android.bp +++ b/protos/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/resources/Android.bp b/resources/Android.bp index 5ebf2cab7..a7c7354d6 100644 --- a/resources/Android.bp +++ b/resources/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/riscv64/Android.bp b/riscv64/Android.bp index bcf321061..d964f7925 100644 --- a/riscv64/Android.bp +++ b/riscv64/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/rutabaga_gfx/Android.bp b/rutabaga_gfx/Android.bp index 0c64f2ef2..b355aa2ae 100644 --- a/rutabaga_gfx/Android.bp +++ b/rutabaga_gfx/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/serde_keyvalue/Android.bp b/serde_keyvalue/Android.bp index c31364171..92080a453 100644 --- a/serde_keyvalue/Android.bp +++ b/serde_keyvalue/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/serde_keyvalue/serde_keyvalue_derive/Android.bp b/serde_keyvalue/serde_keyvalue_derive/Android.bp index 7d4376402..15d9f0f6e 100644 --- a/serde_keyvalue/serde_keyvalue_derive/Android.bp +++ b/serde_keyvalue/serde_keyvalue_derive/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/src/crosvm/cmdline.rs b/src/crosvm/cmdline.rs index 04f343dcd..c6cf27e38 100644 --- a/src/crosvm/cmdline.rs +++ b/src/crosvm/cmdline.rs @@ -289,7 +289,11 @@ pub struct MakeRTCommand { #[derive(FromArgs)] #[argh(subcommand, name = "resume")] -/// Resumes the crosvm instance +/// Resumes the crosvm instance. No-op if already running. When starting crosvm with `--restore`, +/// this command can be used to wait until the restore is complete +// Implementation note: All the restore work happens before crosvm becomes able to process incoming +// commands, so really all commands can be used to wait for restore to complete, but few are side +// effect free. pub struct ResumeCommand { #[argh(positional, arg_name = "VM_SOCKET")] /// VM Socket path @@ -1334,9 +1338,13 @@ pub struct RunCommand { /// Possible key values: /// backend=(2d|virglrenderer|gfxstream) - Which backend to /// use for virtio-gpu (determining rendering protocol) + /// max_num_displays=INT - The maximum number of concurrent + /// virtual displays in this VM. This must not exceed + /// VIRTIO_GPU_MAX_SCANOUTS (i.e. 16). /// displays=[[GpuDisplayParameters]] - The list of virtual - /// displays to create. See the possible key values for - /// GpuDisplayParameters in the section below. + /// displays to create when booting this VM. Displays may + /// be hotplugged after booting. See the possible key + /// values for GpuDisplayParameters in the section below. /// context-types=LIST - The list of supported context /// types, separated by ':' (default: no contexts enabled) /// width=INT - The width of the virtual display connected diff --git a/src/crosvm/gpu_config.rs b/src/crosvm/gpu_config.rs index 650f4ae47..c8ef82184 100644 --- a/src/crosvm/gpu_config.rs +++ b/src/crosvm/gpu_config.rs @@ -2,10 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#[cfg(feature = "kiwi")] -use base::warn; -#[cfg(feature = "kiwi")] -use battlestar::process_invariants; +use devices::virtio::gpu::VIRTIO_GPU_MAX_SCANOUTS; use devices::virtio::GpuDisplayMode; use devices::virtio::GpuDisplayParameters; #[cfg(feature = "gfxstream")] @@ -103,37 +100,27 @@ pub(crate) fn validate_gpu_config(cfg: &mut Config) -> Result<(), String> { )); } + if gpu_parameters.max_num_displays < 1 + || gpu_parameters.max_num_displays > VIRTIO_GPU_MAX_SCANOUTS as u32 + { + return Err(format!( + "`max_num_displays` must be in range [1, {}]", + VIRTIO_GPU_MAX_SCANOUTS + )); + } + if gpu_parameters.display_params.len() as u32 > gpu_parameters.max_num_displays { + return Err(format!( + "Provided more `display_params` ({}) than `max_num_displays` ({})", + gpu_parameters.display_params.len(), + gpu_parameters.max_num_displays + )); + } + // Add a default display if no display is specified. if gpu_parameters.display_params.is_empty() { gpu_parameters.display_params.push(Default::default()); } - // Process invariants are not written to the static `PROCESS_INVARIANTS` yet, so instead of - // calling `phenotype!(kiwi_emulator_feature, get_enable_4k_uhd_resolution)`, we have to - // load it by ourselves. - // TODO(b/276909432): The BSS should read the experiment flags and specify the virtual - // display size, and then we can remove this workaround. - #[cfg(feature = "kiwi")] - let is_4k_uhd_enabled = match process_invariants::load_invariants( - &cfg.process_invariants_data_handle, - &cfg.process_invariants_data_size, - ) { - Ok(invariants) => invariants - .get_flag_snapshot() - .get_features() - .kiwi_emulator_feature - .clone() - .unwrap_or_default() - .get_enable_4k_uhd_resolution(), - Err(e) => { - warn!( - "Failed to load process invariants, will not enable 4k UHD: {}", - e - ); - false - } - }; - #[cfg(not(feature = "kiwi"))] let is_4k_uhd_enabled = false; let (width, height) = gpu_parameters.display_params[0].get_virtual_display_size_4k_uhd(is_4k_uhd_enabled); @@ -172,6 +159,81 @@ mod tests { } #[test] + fn parse_gpu_options_max_num_displays() { + { + let gpu_params = parse_gpu_options("").unwrap(); + assert_eq!(gpu_params.max_num_displays, VIRTIO_GPU_MAX_SCANOUTS as u32); + } + { + let gpu_params = parse_gpu_options("max-num-displays=5").unwrap(); + assert_eq!(gpu_params.max_num_displays, 5); + } + { + let command = crate::crosvm::cmdline::RunCommand::from_args( + &[], + &["--gpu", "max-num-displays=0", "/dev/null"], + ) + .unwrap(); + assert!(Config::try_from(command).is_err()); + } + { + let command = crate::crosvm::cmdline::RunCommand::from_args( + &[], + &[ + "--gpu", + format!("max-num-displays={}", VIRTIO_GPU_MAX_SCANOUTS + 1).as_str(), + "/dev/null", + ], + ) + .unwrap(); + assert!(Config::try_from(command).is_err()); + } + // TODO(b/332910955): Remove the single display restriction on Windows and enable this test. + #[cfg(any(target_os = "android", target_os = "linux"))] + { + let command = crate::crosvm::cmdline::RunCommand::from_args( + &[], + &[ + "--gpu", + "max-num-displays=1,displays=[[mode=windowed[1920,1080]],\ + [mode=windowed[1280,720]]]", + "/dev/null", + ], + ) + .unwrap(); + assert!(Config::try_from(command).is_err()); + } + #[cfg(any(target_os = "android", target_os = "linux"))] + { + let config: Config = crate::crosvm::cmdline::RunCommand::from_args( + &[], + &[ + "--gpu", + "max-num-displays=3,displays=[[mode=windowed[1920,1080]],\ + [mode=windowed[1280,720]]]", + "/dev/null", + ], + ) + .unwrap() + .try_into() + .unwrap(); + + let gpu_params = config.gpu_parameters.unwrap(); + + assert_eq!(gpu_params.max_num_displays, 3); + assert_eq!(gpu_params.display_params.len(), 2); + assert_eq!( + gpu_params.display_params[0].mode, + GpuDisplayMode::Windowed(1920, 1080) + ); + assert_eq!( + gpu_params.display_params[1].mode, + GpuDisplayMode::Windowed(1280, 720) + ); + } + } + + #[test] fn parse_gpu_options_mode() { use devices::virtio::gpu::GpuMode; diff --git a/src/crosvm/sys/linux.rs b/src/crosvm/sys/linux.rs index 2bfdb442b..f94096cde 100644 --- a/src/crosvm/sys/linux.rs +++ b/src/crosvm/sys/linux.rs @@ -164,6 +164,7 @@ use jail_warden::JailWardenImpl; #[cfg(feature = "pci-hotplug")] use jail_warden::PermissiveJailWarden; use libc; +use metrics::MetricsController; use minijail::Minijail; #[cfg(feature = "pci-hotplug")] use pci_hotplug_manager::PciHotPlugManager; @@ -1681,6 +1682,10 @@ where // access to those files will not be possible. info!("crosvm entering multiprocess mode"); } + + let (metrics_send, metrics_recv) = Tube::directional_pair().context("metrics tube")?; + metrics::initialize(metrics_send); + #[cfg(all(feature = "pci-hotplug", feature = "swap"))] let swap_device_helper = match &swap_controller { Some(swap_controller) => Some(swap_controller.create_device_helper()?), @@ -2211,6 +2216,7 @@ where guest_suspended_cvar, #[cfg(feature = "pvclock")] pvclock_host_tube, + metrics_recv, ) } @@ -3277,6 +3283,7 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>( #[cfg(feature = "registered_events")] reg_evt_rdtube: RecvTube, guest_suspended_cvar: Option<Arc<(Mutex<bool>, Condvar)>>, #[cfg(feature = "pvclock")] pvclock_host_tube: Option<Tube>, + metrics_tube: RecvTube, ) -> Result<ExitState> { #[derive(EventToken)] enum Token { @@ -3626,6 +3633,21 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>( .context("static device setup complete")?; } + let metrics_thread = if metrics::is_initialized() { + Some( + std::thread::Builder::new() + .name("metrics_thread".into()) + .spawn(move || { + if let Err(e) = MetricsController::new(vec![metrics_tube]).run() { + error!("Metrics controller error: {:?}", e); + } + }) + .context("metrics thread failed")?, + ) + } else { + None + }; + let mut exit_state = ExitState::Stop; let mut pvpanic_code = PvPanicCode::Unknown; #[cfg(feature = "registered_events")] @@ -3955,6 +3977,20 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>( // control sockets are closed when this function exits. mem::drop(linux); + // Drop the hotplug manager to tell the warden process to exit before we try to join + // the metrics thread. + #[cfg(feature = "pci-hotplug")] + mem::drop(hotplug_manager); + + // All our children should have exited by now, so closing our fd should + // terminate metrics. Then join so that everything gets flushed. + metrics::get_destructor().cleanup(); + if let Some(metrics_thread) = metrics_thread { + if let Err(e) = metrics_thread.join() { + error!("failed to exit irq handler thread: {:?}", e); + } + } + stdin() .set_canon_mode() .expect("failed to restore canonical mode for terminal"); @@ -4400,6 +4436,7 @@ fn jail_and_start_vu_device<T: VirtioDeviceBuilder>( base::syslog::push_descriptors(&mut keep_rds); cros_tracing::push_descriptors!(&mut keep_rds); + metrics::push_descriptors(&mut keep_rds); let jail_type = VirtioDeviceType::VhostUser; diff --git a/src/crosvm/sys/linux/jail_warden.rs b/src/crosvm/sys/linux/jail_warden.rs index 3cd9e3420..cee109ed6 100644 --- a/src/crosvm/sys/linux/jail_warden.rs +++ b/src/crosvm/sys/linux/jail_warden.rs @@ -88,6 +88,7 @@ impl JailWardenImpl { let mut keep_rds = Vec::new(); syslog::push_descriptors(&mut keep_rds); cros_tracing::push_descriptors!(&mut keep_rds); + metrics::push_descriptors(&mut keep_rds); let (main_tube, worker_tube) = Tube::pair()?; keep_rds.push(worker_tube.as_raw_descriptor()); #[cfg(feature = "swap")] @@ -201,6 +202,7 @@ fn jail_worker_process( let mut keep_rds = vec![]; syslog::push_descriptors(&mut keep_rds); cros_tracing::push_descriptors!(&mut keep_rds); + metrics::push_descriptors(&mut keep_rds); keep_rds.extend(pci_device.keep_rds()); let proxy_device_primitive = ChildProcIntf::new( pci_device, diff --git a/src/crosvm/sys/windows/broker.rs b/src/crosvm/sys/windows/broker.rs index 55f8d287d..c5ba51821 100644 --- a/src/crosvm/sys/windows/broker.rs +++ b/src/crosvm/sys/windows/broker.rs @@ -102,8 +102,6 @@ use gpu_display::EventDevice; use gpu_display::WindowProcedureThread; #[cfg(feature = "gpu")] use gpu_display::WindowProcedureThreadBuilder; -use metrics::protos::event_details::EmulatorChildProcessExitDetails; -use metrics::protos::event_details::RecordDetails; use metrics::MetricEventType; #[cfg(all(feature = "net", feature = "slirp"))] use net_util::slirp::sys::windows::SlirpStartupConfig; @@ -399,12 +397,10 @@ impl Drop for ChildCleanup { if self.process_type != ProcessType::Metrics { let exit_code = self.child.wait(); if let Ok(Some(exit_code)) = exit_code { - let mut details = RecordDetails::new(); - let mut exit_details = EmulatorChildProcessExitDetails::new(); - exit_details.set_exit_code(exit_code as u32); - exit_details.set_process_type(self.process_type.into()); - details.emulator_child_process_exit_details = Some(exit_details).into(); - metrics::log_event_with_details(MetricEventType::ChildProcessExit, &details); + metrics::log_event(MetricEventType::ChildProcessExit { + exit_code: exit_code as u32, + process_type: self.process_type, + }); } else { error!( "Failed to log exit code for process: {:?}, couldn't get exit code", @@ -466,10 +462,11 @@ fn get_log_path(cfg: &Config, file_name: &str) -> Option<PathBuf> { /// IMPORTANT NOTE: The metrics process must receive the client (second) end /// of the Tube pair in order to allow the connection to be properly shut /// down without data loss. -fn metrics_tube_pair(metric_tubes: &mut Vec<Tube>) -> Result<Tube> { +fn metrics_tube_pair(metric_tubes: &mut Vec<RecvTube>) -> Result<SendTube> { // TODO(nkgold): as written, this Tube pair won't handle ancillary data properly because the // PIDs are not set properly at each end; however, we don't plan to send ancillary data. - let (t1, t2) = Tube::pair().exit_context(Exit::CreateTube, "failed to create tube")?; + let (t1, t2) = + Tube::directional_pair().exit_context(Exit::CreateTube, "failed to create tube")?; metric_tubes.push(t2); Ok(t1) } @@ -1193,7 +1190,7 @@ fn start_up_block_backends( exit_events: &mut Vec<Event>, wait_ctx: &mut WaitContext<Token>, main_child: &mut ChildProcess, - metric_tubes: &mut Vec<Tube>, + metric_tubes: &mut Vec<RecvTube>, #[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants, ) -> Result<Vec<ChildProcess>> { let mut block_children = Vec::new(); @@ -1421,7 +1418,7 @@ fn start_up_net_backend( wait_ctx: &mut WaitContext<Token>, cfg: &mut Config, log_args: &LogArgs, - metric_tubes: &mut Vec<Tube>, + metric_tubes: &mut Vec<RecvTube>, #[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants, ) -> Result<(ChildProcess, ChildProcess)> { let (host_pipe, guest_pipe) = named_pipes::pair_with_buffer_size( @@ -1609,7 +1606,7 @@ fn start_up_snd( main_child: &mut ChildProcess, children: &mut HashMap<u32, ChildCleanup>, wait_ctx: &mut WaitContext<Token>, - metric_tubes: &mut Vec<Tube>, + metric_tubes: &mut Vec<RecvTube>, #[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants, ) -> Result<ChildProcess> { // Extract the backend config from the sound config, so it can run elsewhere. @@ -1802,7 +1799,7 @@ fn start_up_gpu( main_child: &mut ChildProcess, children: &mut HashMap<u32, ChildCleanup>, wait_ctx: &mut WaitContext<Token>, - metric_tubes: &mut Vec<Tube>, + metric_tubes: &mut Vec<RecvTube>, wndproc_thread_builder: WindowProcedureThreadBuilder, #[cfg(feature = "process-invariants")] process_invariants: &EmulatorProcessInvariants, ) -> Result<ChildProcess> { diff --git a/src/sys/windows/main.rs b/src/sys/windows/main.rs index 44360777b..7775b5eb3 100644 --- a/src/sys/windows/main.rs +++ b/src/sys/windows/main.rs @@ -22,8 +22,6 @@ use broker_ipc::CommonChildStartupArgs; use crosvm_cli::sys::windows::exit::Exit; use crosvm_cli::sys::windows::exit::ExitContext; use crosvm_cli::sys::windows::exit::ExitContextAnyhow; -use metrics::protos::event_details::EmulatorDllDetails; -use metrics::protos::event_details::RecordDetails; use metrics::MetricEventType; #[cfg(feature = "slirp")] use net_util::slirp::sys::windows::SlirpStartupConfig; @@ -110,11 +108,7 @@ pub fn sandbox_lower_token() -> Result<()> { } fn report_dll_loaded(dll_name: String) { - let mut dll_load_details = EmulatorDllDetails::new(); - dll_load_details.set_dll_base_name(dll_name); - let mut details = RecordDetails::new(); - details.emulator_dll_details = Some(dll_load_details).into(); - metrics::log_event_with_details(MetricEventType::DllLoaded, &details); + metrics::log_event(MetricEventType::DllLoaded(dll_name)); } pub fn get_library_watcher( diff --git a/swap/Android.bp b/swap/Android.bp index 1dc7288e3..742feb72a 100644 --- a/swap/Android.bp +++ b/swap/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { @@ -27,6 +28,7 @@ rust_library { "libcros_tracing", "libjail", "liblibc", + "libmetrics", "libnum_cpus", "libonce_cell", "libserde", diff --git a/swap/Cargo.toml b/swap/Cargo.toml index 3833a6e69..86d7e5587 100644 --- a/swap/Cargo.toml +++ b/swap/Cargo.toml @@ -22,6 +22,7 @@ base = { path = "../base" } cfg-if = "*" cros_tracing = { path = "../cros_tracing" } jail = { path = "../jail" } +metrics = { path = "../metrics" } num_cpus = "*" once_cell = "*" remain = "*" diff --git a/swap/src/controller.rs b/swap/src/controller.rs index 8322ddb9a..928c2ac19 100644 --- a/swap/src/controller.rs +++ b/swap/src/controller.rs @@ -197,6 +197,7 @@ impl SwapController { syslog::push_descriptors(&mut keep_rds); cros_tracing::push_descriptors!(&mut keep_rds); + metrics::push_descriptors(&mut keep_rds); keep_rds.extend(guest_memory.as_raw_descriptors()); keep_rds.extend(uffd_factory.as_raw_descriptors()); diff --git a/swap/src/processes.rs b/swap/src/processes.rs index 0d8916999..b0fef61fc 100644 --- a/swap/src/processes.rs +++ b/swap/src/processes.rs @@ -6,11 +6,13 @@ use std::fs::read_to_string; use std::num::ParseIntError; +use std::path::Path; use std::str::FromStr; use std::thread::sleep; use std::time::Duration; use anyhow::anyhow; +use anyhow::bail; use anyhow::Context; use anyhow::Result; use base::linux::getpid; @@ -41,13 +43,20 @@ pub struct ProcessesGuard { /// /// This must be called from the main process. pub fn freeze_child_processes(monitor_pid: Pid) -> Result<ProcessesGuard> { - let guard = ProcessesGuard { + let mut guard = ProcessesGuard { pids: load_descendants(getpid(), monitor_pid)?, }; - guard.stop_the_world().context("stop the world")?; + for _ in 0..3 { + guard.stop_the_world().context("stop the world")?; + let pids_after = load_descendants(getpid(), monitor_pid)?; + if pids_after == guard.pids { + return Ok(guard); + } + guard.pids = pids_after; + } - Ok(guard) + bail!("new processes forked while freezing"); } impl ProcessesGuard { @@ -139,10 +148,9 @@ fn parse_process_state(text: &str) -> Option<char> { chars.next() } -fn wait_process_stopped(pid: Pid) -> Result<()> { - let process_stat_path = format!("/proc/{}/stat", pid); +fn wait_for_task_stopped(task_path: &Path) -> Result<()> { for _ in 0..10 { - let stat = read_to_string(&process_stat_path).context("read process status")?; + let stat = read_to_string(task_path.join("stat")).context("read process status")?; if let Some(state) = parse_process_state(&stat) { if state == 'T' { return Ok(()); @@ -153,6 +161,14 @@ fn wait_process_stopped(pid: Pid) -> Result<()> { Err(anyhow!("time out")) } +fn wait_process_stopped(pid: Pid) -> Result<()> { + let all_tasks = std::fs::read_dir(format!("/proc/{}/task", pid)).context("read tasks")?; + for task in all_tasks { + wait_for_task_stopped(&task.context("read task entry")?.path()).context("wait for task")?; + } + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/swap/src/userfaultfd.rs b/swap/src/userfaultfd.rs index 41b7c41ba..10b9df6e1 100644 --- a/swap/src/userfaultfd.rs +++ b/swap/src/userfaultfd.rs @@ -25,6 +25,7 @@ use base::linux::MemoryMappingUnix; use base::AsRawDescriptor; use base::AsRawDescriptors; use base::FromRawDescriptor; +use base::IntoRawDescriptor; use base::MappedRegion; use base::MemoryMapping; use base::MemoryMappingBuilder; @@ -396,7 +397,7 @@ impl Userfaultfd { pub fn try_clone(&self) -> Result<Self> { let dup_desc = base::clone_descriptor(self).map_err(Error::Clone)?; // SAFETY: no one owns dup_desc. - let uffd = unsafe { Self::from_raw_descriptor(dup_desc) }; + let uffd = Self::from(unsafe { Uffd::from_raw_fd(dup_desc.into_raw_descriptor()) }); Ok(uffd) } } diff --git a/third_party/vmm_vhost/Android.bp b/third_party/vmm_vhost/Android.bp index b767bc492..13f98717f 100644 --- a/third_party/vmm_vhost/Android.bp +++ b/third_party/vmm_vhost/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/tools/chromeos/merge_bot b/tools/chromeos/merge_bot index 1e4a9ae0b..08268a54c 100755 --- a/tools/chromeos/merge_bot +++ b/tools/chromeos/merge_bot @@ -212,6 +212,11 @@ def create_merge_commits( if not commits: print("Nothing to merge.") return (0, False) + else: + commit_authors = git_log(f"HEAD..{revision}", "--pretty=%an").lines() + if all(map(lambda x: x == "recipe-roller", commit_authors)): + print("All commits are from recipe roller, don't merge yet") + return (0, False) # Create a merge commit for each batch batches = list(batched(commits, max_size)) if max_size > 0 else [commits] diff --git a/tools/impl/cros_container/Dockerfile b/tools/impl/cros_container/Dockerfile index 9bf4de6a4..4ba7d10c9 100644 --- a/tools/impl/cros_container/Dockerfile +++ b/tools/impl/cros_container/Dockerfile @@ -38,4 +38,4 @@ RUN repo sync -j 8 --current-branch RUN --security=insecure cros_sdk --create && rm /home/crosvmdev/chromiumos/.cache/sdks/* RUN --security=insecure cros_sdk setup_board --board=${BOARD} -RUN --security=insecure cros_sdk emerge-${BOARD} --update --deep -j$(nproc) crosvm +RUN --security=insecure cros_sdk emerge-${BOARD} --update --deep -j$(nproc) chromeos-base/crosvm diff --git a/usb_sys/Android.bp b/usb_sys/Android.bp index d5d60f8ec..f78cbf725 100644 --- a/usb_sys/Android.bp +++ b/usb_sys/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/usb_util/Android.bp b/usb_util/Android.bp index c3979105a..dcd10ce9d 100644 --- a/usb_util/Android.bp +++ b/usb_util/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/vendor/generic/anti_tamper/Android.bp b/vendor/generic/anti_tamper/Android.bp index 9065f3a59..b13eaa112 100644 --- a/vendor/generic/anti_tamper/Android.bp +++ b/vendor/generic/anti_tamper/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/vendor/generic/broker_ipc/Android.bp b/vendor/generic/broker_ipc/Android.bp index 13fce8054..bec0cebbc 100644 --- a/vendor/generic/broker_ipc/Android.bp +++ b/vendor/generic/broker_ipc/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/vendor/generic/crash_report/Android.bp b/vendor/generic/crash_report/Android.bp index 00a636f91..23becbc8d 100644 --- a/vendor/generic/crash_report/Android.bp +++ b/vendor/generic/crash_report/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/vendor/generic/crypto/Android.bp b/vendor/generic/crypto/Android.bp index 05b69120a..15db0799a 100644 --- a/vendor/generic/crypto/Android.bp +++ b/vendor/generic/crypto/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/vendor/generic/metrics/Android.bp b/vendor/generic/metrics/Android.bp index e208771ed..42a670288 100644 --- a/vendor/generic/metrics/Android.bp +++ b/vendor/generic/metrics/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { @@ -24,8 +25,6 @@ rust_library { rustlibs: [ "libanyhow", "libbase_rust", - "libcfg_if", - "libprotobuf", - "libserde", + "libmetrics_events", ], } diff --git a/vendor/generic/metrics/Cargo.toml b/vendor/generic/metrics/Cargo.toml index b7dd68dc9..b5f57f8b5 100644 --- a/vendor/generic/metrics/Cargo.toml +++ b/vendor/generic/metrics/Cargo.toml @@ -12,12 +12,4 @@ collect = [] [dependencies] anyhow = "*" base = { path = "../../../base" } -cfg-if = "*" -protobuf = "3.2" -serde = { version = "1", features = ["derive"] } - -[target.'cfg(windows)'.dependencies] -win_util = { path = "../../../win_util" } - -[build-dependencies] -proto_build_tools = { path = "../../../proto_build_tools" } +metrics_events = { path = "../../../metrics_events" } diff --git a/vendor/generic/metrics/build.rs b/vendor/generic/metrics/build.rs deleted file mode 100644 index bf1fe6179..000000000 --- a/vendor/generic/metrics/build.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2022 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::env; -use std::path::PathBuf; - -fn main() { - let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); - - #[cfg(debug_assertions)] - println!( - "cargo:rustc-link-search={}\\..\\..\\..\\libs\\debug\\", - manifest_dir - ); - #[cfg(not(debug_assertions))] - println!( - "cargo:rustc-link-search={}\\..\\..\\..\\libs\\release\\", - manifest_dir - ); - - build_protos(&PathBuf::from(manifest_dir)); -} - -fn build_protos(manifest_dir: &PathBuf) { - let mut event_details_path = manifest_dir.to_owned(); - event_details_path.extend(["protos", "event_details.proto"]); - - let mut out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR env does not exist.")); - // ANDROID: b/259142784 - we remove metrics_out subdir b/c cargo2android - // out_dir.push("metrics_protos"); - proto_build_tools::build_protos(&out_dir, &[event_details_path]); -} diff --git a/vendor/generic/metrics/protos/event_details.proto b/vendor/generic/metrics/protos/event_details.proto deleted file mode 100644 index 60e46f4e9..000000000 --- a/vendor/generic/metrics/protos/event_details.proto +++ /dev/null @@ -1,105 +0,0 @@ -// Provides data structures for additional details for metrics events. -syntax = "proto2"; - -message RecordDetails { - reserved 1 to 11, 14 to 18; - // Additional details about an unexpected exit of a child process within - // the emulator. - optional EmulatorChildProcessExitDetails emulator_child_process_exit_details = - 12; - // Additional details about wave formats from the Window's host system. - optional WaveFormatDetails wave_format_details = 13; - optional EmulatorDllDetails emulator_dll_details = 19; -} - -message WaveFormatDetails { - // Format requested by WASAPI `GetMixFormat` system call. - optional WaveFormat requested = 1; - // Originally the requested wave format that's modified by the emulator. Only - // populated if the emulator decides the requested wave format should not be - // used. - optional WaveFormat modified = 2; - // Format that is valid and closest matching to the modified format, if the - // modified was rejected. Should only be populated if modified is also - // non-null and was rejected by WASAPI `IsFormatSupported` system call. - optional WaveFormat closest_matched = 3; -} - -// Defines the format of waveformat audio data. This information is used by -// WASAPI to determine how to process the audio playback data coming from the -// emulator. -// -// The fields in the structure come from WAVEFORMATEXTENSIBLE of win32 api. -// https://docs.microsoft.com/en-us/windows/win32/api/mmreg/ns-mmreg-waveformatextensible -message WaveFormat { - // Ex. 65534 (Maps to WAVE_FORMAT_EXTENSIBLE) - optional int32 format_tag = 1; - // Number of channels. - optional int32 channels = 2; - // Sample rate in Hz. Ex: 48000 - optional int32 samples_per_sec = 3; - // Required average data-transfer rate for the format tag. Usually this will - // be samples_per_sec * block_align, since the format tag is usually - // WAVE_FORMAT_IEEE_FLOAT or it's extensible and SubFormat is - // KSDATAFORMAT_SUBTYPE_IEEE_FLOAT. - optional int32 avg_bytes_per_sec = 4; - // Minimum atomic unit of data based on the format_tag. Usually this will - // just be bits_per_samples * channels. - optional int32 block_align = 5; - // Bits used per sample. Must be a multiple of 8. - optional int32 bits_per_sample = 6; - // Size in bytes of extra information appended to WAVEFORMATEX struct. - optional int32 size_bytes = 7; - - // The next fields are part of the WAVEFORMATEXTENSIBLE struct. They will only - // be non-null if format_tag is WAVE_FORMAT_EXTENSIBLE. - - // Bit depth. Can be any value. Ex. bits_per_sample is 24, - // but samples is 20. Note: This value is a union, so it could mean something - // slightly different, but most likely won't. Refer to doc for more info. - optional int32 samples = 8; - // Bitmask mapping channels in stream to speaker positions. - // Ex. 3 ( SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT ) - optional int64 channel_mask = 9; - // Similar to format_tag, but for WAVEFORMATEXTENSIBLE structs. - optional WaveFormatSubFormat sub_format = 10; - - // Subformat GUID mapping: - // https://github.com/retep998/winapi-rs/blob/2f76bdea3a79817ccfab496fbd1786d5a697387b/src/shared/ksmedia.rs - enum WaveFormatSubFormat { - KSDATAFORMAT_SUBTYPE_INVALID = 0; - KSDATAFORMAT_SUBTYPE_ANALOG = 1; - KSDATAFORMAT_SUBTYPE_PCM = 2; - KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = 3; - KSDATAFORMAT_SUBTYPE_DRM = 4; - KSDATAFORMAT_SUBTYPE_ALAW = 5; - KSDATAFORMAT_SUBTYPE_MULAW = 6; - KSDATAFORMAT_SUBTYPE_ADPCM = 7; - KSDATAFORMAT_SUBTYPE_MPEG = 8; - } -} - -enum EmulatorProcessType { - PROCESS_TYPE_UNKNOWN = 0; - PROCESS_TYPE_MAIN = 1; - PROCESS_TYPE_BLOCK = 2; - PROCESS_TYPE_METRICS = 3; - PROCESS_TYPE_NET = 4; - PROCESS_TYPE_SLIRP = 5; - PROCESS_TYPE_GPU = 6; - PROCESS_TYPE_SOUND = 7; - PROCESS_TYPE_BROKER = 8; - PROCESS_TYPE_SPU = 9; -} - -message EmulatorChildProcessExitDetails { - // The Windows exit code of the child process - optional uint32 exit_code = 1; - // The process identifier, as defined by the ProcessType enum in the - // emulator code. - optional EmulatorProcessType process_type = 2; -} - -message EmulatorDllDetails { - optional string dll_base_name = 1; -} diff --git a/vendor/generic/metrics/src/client.rs b/vendor/generic/metrics/src/client.rs index 380cca580..f7e691ac9 100644 --- a/vendor/generic/metrics/src/client.rs +++ b/vendor/generic/metrics/src/client.rs @@ -2,17 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use base::Tube; +use base::RawDescriptor; +use base::SendTube; +use metrics_events::MetricEventType; +use metrics_events::RecordDetails; -use crate::protos::event_details::RecordDetails; -use crate::MetricEventType; use crate::MetricsClientDestructor; /// This interface exists to be used and re-implemented by downstream forks. Updates shouldn't be /// done without ensuring they won't cause breakages in dependent codebases. -pub fn initialize(_: Tube) {} +pub fn initialize(_: SendTube) {} #[cfg(test)] -pub fn force_initialize(_: Tube) {} +pub fn force_initialize(_: SendTube) {} + +pub fn push_descriptors(_: &mut Vec<RawDescriptor>) {} + pub fn get_destructor() -> MetricsClientDestructor { MetricsClientDestructor::new(|| {}) } diff --git a/vendor/generic/metrics/src/event_types.rs b/vendor/generic/metrics/src/event_types.rs deleted file mode 100644 index 64ee5edd5..000000000 --- a/vendor/generic/metrics/src/event_types.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2022 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use std::convert::From; -use std::convert::TryFrom; - -use anyhow::Error; -use serde::Deserialize; -use serde::Serialize; - -// TODO(mikehoyle): Create a way to generate these directly from the -// proto for a single source-of-truth. -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] -pub enum MetricEventType { - CpuUsage, - MemoryUsage, - Fps, - JankyFps, - NetworkTxRate, - NetworkRxRate, - Interrupts, - FrameTime, - EmulatorGraphicsFreeze, - EmulatorGraphicsUnfreeze, - EmulatorGfxstreamVkAbortReason, - ChildProcessExit, - ReadIo, - WriteIo, - AudioFormatRequestOk, - AudioFormatModifiedOk, - AudioFormatFailed, - TscCoresOutOfSync, - NetworkTxRateSummarized, - NetworkRxRateSummarized, - DllLoaded, - GraphicsHangRenderThread, - GraphicsHangSyncThread, - AudioNoopStreamForced, - AudioPlaybackError, - Other(i64), -} - -impl From<MetricEventType> for i64 { - fn from(event_code: MetricEventType) -> Self { - match event_code { - MetricEventType::CpuUsage => 10001, - MetricEventType::MemoryUsage => 10002, - MetricEventType::Fps => 10003, - MetricEventType::JankyFps => 10004, - MetricEventType::NetworkTxRate => 10005, - MetricEventType::NetworkRxRate => 10006, - MetricEventType::Interrupts => 10007, - MetricEventType::FrameTime => 10008, - MetricEventType::EmulatorGraphicsFreeze => 10009, - MetricEventType::EmulatorGraphicsUnfreeze => 10010, - MetricEventType::EmulatorGfxstreamVkAbortReason => 10011, - MetricEventType::ChildProcessExit => 10012, - MetricEventType::ReadIo => 10013, - MetricEventType::WriteIo => 10014, - MetricEventType::AudioFormatRequestOk => 10015, - MetricEventType::AudioFormatModifiedOk => 10016, - MetricEventType::AudioFormatFailed => 10017, - MetricEventType::TscCoresOutOfSync => 10018, - MetricEventType::NetworkTxRateSummarized => 10019, - MetricEventType::NetworkRxRateSummarized => 10020, - MetricEventType::DllLoaded => 10021, - MetricEventType::GraphicsHangRenderThread => 10024, - MetricEventType::GraphicsHangSyncThread => 10026, - MetricEventType::AudioNoopStreamForced => 10038, - MetricEventType::AudioPlaybackError => 10039, - MetricEventType::Other(code) => code, - } - } -} - -impl TryFrom<i64> for MetricEventType { - type Error = Error; - - fn try_from(event_code: i64) -> Result<Self, Self::Error> { - match event_code { - 10001 => Ok(MetricEventType::CpuUsage), - 10002 => Ok(MetricEventType::MemoryUsage), - 10003 => Ok(MetricEventType::Fps), - 10004 => Ok(MetricEventType::JankyFps), - 10005 => Ok(MetricEventType::NetworkTxRate), - 10006 => Ok(MetricEventType::NetworkRxRate), - 10007 => Ok(MetricEventType::Interrupts), - 10008 => Ok(MetricEventType::FrameTime), - 10009 => Ok(MetricEventType::EmulatorGraphicsFreeze), - 10010 => Ok(MetricEventType::EmulatorGraphicsUnfreeze), - 10011 => Ok(MetricEventType::EmulatorGfxstreamVkAbortReason), - 10012 => Ok(MetricEventType::ChildProcessExit), - 10013 => Ok(MetricEventType::ReadIo), - 10014 => Ok(MetricEventType::WriteIo), - 10015 => Ok(MetricEventType::AudioFormatRequestOk), - 10016 => Ok(MetricEventType::AudioFormatModifiedOk), - 10017 => Ok(MetricEventType::AudioFormatFailed), - 10018 => Ok(MetricEventType::TscCoresOutOfSync), - 10019 => Ok(MetricEventType::NetworkTxRateSummarized), - 10020 => Ok(MetricEventType::NetworkRxRateSummarized), - 10021 => Ok(MetricEventType::DllLoaded), - 10024 => Ok(MetricEventType::GraphicsHangRenderThread), - 10026 => Ok(MetricEventType::GraphicsHangSyncThread), - 10038 => Ok(MetricEventType::AudioNoopStreamForced), - 10039 => Ok(MetricEventType::AudioPlaybackError), - _ => Ok(MetricEventType::Other(event_code)), - } - } -} diff --git a/vendor/generic/metrics/src/lib.rs b/vendor/generic/metrics/src/lib.rs index 45c30d3c0..da5c81217 100644 --- a/vendor/generic/metrics/src/lib.rs +++ b/vendor/generic/metrics/src/lib.rs @@ -6,16 +6,9 @@ //! to log metrics. mod client; -mod event_types; -mod metrics_requests; mod periodic_logger; mod request_handler; -mod sys; -pub mod protos { - // ANDROID: b/259142784 - we remove metrics_out subdir b/c cargo2android - include!(concat!(env!("OUT_DIR"), "/generated.rs")); -} mod metrics_cleanup; use std::time::Duration; @@ -32,12 +25,11 @@ pub use client::log_high_frequency_descriptor_event; pub use client::log_histogram_metric; pub use client::log_metric; pub use client::merge_session_invariants; +pub use client::push_descriptors; pub use client::set_auth_token; pub use client::set_graphics_api; pub use client::set_package_name; -pub use event_types::MetricEventType; pub use metrics_cleanup::MetricsClientDestructor; -pub use metrics_requests::MetricsRequest; pub use periodic_logger::PeriodicLogger; pub use request_handler::MetricsRequestHandler; diff --git a/vendor/generic/metrics/src/metrics_requests.rs b/vendor/generic/metrics/src/metrics_requests.rs deleted file mode 100644 index 33b77f22e..000000000 --- a/vendor/generic/metrics/src/metrics_requests.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2022 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -//! Structs used to transport log requests between client processes and the logging controller - -use serde::Deserialize; -use serde::Serialize; - -use crate::MetricEventType; - -#[derive(Serialize, Deserialize, Debug)] -pub struct LogMetric { - pub event_code: MetricEventType, - pub value: i64, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct LogDescriptor { - pub event_code: MetricEventType, - pub descriptor: i64, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct LogHighFrequencyDescriptorMetric { - pub event_code: MetricEventType, - pub descriptor: i64, - pub step: i64, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct EventWithSerializedDetails { - pub event_code: MetricEventType, - pub serialized_details: Box<[u8]>, -} - -#[derive(Serialize, Deserialize, Debug)] -pub enum MetricsRequest { - LogDescriptor(LogDescriptor), - LogEvent(MetricEventType), - LogMetric(LogMetric), - LogHistogram(LogMetric), - SetAuthToken(String), - SetGraphicsApi(String), - SetPackageName(String), - MergeSessionInvariants(Vec<u8>), - LogHighFrequencyDescriptorMetric(LogHighFrequencyDescriptorMetric), - LogEventWithSerializedDetails(EventWithSerializedDetails), -} diff --git a/vendor/generic/metrics/src/out/event_details.rs b/vendor/generic/metrics/src/out/event_details.rs deleted file mode 100644 index a44b05c90..000000000 --- a/vendor/generic/metrics/src/out/event_details.rs +++ /dev/null @@ -1,1408 +0,0 @@ -// This file is generated by rust-protobuf 3.2.0. Do not edit -// .proto file is parsed by protoc 3.21.12 -// @generated - -// https://github.com/rust-lang/rust-clippy/issues/702 -#![allow(unknown_lints)] -#![allow(clippy::all)] - -#![allow(unused_attributes)] -#![cfg_attr(rustfmt, rustfmt::skip)] - -#![allow(box_pointers)] -#![allow(dead_code)] -#![allow(missing_docs)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] -#![allow(non_upper_case_globals)] -#![allow(trivial_casts)] -#![allow(unused_results)] -#![allow(unused_mut)] - -//! Generated file from `event_details.proto` - -/// Generated files are compatible only with the same version -/// of protobuf runtime. -const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0; - -#[derive(PartialEq,Clone,Default,Debug)] -// @@protoc_insertion_point(message:RecordDetails) -pub struct RecordDetails { - // message fields - // @@protoc_insertion_point(field:RecordDetails.emulator_child_process_exit_details) - pub emulator_child_process_exit_details: ::protobuf::MessageField<EmulatorChildProcessExitDetails>, - // @@protoc_insertion_point(field:RecordDetails.wave_format_details) - pub wave_format_details: ::protobuf::MessageField<WaveFormatDetails>, - // @@protoc_insertion_point(field:RecordDetails.emulator_dll_details) - pub emulator_dll_details: ::protobuf::MessageField<EmulatorDllDetails>, - // special fields - // @@protoc_insertion_point(special_field:RecordDetails.special_fields) - pub special_fields: ::protobuf::SpecialFields, -} - -impl<'a> ::std::default::Default for &'a RecordDetails { - fn default() -> &'a RecordDetails { - <RecordDetails as ::protobuf::Message>::default_instance() - } -} - -impl RecordDetails { - pub fn new() -> RecordDetails { - ::std::default::Default::default() - } - - fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { - let mut fields = ::std::vec::Vec::with_capacity(3); - let mut oneofs = ::std::vec::Vec::with_capacity(0); - fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, EmulatorChildProcessExitDetails>( - "emulator_child_process_exit_details", - |m: &RecordDetails| { &m.emulator_child_process_exit_details }, - |m: &mut RecordDetails| { &mut m.emulator_child_process_exit_details }, - )); - fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, WaveFormatDetails>( - "wave_format_details", - |m: &RecordDetails| { &m.wave_format_details }, - |m: &mut RecordDetails| { &mut m.wave_format_details }, - )); - fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, EmulatorDllDetails>( - "emulator_dll_details", - |m: &RecordDetails| { &m.emulator_dll_details }, - |m: &mut RecordDetails| { &mut m.emulator_dll_details }, - )); - ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<RecordDetails>( - "RecordDetails", - fields, - oneofs, - ) - } -} - -impl ::protobuf::Message for RecordDetails { - const NAME: &'static str = "RecordDetails"; - - fn is_initialized(&self) -> bool { - true - } - - fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { - while let Some(tag) = is.read_raw_tag_or_eof()? { - match tag { - 98 => { - ::protobuf::rt::read_singular_message_into_field(is, &mut self.emulator_child_process_exit_details)?; - }, - 106 => { - ::protobuf::rt::read_singular_message_into_field(is, &mut self.wave_format_details)?; - }, - 154 => { - ::protobuf::rt::read_singular_message_into_field(is, &mut self.emulator_dll_details)?; - }, - tag => { - ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; - }, - }; - } - ::std::result::Result::Ok(()) - } - - // Compute sizes of nested messages - #[allow(unused_variables)] - fn compute_size(&self) -> u64 { - let mut my_size = 0; - if let Some(v) = self.emulator_child_process_exit_details.as_ref() { - let len = v.compute_size(); - my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; - } - if let Some(v) = self.wave_format_details.as_ref() { - let len = v.compute_size(); - my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; - } - if let Some(v) = self.emulator_dll_details.as_ref() { - let len = v.compute_size(); - my_size += 2 + ::protobuf::rt::compute_raw_varint64_size(len) + len; - } - my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); - self.special_fields.cached_size().set(my_size as u32); - my_size - } - - fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { - if let Some(v) = self.emulator_child_process_exit_details.as_ref() { - ::protobuf::rt::write_message_field_with_cached_size(12, v, os)?; - } - if let Some(v) = self.wave_format_details.as_ref() { - ::protobuf::rt::write_message_field_with_cached_size(13, v, os)?; - } - if let Some(v) = self.emulator_dll_details.as_ref() { - ::protobuf::rt::write_message_field_with_cached_size(19, v, os)?; - } - os.write_unknown_fields(self.special_fields.unknown_fields())?; - ::std::result::Result::Ok(()) - } - - fn special_fields(&self) -> &::protobuf::SpecialFields { - &self.special_fields - } - - fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { - &mut self.special_fields - } - - fn new() -> RecordDetails { - RecordDetails::new() - } - - fn clear(&mut self) { - self.emulator_child_process_exit_details.clear(); - self.wave_format_details.clear(); - self.emulator_dll_details.clear(); - self.special_fields.clear(); - } - - fn default_instance() -> &'static RecordDetails { - static instance: RecordDetails = RecordDetails { - emulator_child_process_exit_details: ::protobuf::MessageField::none(), - wave_format_details: ::protobuf::MessageField::none(), - emulator_dll_details: ::protobuf::MessageField::none(), - special_fields: ::protobuf::SpecialFields::new(), - }; - &instance - } -} - -impl ::protobuf::MessageFull for RecordDetails { - fn descriptor() -> ::protobuf::reflect::MessageDescriptor { - static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); - descriptor.get(|| file_descriptor().message_by_package_relative_name("RecordDetails").unwrap()).clone() - } -} - -impl ::std::fmt::Display for RecordDetails { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - ::protobuf::text_format::fmt(self, f) - } -} - -impl ::protobuf::reflect::ProtobufValue for RecordDetails { - type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; -} - -#[derive(PartialEq,Clone,Default,Debug)] -// @@protoc_insertion_point(message:WaveFormatDetails) -pub struct WaveFormatDetails { - // message fields - // @@protoc_insertion_point(field:WaveFormatDetails.requested) - pub requested: ::protobuf::MessageField<WaveFormat>, - // @@protoc_insertion_point(field:WaveFormatDetails.modified) - pub modified: ::protobuf::MessageField<WaveFormat>, - // @@protoc_insertion_point(field:WaveFormatDetails.closest_matched) - pub closest_matched: ::protobuf::MessageField<WaveFormat>, - // special fields - // @@protoc_insertion_point(special_field:WaveFormatDetails.special_fields) - pub special_fields: ::protobuf::SpecialFields, -} - -impl<'a> ::std::default::Default for &'a WaveFormatDetails { - fn default() -> &'a WaveFormatDetails { - <WaveFormatDetails as ::protobuf::Message>::default_instance() - } -} - -impl WaveFormatDetails { - pub fn new() -> WaveFormatDetails { - ::std::default::Default::default() - } - - fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { - let mut fields = ::std::vec::Vec::with_capacity(3); - let mut oneofs = ::std::vec::Vec::with_capacity(0); - fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, WaveFormat>( - "requested", - |m: &WaveFormatDetails| { &m.requested }, - |m: &mut WaveFormatDetails| { &mut m.requested }, - )); - fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, WaveFormat>( - "modified", - |m: &WaveFormatDetails| { &m.modified }, - |m: &mut WaveFormatDetails| { &mut m.modified }, - )); - fields.push(::protobuf::reflect::rt::v2::make_message_field_accessor::<_, WaveFormat>( - "closest_matched", - |m: &WaveFormatDetails| { &m.closest_matched }, - |m: &mut WaveFormatDetails| { &mut m.closest_matched }, - )); - ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<WaveFormatDetails>( - "WaveFormatDetails", - fields, - oneofs, - ) - } -} - -impl ::protobuf::Message for WaveFormatDetails { - const NAME: &'static str = "WaveFormatDetails"; - - fn is_initialized(&self) -> bool { - true - } - - fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { - while let Some(tag) = is.read_raw_tag_or_eof()? { - match tag { - 10 => { - ::protobuf::rt::read_singular_message_into_field(is, &mut self.requested)?; - }, - 18 => { - ::protobuf::rt::read_singular_message_into_field(is, &mut self.modified)?; - }, - 26 => { - ::protobuf::rt::read_singular_message_into_field(is, &mut self.closest_matched)?; - }, - tag => { - ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; - }, - }; - } - ::std::result::Result::Ok(()) - } - - // Compute sizes of nested messages - #[allow(unused_variables)] - fn compute_size(&self) -> u64 { - let mut my_size = 0; - if let Some(v) = self.requested.as_ref() { - let len = v.compute_size(); - my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; - } - if let Some(v) = self.modified.as_ref() { - let len = v.compute_size(); - my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; - } - if let Some(v) = self.closest_matched.as_ref() { - let len = v.compute_size(); - my_size += 1 + ::protobuf::rt::compute_raw_varint64_size(len) + len; - } - my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); - self.special_fields.cached_size().set(my_size as u32); - my_size - } - - fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { - if let Some(v) = self.requested.as_ref() { - ::protobuf::rt::write_message_field_with_cached_size(1, v, os)?; - } - if let Some(v) = self.modified.as_ref() { - ::protobuf::rt::write_message_field_with_cached_size(2, v, os)?; - } - if let Some(v) = self.closest_matched.as_ref() { - ::protobuf::rt::write_message_field_with_cached_size(3, v, os)?; - } - os.write_unknown_fields(self.special_fields.unknown_fields())?; - ::std::result::Result::Ok(()) - } - - fn special_fields(&self) -> &::protobuf::SpecialFields { - &self.special_fields - } - - fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { - &mut self.special_fields - } - - fn new() -> WaveFormatDetails { - WaveFormatDetails::new() - } - - fn clear(&mut self) { - self.requested.clear(); - self.modified.clear(); - self.closest_matched.clear(); - self.special_fields.clear(); - } - - fn default_instance() -> &'static WaveFormatDetails { - static instance: WaveFormatDetails = WaveFormatDetails { - requested: ::protobuf::MessageField::none(), - modified: ::protobuf::MessageField::none(), - closest_matched: ::protobuf::MessageField::none(), - special_fields: ::protobuf::SpecialFields::new(), - }; - &instance - } -} - -impl ::protobuf::MessageFull for WaveFormatDetails { - fn descriptor() -> ::protobuf::reflect::MessageDescriptor { - static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); - descriptor.get(|| file_descriptor().message_by_package_relative_name("WaveFormatDetails").unwrap()).clone() - } -} - -impl ::std::fmt::Display for WaveFormatDetails { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - ::protobuf::text_format::fmt(self, f) - } -} - -impl ::protobuf::reflect::ProtobufValue for WaveFormatDetails { - type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; -} - -#[derive(PartialEq,Clone,Default,Debug)] -// @@protoc_insertion_point(message:WaveFormat) -pub struct WaveFormat { - // message fields - // @@protoc_insertion_point(field:WaveFormat.format_tag) - pub format_tag: ::std::option::Option<i32>, - // @@protoc_insertion_point(field:WaveFormat.channels) - pub channels: ::std::option::Option<i32>, - // @@protoc_insertion_point(field:WaveFormat.samples_per_sec) - pub samples_per_sec: ::std::option::Option<i32>, - // @@protoc_insertion_point(field:WaveFormat.avg_bytes_per_sec) - pub avg_bytes_per_sec: ::std::option::Option<i32>, - // @@protoc_insertion_point(field:WaveFormat.block_align) - pub block_align: ::std::option::Option<i32>, - // @@protoc_insertion_point(field:WaveFormat.bits_per_sample) - pub bits_per_sample: ::std::option::Option<i32>, - // @@protoc_insertion_point(field:WaveFormat.size_bytes) - pub size_bytes: ::std::option::Option<i32>, - // @@protoc_insertion_point(field:WaveFormat.samples) - pub samples: ::std::option::Option<i32>, - // @@protoc_insertion_point(field:WaveFormat.channel_mask) - pub channel_mask: ::std::option::Option<i64>, - // @@protoc_insertion_point(field:WaveFormat.sub_format) - pub sub_format: ::std::option::Option<::protobuf::EnumOrUnknown<wave_format::WaveFormatSubFormat>>, - // special fields - // @@protoc_insertion_point(special_field:WaveFormat.special_fields) - pub special_fields: ::protobuf::SpecialFields, -} - -impl<'a> ::std::default::Default for &'a WaveFormat { - fn default() -> &'a WaveFormat { - <WaveFormat as ::protobuf::Message>::default_instance() - } -} - -impl WaveFormat { - pub fn new() -> WaveFormat { - ::std::default::Default::default() - } - - // optional int32 format_tag = 1; - - pub fn format_tag(&self) -> i32 { - self.format_tag.unwrap_or(0) - } - - pub fn clear_format_tag(&mut self) { - self.format_tag = ::std::option::Option::None; - } - - pub fn has_format_tag(&self) -> bool { - self.format_tag.is_some() - } - - // Param is passed by value, moved - pub fn set_format_tag(&mut self, v: i32) { - self.format_tag = ::std::option::Option::Some(v); - } - - // optional int32 channels = 2; - - pub fn channels(&self) -> i32 { - self.channels.unwrap_or(0) - } - - pub fn clear_channels(&mut self) { - self.channels = ::std::option::Option::None; - } - - pub fn has_channels(&self) -> bool { - self.channels.is_some() - } - - // Param is passed by value, moved - pub fn set_channels(&mut self, v: i32) { - self.channels = ::std::option::Option::Some(v); - } - - // optional int32 samples_per_sec = 3; - - pub fn samples_per_sec(&self) -> i32 { - self.samples_per_sec.unwrap_or(0) - } - - pub fn clear_samples_per_sec(&mut self) { - self.samples_per_sec = ::std::option::Option::None; - } - - pub fn has_samples_per_sec(&self) -> bool { - self.samples_per_sec.is_some() - } - - // Param is passed by value, moved - pub fn set_samples_per_sec(&mut self, v: i32) { - self.samples_per_sec = ::std::option::Option::Some(v); - } - - // optional int32 avg_bytes_per_sec = 4; - - pub fn avg_bytes_per_sec(&self) -> i32 { - self.avg_bytes_per_sec.unwrap_or(0) - } - - pub fn clear_avg_bytes_per_sec(&mut self) { - self.avg_bytes_per_sec = ::std::option::Option::None; - } - - pub fn has_avg_bytes_per_sec(&self) -> bool { - self.avg_bytes_per_sec.is_some() - } - - // Param is passed by value, moved - pub fn set_avg_bytes_per_sec(&mut self, v: i32) { - self.avg_bytes_per_sec = ::std::option::Option::Some(v); - } - - // optional int32 block_align = 5; - - pub fn block_align(&self) -> i32 { - self.block_align.unwrap_or(0) - } - - pub fn clear_block_align(&mut self) { - self.block_align = ::std::option::Option::None; - } - - pub fn has_block_align(&self) -> bool { - self.block_align.is_some() - } - - // Param is passed by value, moved - pub fn set_block_align(&mut self, v: i32) { - self.block_align = ::std::option::Option::Some(v); - } - - // optional int32 bits_per_sample = 6; - - pub fn bits_per_sample(&self) -> i32 { - self.bits_per_sample.unwrap_or(0) - } - - pub fn clear_bits_per_sample(&mut self) { - self.bits_per_sample = ::std::option::Option::None; - } - - pub fn has_bits_per_sample(&self) -> bool { - self.bits_per_sample.is_some() - } - - // Param is passed by value, moved - pub fn set_bits_per_sample(&mut self, v: i32) { - self.bits_per_sample = ::std::option::Option::Some(v); - } - - // optional int32 size_bytes = 7; - - pub fn size_bytes(&self) -> i32 { - self.size_bytes.unwrap_or(0) - } - - pub fn clear_size_bytes(&mut self) { - self.size_bytes = ::std::option::Option::None; - } - - pub fn has_size_bytes(&self) -> bool { - self.size_bytes.is_some() - } - - // Param is passed by value, moved - pub fn set_size_bytes(&mut self, v: i32) { - self.size_bytes = ::std::option::Option::Some(v); - } - - // optional int32 samples = 8; - - pub fn samples(&self) -> i32 { - self.samples.unwrap_or(0) - } - - pub fn clear_samples(&mut self) { - self.samples = ::std::option::Option::None; - } - - pub fn has_samples(&self) -> bool { - self.samples.is_some() - } - - // Param is passed by value, moved - pub fn set_samples(&mut self, v: i32) { - self.samples = ::std::option::Option::Some(v); - } - - // optional int64 channel_mask = 9; - - pub fn channel_mask(&self) -> i64 { - self.channel_mask.unwrap_or(0) - } - - pub fn clear_channel_mask(&mut self) { - self.channel_mask = ::std::option::Option::None; - } - - pub fn has_channel_mask(&self) -> bool { - self.channel_mask.is_some() - } - - // Param is passed by value, moved - pub fn set_channel_mask(&mut self, v: i64) { - self.channel_mask = ::std::option::Option::Some(v); - } - - // optional .WaveFormat.WaveFormatSubFormat sub_format = 10; - - pub fn sub_format(&self) -> wave_format::WaveFormatSubFormat { - match self.sub_format { - Some(e) => e.enum_value_or(wave_format::WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_INVALID), - None => wave_format::WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_INVALID, - } - } - - pub fn clear_sub_format(&mut self) { - self.sub_format = ::std::option::Option::None; - } - - pub fn has_sub_format(&self) -> bool { - self.sub_format.is_some() - } - - // Param is passed by value, moved - pub fn set_sub_format(&mut self, v: wave_format::WaveFormatSubFormat) { - self.sub_format = ::std::option::Option::Some(::protobuf::EnumOrUnknown::new(v)); - } - - fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { - let mut fields = ::std::vec::Vec::with_capacity(10); - let mut oneofs = ::std::vec::Vec::with_capacity(0); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "format_tag", - |m: &WaveFormat| { &m.format_tag }, - |m: &mut WaveFormat| { &mut m.format_tag }, - )); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "channels", - |m: &WaveFormat| { &m.channels }, - |m: &mut WaveFormat| { &mut m.channels }, - )); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "samples_per_sec", - |m: &WaveFormat| { &m.samples_per_sec }, - |m: &mut WaveFormat| { &mut m.samples_per_sec }, - )); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "avg_bytes_per_sec", - |m: &WaveFormat| { &m.avg_bytes_per_sec }, - |m: &mut WaveFormat| { &mut m.avg_bytes_per_sec }, - )); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "block_align", - |m: &WaveFormat| { &m.block_align }, - |m: &mut WaveFormat| { &mut m.block_align }, - )); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "bits_per_sample", - |m: &WaveFormat| { &m.bits_per_sample }, - |m: &mut WaveFormat| { &mut m.bits_per_sample }, - )); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "size_bytes", - |m: &WaveFormat| { &m.size_bytes }, - |m: &mut WaveFormat| { &mut m.size_bytes }, - )); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "samples", - |m: &WaveFormat| { &m.samples }, - |m: &mut WaveFormat| { &mut m.samples }, - )); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "channel_mask", - |m: &WaveFormat| { &m.channel_mask }, - |m: &mut WaveFormat| { &mut m.channel_mask }, - )); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "sub_format", - |m: &WaveFormat| { &m.sub_format }, - |m: &mut WaveFormat| { &mut m.sub_format }, - )); - ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<WaveFormat>( - "WaveFormat", - fields, - oneofs, - ) - } -} - -impl ::protobuf::Message for WaveFormat { - const NAME: &'static str = "WaveFormat"; - - fn is_initialized(&self) -> bool { - true - } - - fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { - while let Some(tag) = is.read_raw_tag_or_eof()? { - match tag { - 8 => { - self.format_tag = ::std::option::Option::Some(is.read_int32()?); - }, - 16 => { - self.channels = ::std::option::Option::Some(is.read_int32()?); - }, - 24 => { - self.samples_per_sec = ::std::option::Option::Some(is.read_int32()?); - }, - 32 => { - self.avg_bytes_per_sec = ::std::option::Option::Some(is.read_int32()?); - }, - 40 => { - self.block_align = ::std::option::Option::Some(is.read_int32()?); - }, - 48 => { - self.bits_per_sample = ::std::option::Option::Some(is.read_int32()?); - }, - 56 => { - self.size_bytes = ::std::option::Option::Some(is.read_int32()?); - }, - 64 => { - self.samples = ::std::option::Option::Some(is.read_int32()?); - }, - 72 => { - self.channel_mask = ::std::option::Option::Some(is.read_int64()?); - }, - 80 => { - self.sub_format = ::std::option::Option::Some(is.read_enum_or_unknown()?); - }, - tag => { - ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; - }, - }; - } - ::std::result::Result::Ok(()) - } - - // Compute sizes of nested messages - #[allow(unused_variables)] - fn compute_size(&self) -> u64 { - let mut my_size = 0; - if let Some(v) = self.format_tag { - my_size += ::protobuf::rt::int32_size(1, v); - } - if let Some(v) = self.channels { - my_size += ::protobuf::rt::int32_size(2, v); - } - if let Some(v) = self.samples_per_sec { - my_size += ::protobuf::rt::int32_size(3, v); - } - if let Some(v) = self.avg_bytes_per_sec { - my_size += ::protobuf::rt::int32_size(4, v); - } - if let Some(v) = self.block_align { - my_size += ::protobuf::rt::int32_size(5, v); - } - if let Some(v) = self.bits_per_sample { - my_size += ::protobuf::rt::int32_size(6, v); - } - if let Some(v) = self.size_bytes { - my_size += ::protobuf::rt::int32_size(7, v); - } - if let Some(v) = self.samples { - my_size += ::protobuf::rt::int32_size(8, v); - } - if let Some(v) = self.channel_mask { - my_size += ::protobuf::rt::int64_size(9, v); - } - if let Some(v) = self.sub_format { - my_size += ::protobuf::rt::int32_size(10, v.value()); - } - my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); - self.special_fields.cached_size().set(my_size as u32); - my_size - } - - fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { - if let Some(v) = self.format_tag { - os.write_int32(1, v)?; - } - if let Some(v) = self.channels { - os.write_int32(2, v)?; - } - if let Some(v) = self.samples_per_sec { - os.write_int32(3, v)?; - } - if let Some(v) = self.avg_bytes_per_sec { - os.write_int32(4, v)?; - } - if let Some(v) = self.block_align { - os.write_int32(5, v)?; - } - if let Some(v) = self.bits_per_sample { - os.write_int32(6, v)?; - } - if let Some(v) = self.size_bytes { - os.write_int32(7, v)?; - } - if let Some(v) = self.samples { - os.write_int32(8, v)?; - } - if let Some(v) = self.channel_mask { - os.write_int64(9, v)?; - } - if let Some(v) = self.sub_format { - os.write_enum(10, ::protobuf::EnumOrUnknown::value(&v))?; - } - os.write_unknown_fields(self.special_fields.unknown_fields())?; - ::std::result::Result::Ok(()) - } - - fn special_fields(&self) -> &::protobuf::SpecialFields { - &self.special_fields - } - - fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { - &mut self.special_fields - } - - fn new() -> WaveFormat { - WaveFormat::new() - } - - fn clear(&mut self) { - self.format_tag = ::std::option::Option::None; - self.channels = ::std::option::Option::None; - self.samples_per_sec = ::std::option::Option::None; - self.avg_bytes_per_sec = ::std::option::Option::None; - self.block_align = ::std::option::Option::None; - self.bits_per_sample = ::std::option::Option::None; - self.size_bytes = ::std::option::Option::None; - self.samples = ::std::option::Option::None; - self.channel_mask = ::std::option::Option::None; - self.sub_format = ::std::option::Option::None; - self.special_fields.clear(); - } - - fn default_instance() -> &'static WaveFormat { - static instance: WaveFormat = WaveFormat { - format_tag: ::std::option::Option::None, - channels: ::std::option::Option::None, - samples_per_sec: ::std::option::Option::None, - avg_bytes_per_sec: ::std::option::Option::None, - block_align: ::std::option::Option::None, - bits_per_sample: ::std::option::Option::None, - size_bytes: ::std::option::Option::None, - samples: ::std::option::Option::None, - channel_mask: ::std::option::Option::None, - sub_format: ::std::option::Option::None, - special_fields: ::protobuf::SpecialFields::new(), - }; - &instance - } -} - -impl ::protobuf::MessageFull for WaveFormat { - fn descriptor() -> ::protobuf::reflect::MessageDescriptor { - static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); - descriptor.get(|| file_descriptor().message_by_package_relative_name("WaveFormat").unwrap()).clone() - } -} - -impl ::std::fmt::Display for WaveFormat { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - ::protobuf::text_format::fmt(self, f) - } -} - -impl ::protobuf::reflect::ProtobufValue for WaveFormat { - type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; -} - -/// Nested message and enums of message `WaveFormat` -pub mod wave_format { - #[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)] - // @@protoc_insertion_point(enum:WaveFormat.WaveFormatSubFormat) - pub enum WaveFormatSubFormat { - // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_INVALID) - KSDATAFORMAT_SUBTYPE_INVALID = 0, - // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_ANALOG) - KSDATAFORMAT_SUBTYPE_ANALOG = 1, - // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_PCM) - KSDATAFORMAT_SUBTYPE_PCM = 2, - // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) - KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = 3, - // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_DRM) - KSDATAFORMAT_SUBTYPE_DRM = 4, - // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_ALAW) - KSDATAFORMAT_SUBTYPE_ALAW = 5, - // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_MULAW) - KSDATAFORMAT_SUBTYPE_MULAW = 6, - // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_ADPCM) - KSDATAFORMAT_SUBTYPE_ADPCM = 7, - // @@protoc_insertion_point(enum_value:WaveFormat.WaveFormatSubFormat.KSDATAFORMAT_SUBTYPE_MPEG) - KSDATAFORMAT_SUBTYPE_MPEG = 8, - } - - impl ::protobuf::Enum for WaveFormatSubFormat { - const NAME: &'static str = "WaveFormatSubFormat"; - - fn value(&self) -> i32 { - *self as i32 - } - - fn from_i32(value: i32) -> ::std::option::Option<WaveFormatSubFormat> { - match value { - 0 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_INVALID), - 1 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_ANALOG), - 2 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_PCM), - 3 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT), - 4 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_DRM), - 5 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_ALAW), - 6 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_MULAW), - 7 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_ADPCM), - 8 => ::std::option::Option::Some(WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_MPEG), - _ => ::std::option::Option::None - } - } - - const VALUES: &'static [WaveFormatSubFormat] = &[ - WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_INVALID, - WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_ANALOG, - WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_PCM, - WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, - WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_DRM, - WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_ALAW, - WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_MULAW, - WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_ADPCM, - WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_MPEG, - ]; - } - - impl ::protobuf::EnumFull for WaveFormatSubFormat { - fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor { - static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new(); - descriptor.get(|| super::file_descriptor().enum_by_package_relative_name("WaveFormat.WaveFormatSubFormat").unwrap()).clone() - } - - fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor { - let index = *self as usize; - Self::enum_descriptor().value_by_index(index) - } - } - - impl ::std::default::Default for WaveFormatSubFormat { - fn default() -> Self { - WaveFormatSubFormat::KSDATAFORMAT_SUBTYPE_INVALID - } - } - - impl WaveFormatSubFormat { - pub(in super) fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData { - ::protobuf::reflect::GeneratedEnumDescriptorData::new::<WaveFormatSubFormat>("WaveFormat.WaveFormatSubFormat") - } - } -} - -#[derive(PartialEq,Clone,Default,Debug)] -// @@protoc_insertion_point(message:EmulatorChildProcessExitDetails) -pub struct EmulatorChildProcessExitDetails { - // message fields - // @@protoc_insertion_point(field:EmulatorChildProcessExitDetails.exit_code) - pub exit_code: ::std::option::Option<u32>, - // @@protoc_insertion_point(field:EmulatorChildProcessExitDetails.process_type) - pub process_type: ::std::option::Option<::protobuf::EnumOrUnknown<EmulatorProcessType>>, - // special fields - // @@protoc_insertion_point(special_field:EmulatorChildProcessExitDetails.special_fields) - pub special_fields: ::protobuf::SpecialFields, -} - -impl<'a> ::std::default::Default for &'a EmulatorChildProcessExitDetails { - fn default() -> &'a EmulatorChildProcessExitDetails { - <EmulatorChildProcessExitDetails as ::protobuf::Message>::default_instance() - } -} - -impl EmulatorChildProcessExitDetails { - pub fn new() -> EmulatorChildProcessExitDetails { - ::std::default::Default::default() - } - - // optional uint32 exit_code = 1; - - pub fn exit_code(&self) -> u32 { - self.exit_code.unwrap_or(0) - } - - pub fn clear_exit_code(&mut self) { - self.exit_code = ::std::option::Option::None; - } - - pub fn has_exit_code(&self) -> bool { - self.exit_code.is_some() - } - - // Param is passed by value, moved - pub fn set_exit_code(&mut self, v: u32) { - self.exit_code = ::std::option::Option::Some(v); - } - - // optional .EmulatorProcessType process_type = 2; - - pub fn process_type(&self) -> EmulatorProcessType { - match self.process_type { - Some(e) => e.enum_value_or(EmulatorProcessType::PROCESS_TYPE_UNKNOWN), - None => EmulatorProcessType::PROCESS_TYPE_UNKNOWN, - } - } - - pub fn clear_process_type(&mut self) { - self.process_type = ::std::option::Option::None; - } - - pub fn has_process_type(&self) -> bool { - self.process_type.is_some() - } - - // Param is passed by value, moved - pub fn set_process_type(&mut self, v: EmulatorProcessType) { - self.process_type = ::std::option::Option::Some(::protobuf::EnumOrUnknown::new(v)); - } - - fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { - let mut fields = ::std::vec::Vec::with_capacity(2); - let mut oneofs = ::std::vec::Vec::with_capacity(0); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "exit_code", - |m: &EmulatorChildProcessExitDetails| { &m.exit_code }, - |m: &mut EmulatorChildProcessExitDetails| { &mut m.exit_code }, - )); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "process_type", - |m: &EmulatorChildProcessExitDetails| { &m.process_type }, - |m: &mut EmulatorChildProcessExitDetails| { &mut m.process_type }, - )); - ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<EmulatorChildProcessExitDetails>( - "EmulatorChildProcessExitDetails", - fields, - oneofs, - ) - } -} - -impl ::protobuf::Message for EmulatorChildProcessExitDetails { - const NAME: &'static str = "EmulatorChildProcessExitDetails"; - - fn is_initialized(&self) -> bool { - true - } - - fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { - while let Some(tag) = is.read_raw_tag_or_eof()? { - match tag { - 8 => { - self.exit_code = ::std::option::Option::Some(is.read_uint32()?); - }, - 16 => { - self.process_type = ::std::option::Option::Some(is.read_enum_or_unknown()?); - }, - tag => { - ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; - }, - }; - } - ::std::result::Result::Ok(()) - } - - // Compute sizes of nested messages - #[allow(unused_variables)] - fn compute_size(&self) -> u64 { - let mut my_size = 0; - if let Some(v) = self.exit_code { - my_size += ::protobuf::rt::uint32_size(1, v); - } - if let Some(v) = self.process_type { - my_size += ::protobuf::rt::int32_size(2, v.value()); - } - my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); - self.special_fields.cached_size().set(my_size as u32); - my_size - } - - fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { - if let Some(v) = self.exit_code { - os.write_uint32(1, v)?; - } - if let Some(v) = self.process_type { - os.write_enum(2, ::protobuf::EnumOrUnknown::value(&v))?; - } - os.write_unknown_fields(self.special_fields.unknown_fields())?; - ::std::result::Result::Ok(()) - } - - fn special_fields(&self) -> &::protobuf::SpecialFields { - &self.special_fields - } - - fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { - &mut self.special_fields - } - - fn new() -> EmulatorChildProcessExitDetails { - EmulatorChildProcessExitDetails::new() - } - - fn clear(&mut self) { - self.exit_code = ::std::option::Option::None; - self.process_type = ::std::option::Option::None; - self.special_fields.clear(); - } - - fn default_instance() -> &'static EmulatorChildProcessExitDetails { - static instance: EmulatorChildProcessExitDetails = EmulatorChildProcessExitDetails { - exit_code: ::std::option::Option::None, - process_type: ::std::option::Option::None, - special_fields: ::protobuf::SpecialFields::new(), - }; - &instance - } -} - -impl ::protobuf::MessageFull for EmulatorChildProcessExitDetails { - fn descriptor() -> ::protobuf::reflect::MessageDescriptor { - static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); - descriptor.get(|| file_descriptor().message_by_package_relative_name("EmulatorChildProcessExitDetails").unwrap()).clone() - } -} - -impl ::std::fmt::Display for EmulatorChildProcessExitDetails { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - ::protobuf::text_format::fmt(self, f) - } -} - -impl ::protobuf::reflect::ProtobufValue for EmulatorChildProcessExitDetails { - type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; -} - -#[derive(PartialEq,Clone,Default,Debug)] -// @@protoc_insertion_point(message:EmulatorDllDetails) -pub struct EmulatorDllDetails { - // message fields - // @@protoc_insertion_point(field:EmulatorDllDetails.dll_base_name) - pub dll_base_name: ::std::option::Option<::std::string::String>, - // special fields - // @@protoc_insertion_point(special_field:EmulatorDllDetails.special_fields) - pub special_fields: ::protobuf::SpecialFields, -} - -impl<'a> ::std::default::Default for &'a EmulatorDllDetails { - fn default() -> &'a EmulatorDllDetails { - <EmulatorDllDetails as ::protobuf::Message>::default_instance() - } -} - -impl EmulatorDllDetails { - pub fn new() -> EmulatorDllDetails { - ::std::default::Default::default() - } - - // optional string dll_base_name = 1; - - pub fn dll_base_name(&self) -> &str { - match self.dll_base_name.as_ref() { - Some(v) => v, - None => "", - } - } - - pub fn clear_dll_base_name(&mut self) { - self.dll_base_name = ::std::option::Option::None; - } - - pub fn has_dll_base_name(&self) -> bool { - self.dll_base_name.is_some() - } - - // Param is passed by value, moved - pub fn set_dll_base_name(&mut self, v: ::std::string::String) { - self.dll_base_name = ::std::option::Option::Some(v); - } - - // Mutable pointer to the field. - // If field is not initialized, it is initialized with default value first. - pub fn mut_dll_base_name(&mut self) -> &mut ::std::string::String { - if self.dll_base_name.is_none() { - self.dll_base_name = ::std::option::Option::Some(::std::string::String::new()); - } - self.dll_base_name.as_mut().unwrap() - } - - // Take field - pub fn take_dll_base_name(&mut self) -> ::std::string::String { - self.dll_base_name.take().unwrap_or_else(|| ::std::string::String::new()) - } - - fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData { - let mut fields = ::std::vec::Vec::with_capacity(1); - let mut oneofs = ::std::vec::Vec::with_capacity(0); - fields.push(::protobuf::reflect::rt::v2::make_option_accessor::<_, _>( - "dll_base_name", - |m: &EmulatorDllDetails| { &m.dll_base_name }, - |m: &mut EmulatorDllDetails| { &mut m.dll_base_name }, - )); - ::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<EmulatorDllDetails>( - "EmulatorDllDetails", - fields, - oneofs, - ) - } -} - -impl ::protobuf::Message for EmulatorDllDetails { - const NAME: &'static str = "EmulatorDllDetails"; - - fn is_initialized(&self) -> bool { - true - } - - fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { - while let Some(tag) = is.read_raw_tag_or_eof()? { - match tag { - 10 => { - self.dll_base_name = ::std::option::Option::Some(is.read_string()?); - }, - tag => { - ::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?; - }, - }; - } - ::std::result::Result::Ok(()) - } - - // Compute sizes of nested messages - #[allow(unused_variables)] - fn compute_size(&self) -> u64 { - let mut my_size = 0; - if let Some(v) = self.dll_base_name.as_ref() { - my_size += ::protobuf::rt::string_size(1, &v); - } - my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields()); - self.special_fields.cached_size().set(my_size as u32); - my_size - } - - fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> { - if let Some(v) = self.dll_base_name.as_ref() { - os.write_string(1, v)?; - } - os.write_unknown_fields(self.special_fields.unknown_fields())?; - ::std::result::Result::Ok(()) - } - - fn special_fields(&self) -> &::protobuf::SpecialFields { - &self.special_fields - } - - fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields { - &mut self.special_fields - } - - fn new() -> EmulatorDllDetails { - EmulatorDllDetails::new() - } - - fn clear(&mut self) { - self.dll_base_name = ::std::option::Option::None; - self.special_fields.clear(); - } - - fn default_instance() -> &'static EmulatorDllDetails { - static instance: EmulatorDllDetails = EmulatorDllDetails { - dll_base_name: ::std::option::Option::None, - special_fields: ::protobuf::SpecialFields::new(), - }; - &instance - } -} - -impl ::protobuf::MessageFull for EmulatorDllDetails { - fn descriptor() -> ::protobuf::reflect::MessageDescriptor { - static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new(); - descriptor.get(|| file_descriptor().message_by_package_relative_name("EmulatorDllDetails").unwrap()).clone() - } -} - -impl ::std::fmt::Display for EmulatorDllDetails { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - ::protobuf::text_format::fmt(self, f) - } -} - -impl ::protobuf::reflect::ProtobufValue for EmulatorDllDetails { - type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>; -} - -#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)] -// @@protoc_insertion_point(enum:EmulatorProcessType) -pub enum EmulatorProcessType { - // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_UNKNOWN) - PROCESS_TYPE_UNKNOWN = 0, - // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_MAIN) - PROCESS_TYPE_MAIN = 1, - // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_BLOCK) - PROCESS_TYPE_BLOCK = 2, - // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_METRICS) - PROCESS_TYPE_METRICS = 3, - // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_NET) - PROCESS_TYPE_NET = 4, - // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_SLIRP) - PROCESS_TYPE_SLIRP = 5, - // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_GPU) - PROCESS_TYPE_GPU = 6, - // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_SOUND) - PROCESS_TYPE_SOUND = 7, - // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_BROKER) - PROCESS_TYPE_BROKER = 8, - // @@protoc_insertion_point(enum_value:EmulatorProcessType.PROCESS_TYPE_SPU) - PROCESS_TYPE_SPU = 9, -} - -impl ::protobuf::Enum for EmulatorProcessType { - const NAME: &'static str = "EmulatorProcessType"; - - fn value(&self) -> i32 { - *self as i32 - } - - fn from_i32(value: i32) -> ::std::option::Option<EmulatorProcessType> { - match value { - 0 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_UNKNOWN), - 1 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_MAIN), - 2 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_BLOCK), - 3 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_METRICS), - 4 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_NET), - 5 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_SLIRP), - 6 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_GPU), - 7 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_SOUND), - 8 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_BROKER), - 9 => ::std::option::Option::Some(EmulatorProcessType::PROCESS_TYPE_SPU), - _ => ::std::option::Option::None - } - } - - const VALUES: &'static [EmulatorProcessType] = &[ - EmulatorProcessType::PROCESS_TYPE_UNKNOWN, - EmulatorProcessType::PROCESS_TYPE_MAIN, - EmulatorProcessType::PROCESS_TYPE_BLOCK, - EmulatorProcessType::PROCESS_TYPE_METRICS, - EmulatorProcessType::PROCESS_TYPE_NET, - EmulatorProcessType::PROCESS_TYPE_SLIRP, - EmulatorProcessType::PROCESS_TYPE_GPU, - EmulatorProcessType::PROCESS_TYPE_SOUND, - EmulatorProcessType::PROCESS_TYPE_BROKER, - EmulatorProcessType::PROCESS_TYPE_SPU, - ]; -} - -impl ::protobuf::EnumFull for EmulatorProcessType { - fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor { - static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new(); - descriptor.get(|| file_descriptor().enum_by_package_relative_name("EmulatorProcessType").unwrap()).clone() - } - - fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor { - let index = *self as usize; - Self::enum_descriptor().value_by_index(index) - } -} - -impl ::std::default::Default for EmulatorProcessType { - fn default() -> Self { - EmulatorProcessType::PROCESS_TYPE_UNKNOWN - } -} - -impl EmulatorProcessType { - fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData { - ::protobuf::reflect::GeneratedEnumDescriptorData::new::<EmulatorProcessType>("EmulatorProcessType") - } -} - -static file_descriptor_proto_data: &'static [u8] = b"\ - \n\x13event_details.proto\"\x96\x02\n\rRecordDetails\x12n\n#emulator_chi\ - ld_process_exit_details\x18\x0c\x20\x01(\x0b2\x20.EmulatorChildProcessEx\ - itDetailsR\x1femulatorChildProcessExitDetails\x12B\n\x13wave_format_deta\ - ils\x18\r\x20\x01(\x0b2\x12.WaveFormatDetailsR\x11waveFormatDetails\x12E\ - \n\x14emulator_dll_details\x18\x13\x20\x01(\x0b2\x13.EmulatorDllDetailsR\ - \x12emulatorDllDetailsJ\x04\x08\x01\x10\x0cJ\x04\x08\x0e\x10\x13\"\x9d\ - \x01\n\x11WaveFormatDetails\x12)\n\trequested\x18\x01\x20\x01(\x0b2\x0b.\ - WaveFormatR\trequested\x12'\n\x08modified\x18\x02\x20\x01(\x0b2\x0b.Wave\ - FormatR\x08modified\x124\n\x0fclosest_matched\x18\x03\x20\x01(\x0b2\x0b.\ - WaveFormatR\x0eclosestMatched\"\xb9\x05\n\nWaveFormat\x12\x1d\n\nformat_\ - tag\x18\x01\x20\x01(\x05R\tformatTag\x12\x1a\n\x08channels\x18\x02\x20\ - \x01(\x05R\x08channels\x12&\n\x0fsamples_per_sec\x18\x03\x20\x01(\x05R\r\ - samplesPerSec\x12)\n\x11avg_bytes_per_sec\x18\x04\x20\x01(\x05R\x0eavgBy\ - tesPerSec\x12\x1f\n\x0bblock_align\x18\x05\x20\x01(\x05R\nblockAlign\x12\ - &\n\x0fbits_per_sample\x18\x06\x20\x01(\x05R\rbitsPerSample\x12\x1d\n\ns\ - ize_bytes\x18\x07\x20\x01(\x05R\tsizeBytes\x12\x18\n\x07samples\x18\x08\ - \x20\x01(\x05R\x07samples\x12!\n\x0cchannel_mask\x18\t\x20\x01(\x03R\x0b\ - channelMask\x12>\n\nsub_format\x18\n\x20\x01(\x0e2\x1f.WaveFormat.WaveFo\ - rmatSubFormatR\tsubFormat\"\xb7\x02\n\x13WaveFormatSubFormat\x12\x20\n\ - \x1cKSDATAFORMAT_SUBTYPE_INVALID\x10\0\x12\x1f\n\x1bKSDATAFORMAT_SUBTYPE\ - _ANALOG\x10\x01\x12\x1c\n\x18KSDATAFORMAT_SUBTYPE_PCM\x10\x02\x12#\n\x1f\ - KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\x10\x03\x12\x1c\n\x18KSDATAFORMAT_SUBTYP\ - E_DRM\x10\x04\x12\x1d\n\x19KSDATAFORMAT_SUBTYPE_ALAW\x10\x05\x12\x1e\n\ - \x1aKSDATAFORMAT_SUBTYPE_MULAW\x10\x06\x12\x1e\n\x1aKSDATAFORMAT_SUBTYPE\ - _ADPCM\x10\x07\x12\x1d\n\x19KSDATAFORMAT_SUBTYPE_MPEG\x10\x08\"w\n\x1fEm\ - ulatorChildProcessExitDetails\x12\x1b\n\texit_code\x18\x01\x20\x01(\rR\ - \x08exitCode\x127\n\x0cprocess_type\x18\x02\x20\x01(\x0e2\x14.EmulatorPr\ - ocessTypeR\x0bprocessType\"8\n\x12EmulatorDllDetails\x12\"\n\rdll_base_n\ - ame\x18\x01\x20\x01(\tR\x0bdllBaseName*\x83\x02\n\x13EmulatorProcessType\ - \x12\x18\n\x14PROCESS_TYPE_UNKNOWN\x10\0\x12\x15\n\x11PROCESS_TYPE_MAIN\ - \x10\x01\x12\x16\n\x12PROCESS_TYPE_BLOCK\x10\x02\x12\x18\n\x14PROCESS_TY\ - PE_METRICS\x10\x03\x12\x14\n\x10PROCESS_TYPE_NET\x10\x04\x12\x16\n\x12PR\ - OCESS_TYPE_SLIRP\x10\x05\x12\x14\n\x10PROCESS_TYPE_GPU\x10\x06\x12\x16\n\ - \x12PROCESS_TYPE_SOUND\x10\x07\x12\x17\n\x13PROCESS_TYPE_BROKER\x10\x08\ - \x12\x14\n\x10PROCESS_TYPE_SPU\x10\t\ -"; - -/// `FileDescriptorProto` object which was a source for this generated file -fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto { - static file_descriptor_proto_lazy: ::protobuf::rt::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::Lazy::new(); - file_descriptor_proto_lazy.get(|| { - ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap() - }) -} - -/// `FileDescriptor` object which allows dynamic access to files -pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor { - static generated_file_descriptor_lazy: ::protobuf::rt::Lazy<::protobuf::reflect::GeneratedFileDescriptor> = ::protobuf::rt::Lazy::new(); - static file_descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::FileDescriptor> = ::protobuf::rt::Lazy::new(); - file_descriptor.get(|| { - let generated_file_descriptor = generated_file_descriptor_lazy.get(|| { - let mut deps = ::std::vec::Vec::with_capacity(0); - let mut messages = ::std::vec::Vec::with_capacity(5); - messages.push(RecordDetails::generated_message_descriptor_data()); - messages.push(WaveFormatDetails::generated_message_descriptor_data()); - messages.push(WaveFormat::generated_message_descriptor_data()); - messages.push(EmulatorChildProcessExitDetails::generated_message_descriptor_data()); - messages.push(EmulatorDllDetails::generated_message_descriptor_data()); - let mut enums = ::std::vec::Vec::with_capacity(2); - enums.push(EmulatorProcessType::generated_enum_descriptor_data()); - enums.push(wave_format::WaveFormatSubFormat::generated_enum_descriptor_data()); - ::protobuf::reflect::GeneratedFileDescriptor::new_generated( - file_descriptor_proto(), - deps, - messages, - enums, - ) - }); - ::protobuf::reflect::FileDescriptor::new_generated_2(generated_file_descriptor) - }) -} diff --git a/vendor/generic/metrics/src/out/generated.rs b/vendor/generic/metrics/src/out/generated.rs deleted file mode 100644 index c7dc617d2..000000000 --- a/vendor/generic/metrics/src/out/generated.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[path = "./event_details.rs"] -pub mod event_details; diff --git a/vendor/generic/metrics/src/out/mod.rs b/vendor/generic/metrics/src/out/mod.rs deleted file mode 100644 index 2db7abd20..000000000 --- a/vendor/generic/metrics/src/out/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -// @generated - -pub mod event_details; diff --git a/vendor/generic/metrics/src/periodic_logger.rs b/vendor/generic/metrics/src/periodic_logger.rs index 409307856..b6469f648 100644 --- a/vendor/generic/metrics/src/periodic_logger.rs +++ b/vendor/generic/metrics/src/periodic_logger.rs @@ -5,7 +5,7 @@ use std::result::Result; use std::time::Duration; -use crate::MetricEventType; +use metrics_events::MetricEventType; /// A logging struct meant for use in tracking and periodically /// logging a single metric. The metric is aggregated over the diff --git a/vendor/generic/metrics/src/request_handler.rs b/vendor/generic/metrics/src/request_handler.rs index 7ed300d22..7520c700a 100644 --- a/vendor/generic/metrics/src/request_handler.rs +++ b/vendor/generic/metrics/src/request_handler.rs @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -use crate::metrics_requests::MetricsRequest; +use base::RecvTube; #[derive(Default)] pub struct MetricsRequestHandler; @@ -10,6 +10,8 @@ impl MetricsRequestHandler { pub fn new() -> Self { MetricsRequestHandler } - pub fn handle_request(&self, _req: MetricsRequest) {} + pub fn handle_tube_readable(&self, _tube: &RecvTube) { + unreachable!(); + } pub fn shutdown(&self) {} } diff --git a/vendor/generic/metrics/src/sys/windows.rs b/vendor/generic/metrics/src/sys/windows.rs deleted file mode 100644 index 0aedb5c51..000000000 --- a/vendor/generic/metrics/src/sys/windows.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2022 The ChromiumOS Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -use win_util::ProcessType; - -use crate::protos::event_details::EmulatorProcessType; - -impl From<ProcessType> for EmulatorProcessType { - fn from(process_type: ProcessType) -> Self { - match process_type { - ProcessType::Block => EmulatorProcessType::PROCESS_TYPE_BLOCK, - ProcessType::Main => EmulatorProcessType::PROCESS_TYPE_MAIN, - ProcessType::Metrics => EmulatorProcessType::PROCESS_TYPE_METRICS, - ProcessType::Net => EmulatorProcessType::PROCESS_TYPE_NET, - ProcessType::Slirp => EmulatorProcessType::PROCESS_TYPE_SLIRP, - ProcessType::Gpu => EmulatorProcessType::PROCESS_TYPE_GPU, - ProcessType::Snd => EmulatorProcessType::PROCESS_TYPE_SOUND, - ProcessType::Broker => EmulatorProcessType::PROCESS_TYPE_BROKER, - ProcessType::Spu => EmulatorProcessType::PROCESS_TYPE_SPU, - ProcessType::UnknownType => panic!("Unknown process type found"), - } - } -} diff --git a/vendor/generic/metrics_events/Android.bp b/vendor/generic/metrics_events/Android.bp new file mode 100644 index 000000000..90ea97aca --- /dev/null +++ b/vendor/generic/metrics_events/Android.bp @@ -0,0 +1,20 @@ +// This file is generated by cargo_embargo. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. +// Content before the first "rust_*" or "genrule" module is preserved. + +package { + default_applicable_licenses: ["external_crosvm_license"], +} + +rust_library { + name: "libmetrics_events_generic", + defaults: ["crosvm_inner_defaults"], + host_supported: true, + crate_name: "metrics_events_generic", + cargo_env_compat: true, + cargo_pkg_version: "0.1.0", + srcs: ["src/lib.rs"], + edition: "2021", + rustlibs: ["libserde"], +} diff --git a/vendor/generic/metrics_events/Cargo.toml b/vendor/generic/metrics_events/Cargo.toml new file mode 100644 index 000000000..19d3a5ba5 --- /dev/null +++ b/vendor/generic/metrics_events/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "metrics_events_generic" +version = "0.1.0" +authors = ["The ChromiumOS Authors"] +edition = "2021" + +[dependencies] +serde = { version = "1", features = ["derive"] } diff --git a/vendor/generic/metrics_events/src/lib.rs b/vendor/generic/metrics_events/src/lib.rs new file mode 100644 index 000000000..85a7075e5 --- /dev/null +++ b/vendor/generic/metrics_events/src/lib.rs @@ -0,0 +1,18 @@ +// Copyright 2024 The ChromiumOS Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +use serde::Deserialize; +use serde::Serialize; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum MetricEventType { + // No events should ever be added to this enum - all events defined in + // upstream CrosVM should be added to the metrics_event package. Downstream + // projects can replace the generic metrics_event package if they need + // downstream only events. +} + +pub struct RecordDetails { + // Similar to above, this is for downstream projects. +} diff --git a/vendor/generic/vm_control/Android.bp b/vendor/generic/vm_control/Android.bp index 56b56b96b..1371389cd 100644 --- a/vendor/generic/vm_control/Android.bp +++ b/vendor/generic/vm_control/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/vfio_sys/Android.bp b/vfio_sys/Android.bp index 2f2ddde0d..a8024daa0 100644 --- a/vfio_sys/Android.bp +++ b/vfio_sys/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/vhost/Android.bp b/vhost/Android.bp index 1522e3ec3..86146a2fe 100644 --- a/vhost/Android.bp +++ b/vhost/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/virtio_sys/Android.bp b/virtio_sys/Android.bp index 92f33a5e1..da9f5cecb 100644 --- a/virtio_sys/Android.bp +++ b/virtio_sys/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/vm_control/Android.bp b/vm_control/Android.bp index 032a4c921..ff9274ca5 100644 --- a/vm_control/Android.bp +++ b/vm_control/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/vm_memory/Android.bp b/vm_memory/Android.bp index 461e85a2f..5ab3db8e9 100644 --- a/vm_memory/Android.bp +++ b/vm_memory/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/win_audio/src/win_audio_impl/mod.rs b/win_audio/src/win_audio_impl/mod.rs index 7b8704773..1840e5160 100644 --- a/win_audio/src/win_audio_impl/mod.rs +++ b/win_audio/src/win_audio_impl/mod.rs @@ -1801,7 +1801,7 @@ mod tests { use std::thread; use cros_async::Executor; - use metrics::MetricEventType; + use metrics::sys::WaveFormatDetails; use winapi::shared::ksmedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; use winapi::shared::mmreg::WAVEFORMATEX; use winapi::shared::mmreg::WAVEFORMATEXTENSIBLE; @@ -2038,8 +2038,8 @@ mod tests { assert!(check_format( &audio_client, &format, - WaveFormatDetailsProto::new(), - MetricEventType::AudioFormatRequestOk, + WaveFormatDetails::default(), + AudioFormatEventType::RequestOk, ) .is_ok()); @@ -2067,8 +2067,8 @@ mod tests { assert!(check_format( &audio_client, &format, - WaveFormatDetailsProto::new(), - MetricEventType::AudioFormatRequestOk, + WaveFormatDetails::default(), + AudioFormatEventType::RequestOk, ) .is_ok()); @@ -2097,8 +2097,8 @@ mod tests { assert!(check_format( &audio_client, &format, - WaveFormatDetailsProto::new(), - MetricEventType::AudioFormatRequestOk, + WaveFormatDetails::default(), + AudioFormatEventType::RequestOk, ) .is_err()); } diff --git a/win_audio/src/win_audio_impl/wave_format.rs b/win_audio/src/win_audio_impl/wave_format.rs index 9fd0eaf68..0e6ed6948 100644 --- a/win_audio/src/win_audio_impl/wave_format.rs +++ b/win_audio/src/win_audio_impl/wave_format.rs @@ -11,10 +11,9 @@ use base::error; use base::info; use base::warn; use base::Error; -use metrics::protos::event_details::wave_format::WaveFormatSubFormat; -use metrics::protos::event_details::RecordDetails; -use metrics::protos::event_details::WaveFormat; -use metrics::protos::event_details::WaveFormatDetails; +use metrics::sys::WaveFormat as WaveFormatMetric; +use metrics::sys::WaveFormatDetails as WaveFormatDetailsMetric; +use metrics::sys::WaveFormatSubFormat as WaveFormatSubFormatMetric; use metrics::MetricEventType; use winapi::shared::guiddef::IsEqualGUID; use winapi::shared::guiddef::GUID; @@ -46,10 +45,6 @@ use crate::WinAudioError; use crate::MONO_CHANNEL_COUNT; use crate::STEREO_CHANNEL_COUNT; -pub type WaveFormatDetailsProto = WaveFormatDetails; -pub type WaveFormatProto = WaveFormat; -pub type SubFormatProto = WaveFormatSubFormat; - /// Wrapper around `WAVEFORMATEX` and `WAVEFORMATEXTENSIBLE` to hide some of the unsafe calls /// that could be made. pub enum WaveAudioFormat { @@ -59,6 +54,12 @@ pub enum WaveAudioFormat { WaveFormatExtensible(WAVEFORMATEXTENSIBLE), } +pub(crate) enum AudioFormatEventType { + RequestOk, + ModifiedOk, + Failed, +} + impl WaveAudioFormat { /// Wraps a WAVEFORMATEX pointer to make it's use more safe. /// @@ -389,59 +390,51 @@ impl PartialEq for WaveAudioFormat { } } -impl From<&WaveAudioFormat> for WaveFormatProto { - fn from(format: &WaveAudioFormat) -> WaveFormatProto { - let mut wave_format_proto = WaveFormatProto::new(); - +impl From<&WaveAudioFormat> for WaveFormatMetric { + fn from(format: &WaveAudioFormat) -> WaveFormatMetric { match format { - WaveAudioFormat::WaveFormat(wave_format) => { - wave_format_proto.set_format_tag(wave_format.wFormatTag.into()); - wave_format_proto.set_channels(wave_format.nChannels.into()); - wave_format_proto.set_samples_per_sec( - wave_format - .nSamplesPerSec - .try_into() - .expect("Failed to cast nSamplesPerSec to i32"), - ); - wave_format_proto.set_avg_bytes_per_sec( - wave_format - .nAvgBytesPerSec - .try_into() - .expect("Failed to cast nAvgBytesPerSec"), - ); - wave_format_proto.set_block_align(wave_format.nBlockAlign.into()); - wave_format_proto.set_bits_per_sample(wave_format.wBitsPerSample.into()); - wave_format_proto.set_size_bytes(wave_format.cbSize.into()); - } + WaveAudioFormat::WaveFormat(wave_format) => WaveFormatMetric { + format_tag: wave_format.wFormatTag.into(), + channels: wave_format.nChannels.into(), + samples_per_sec: wave_format + .nSamplesPerSec + .try_into() + .expect("Failed to cast nSamplesPerSec to i32"), + avg_bytes_per_sec: wave_format + .nAvgBytesPerSec + .try_into() + .expect("Failed to cast nAvgBytesPerSec"), + block_align: wave_format.nBlockAlign.into(), + bits_per_sample: wave_format.wBitsPerSample.into(), + size_bytes: wave_format.cbSize.into(), + samples: None, + channel_mask: None, + sub_format: None, + }, WaveAudioFormat::WaveFormatExtensible(wave_format_extensible) => { - wave_format_proto.set_format_tag(wave_format_extensible.Format.wFormatTag.into()); - wave_format_proto.set_channels(wave_format_extensible.Format.nChannels.into()); - wave_format_proto.set_samples_per_sec( - wave_format_extensible + let sub_format = wave_format_extensible.SubFormat; + WaveFormatMetric { + format_tag: wave_format_extensible.Format.wFormatTag.into(), + channels: wave_format_extensible.Format.nChannels.into(), + samples_per_sec: wave_format_extensible .Format .nSamplesPerSec .try_into() .expect("Failed to cast nSamplesPerSec to i32"), - ); - wave_format_proto.set_avg_bytes_per_sec( - wave_format_extensible + avg_bytes_per_sec: wave_format_extensible .Format .nAvgBytesPerSec .try_into() .expect("Failed to cast nAvgBytesPerSec"), - ); - wave_format_proto.set_block_align(wave_format_extensible.Format.nBlockAlign.into()); - wave_format_proto - .set_bits_per_sample(wave_format_extensible.Format.wBitsPerSample.into()); - wave_format_proto.set_size_bytes(wave_format_extensible.Format.cbSize.into()); - wave_format_proto.set_samples(wave_format_extensible.Samples.into()); - wave_format_proto.set_channel_mask(wave_format_extensible.dwChannelMask.into()); - let sub_format = wave_format_extensible.SubFormat; - wave_format_proto.set_sub_format(GuidWrapper(&sub_format).into()); + block_align: wave_format_extensible.Format.nBlockAlign.into(), + bits_per_sample: wave_format_extensible.Format.wBitsPerSample.into(), + size_bytes: wave_format_extensible.Format.cbSize.into(), + samples: Some(wave_format_extensible.Samples.into()), + channel_mask: Some(wave_format_extensible.dwChannelMask.into()), + sub_format: Some(GuidWrapper(&sub_format).into()), + } } } - - wave_format_proto } } @@ -465,18 +458,18 @@ pub(crate) fn get_valid_mix_format( WaveAudioFormat::new(format_ptr) }; - let mut wave_format_details = WaveFormatDetailsProto::new(); - let mut event_code = MetricEventType::AudioFormatRequestOk; - wave_format_details.requested = Some(WaveFormatProto::from(&format)).into(); + let mut wave_format_details = WaveFormatDetailsMetric::default(); + let mut event_code = AudioFormatEventType::RequestOk; + wave_format_details.requested = Some(WaveFormatMetric::from(&format)); info!("Printing mix format from `GetMixFormat`:\n{:?}", format); const BIT_DEPTH: usize = 32; format.modify_mix_format(BIT_DEPTH, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT); - let modified_wave_format = Some(WaveFormatProto::from(&format)).into(); + let modified_wave_format = Some(WaveFormatMetric::from(&format)); if modified_wave_format != wave_format_details.requested { wave_format_details.modified = modified_wave_format; - event_code = MetricEventType::AudioFormatModifiedOk; + event_code = AudioFormatEventType::ModifiedOk; } info!("Audio Engine Mix Format Used: \n{:?}", format); @@ -491,8 +484,8 @@ pub(crate) fn get_valid_mix_format( pub(crate) fn check_format( audio_client: &IAudioClient, format: &WaveAudioFormat, - mut wave_format_details: WaveFormatDetailsProto, - event_code: MetricEventType, + mut wave_format_details: WaveFormatDetailsMetric, + event_code: AudioFormatEventType, ) -> Result<(), WinAudioError> { let mut closest_match_format: *mut WAVEFORMATEX = std::ptr::null_mut(); // SAFETY: All values passed into `IsFormatSupport` is owned by us and we will @@ -511,8 +504,7 @@ pub(crate) fn check_format( // SAFETY: If the `hr` value is `S_FALSE`, then `IsFormatSupported` must've // given us a closest match. let closest_match_enum = unsafe { WaveAudioFormat::new(closest_match_format) }; - wave_format_details.closest_matched = - Some(WaveFormatProto::from(&closest_match_enum)).into(); + wave_format_details.closest_matched = Some(WaveFormatMetric::from(&closest_match_enum)); error!( "Current audio format not supported, the closest format is:\n{:?}", @@ -526,7 +518,7 @@ pub(crate) fn check_format( let last_error = Error::last(); // TODO:(b/253509368): Only upload for audio rendering, since these metrics can't // differentiate between rendering and capture. - upload_metrics(wave_format_details, MetricEventType::AudioFormatFailed); + upload_metrics(wave_format_details, AudioFormatEventType::Failed); Err(WinAudioError::WindowsError(hr, last_error)) } else { @@ -536,38 +528,38 @@ pub(crate) fn check_format( } } -fn upload_metrics( - wave_format_details: WaveFormatDetailsProto, - metrics_event_code: MetricEventType, -) { - let mut details = RecordDetails::new(); - details.wave_format_details = Some(wave_format_details).into(); - metrics::log_event_with_details(metrics_event_code, &details); +fn upload_metrics(details: WaveFormatDetailsMetric, event_type: AudioFormatEventType) { + let event = match event_type { + AudioFormatEventType::RequestOk => MetricEventType::AudioFormatRequestOk(details), + AudioFormatEventType::ModifiedOk => MetricEventType::AudioFormatModifiedOk(details), + AudioFormatEventType::Failed => MetricEventType::AudioFormatFailed(details), + }; + metrics::log_event(event); } struct GuidWrapper<'a>(&'a GUID); -impl<'a> From<GuidWrapper<'a>> for SubFormatProto { - fn from(guid: GuidWrapper) -> SubFormatProto { +impl<'a> From<GuidWrapper<'a>> for WaveFormatSubFormatMetric { + fn from(guid: GuidWrapper) -> WaveFormatSubFormatMetric { let guid = guid.0; if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_ANALOG) { - SubFormatProto::KSDATAFORMAT_SUBTYPE_ANALOG + WaveFormatSubFormatMetric::Analog } else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_PCM) { - SubFormatProto::KSDATAFORMAT_SUBTYPE_PCM + WaveFormatSubFormatMetric::Pcm } else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) { - SubFormatProto::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT + WaveFormatSubFormatMetric::IeeeFloat } else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_DRM) { - SubFormatProto::KSDATAFORMAT_SUBTYPE_DRM + WaveFormatSubFormatMetric::Drm } else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_ALAW) { - SubFormatProto::KSDATAFORMAT_SUBTYPE_ALAW + WaveFormatSubFormatMetric::ALaw } else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_MULAW) { - SubFormatProto::KSDATAFORMAT_SUBTYPE_MULAW + WaveFormatSubFormatMetric::MuLaw } else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_ADPCM) { - SubFormatProto::KSDATAFORMAT_SUBTYPE_ADPCM + WaveFormatSubFormatMetric::Adpcm } else if IsEqualGUID(guid, &KSDATAFORMAT_SUBTYPE_MPEG) { - SubFormatProto::KSDATAFORMAT_SUBTYPE_MPEG + WaveFormatSubFormatMetric::Mpeg } else { - SubFormatProto::KSDATAFORMAT_SUBTYPE_INVALID + WaveFormatSubFormatMetric::Invalid } } } @@ -1150,18 +1142,22 @@ mod tests { unsafe { WaveAudioFormat::new((&wave_format) as *const _ as *mut WAVEFORMATEX) }; // Testing the `into`. - let wave_format_proto = WaveFormatProto::from(&wave_audio_format); - - let mut expected = WaveFormatProto::new(); - expected.set_format_tag(WAVE_FORMAT_PCM.into()); - expected.set_channels(2); - expected.set_samples_per_sec(48000); - expected.set_avg_bytes_per_sec(192000); - expected.set_block_align(4); - expected.set_bits_per_sample(16); - expected.set_size_bytes(0); - - assert_eq!(wave_format_proto, expected); + let wave_format_metric = WaveFormatMetric::from(&wave_audio_format); + + let expected = WaveFormatMetric { + format_tag: WAVE_FORMAT_PCM.into(), + channels: 2, + samples_per_sec: 48000, + avg_bytes_per_sec: 192000, + block_align: 4, + bits_per_sample: 16, + size_bytes: 0, + samples: None, + channel_mask: None, + sub_format: None, + }; + + assert_eq!(wave_format_metric, expected); } #[test] @@ -1188,20 +1184,21 @@ mod tests { }; // Testing the `into`. - let wave_format_proto = WaveFormatProto::from(&wave_audio_format); - - let mut expected = WaveFormatProto::new(); - expected.set_format_tag(WAVE_FORMAT_EXTENSIBLE.into()); - expected.set_channels(2); - expected.set_samples_per_sec(48000); - expected.set_avg_bytes_per_sec(8 * 48000); - expected.set_block_align(8); - expected.set_bits_per_sample(32); - expected.set_size_bytes(22); - expected.set_samples(32); - expected.set_channel_mask((SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT) as i64); - expected.set_sub_format(GuidWrapper(&KSDATAFORMAT_SUBTYPE_IEEE_FLOAT).into()); - - assert_eq!(wave_format_proto, expected); + let wave_format_metric = WaveFormatMetric::from(&wave_audio_format); + + let expected = WaveFormatMetric { + format_tag: WAVE_FORMAT_EXTENSIBLE.into(), + channels: 2, + samples_per_sec: 48000, + avg_bytes_per_sec: 8 * 48000, + block_align: 8, + bits_per_sample: 32, + size_bytes: 22, + samples: Some(32), + channel_mask: Some((SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT) as i64), + sub_format: Some(WaveFormatSubFormatMetric::IeeeFloat), + }; + + assert_eq!(wave_format_metric, expected); } } diff --git a/x86_64/Android.bp b/x86_64/Android.bp index 266c6031e..4c882f24e 100644 --- a/x86_64/Android.bp +++ b/x86_64/Android.bp @@ -1,5 +1,6 @@ // This file is generated by cargo_embargo. -// Do not modify this file as most changes will be overridden on upgrade. +// Do not modify this file after the first "rust_*" or "genrule" module +// because the changes will be overridden on upgrade. // Content before the first "rust_*" or "genrule" module is preserved. package { diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs index c0356b42a..6202e2400 100644 --- a/x86_64/src/lib.rs +++ b/x86_64/src/lib.rs @@ -1004,8 +1004,8 @@ impl arch::LinuxArch for X8664arch { let pci_start = read_pci_mmio_before_32bit().start; let mut vcpu_init = vec![VcpuInitX86_64::default(); vcpu_count]; + let mut msrs = BTreeMap::new(); - let mut msrs; match components.vm_image { VmImage::Bios(ref mut bios) => { // Allow a bios to hardcode CMDLINE_OFFSET and read the kernel command line from it. @@ -1016,7 +1016,7 @@ impl arch::LinuxArch for X8664arch { ) .map_err(Error::LoadCmdline)?; Self::load_bios(&mem, bios)?; - msrs = regs::default_msrs(); + regs::set_default_msrs(&mut msrs); // The default values for `Regs` and `Sregs` already set up the reset vector. } VmImage::Kernel(ref mut kernel_image) => { @@ -1039,8 +1039,8 @@ impl arch::LinuxArch for X8664arch { vcpu_init[0].regs.rsp = BOOT_STACK_POINTER; vcpu_init[0].regs.rsi = ZERO_PAGE_OFFSET; - msrs = regs::long_mode_msrs(); - msrs.append(&mut regs::mtrr_msrs(&vm, pci_start)); + regs::set_long_mode_msrs(&mut msrs); + regs::set_mtrr_msrs(&mut msrs, &vm, pci_start); // Set up long mode and enable paging. regs::configure_segments_and_sregs(&mem, &mut vcpu_init[0].sregs) @@ -1111,23 +1111,25 @@ impl arch::LinuxArch for X8664arch { let vcpu_supported_var_mtrrs = regs::vcpu_supported_variable_mtrrs(vcpu); let num_var_mtrrs = regs::count_variable_mtrrs(&vcpu_init.msrs); - let msrs = if num_var_mtrrs > vcpu_supported_var_mtrrs { + let skip_mtrr_msrs = if num_var_mtrrs > vcpu_supported_var_mtrrs { warn!( "Too many variable MTRR entries ({} required, {} supported), please check pci_start addr, guest with pass through device may be very slow", num_var_mtrrs, vcpu_supported_var_mtrrs, ); // Filter out the MTRR entries from the MSR list. - vcpu_init - .msrs - .into_iter() - .filter(|&msr| !regs::is_mtrr_msr(msr.id)) - .collect() + true } else { - vcpu_init.msrs + false }; - vcpu.set_msrs(&msrs).map_err(Error::SetupMsrs)?; + for (msr_index, value) in vcpu_init.msrs.into_iter() { + if skip_mtrr_msrs && regs::is_mtrr_msr(msr_index) { + continue; + } + + vcpu.set_msr(msr_index, value).map_err(Error::SetupMsrs)?; + } interrupts::set_lint(vcpu_id, irq_chip).map_err(Error::SetLint)?; diff --git a/x86_64/src/regs.rs b/x86_64/src/regs.rs index 2dc48b88c..cbdd189ec 100644 --- a/x86_64/src/regs.rs +++ b/x86_64/src/regs.rs @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +use std::collections::BTreeMap; use std::mem; use std::result; use base::warn; -use hypervisor::Register; use hypervisor::Sregs; use hypervisor::VcpuX86_64; use hypervisor::Vm; @@ -98,15 +98,12 @@ fn get_mtrr_pairs(base: u64, len: u64) -> Vec<(u64, u64)> { /// Returns the number of variable MTRR entries supported by `vcpu`. pub fn vcpu_supported_variable_mtrrs(vcpu: &dyn VcpuX86_64) -> usize { // Get VAR MTRR num from MSR_MTRRcap - let mut msrs = vec![Register { - id: crate::msr_index::MSR_MTRRcap, - ..Default::default() - }]; - if vcpu.get_msrs(&mut msrs).is_err() { - warn!("get msrs fail, guest with pass through device may be very slow"); - 0 - } else { - (msrs[0].value & VAR_MTRR_NUM_MASK) as usize + match vcpu.get_msr(crate::msr_index::MSR_MTRRcap) { + Ok(value) => (value & VAR_MTRR_NUM_MASK) as usize, + Err(_e) => { + warn!("failed to get MSR_MTRRcap, guests with passthrough devices may be very slow"); + 0 + } } } @@ -119,105 +116,64 @@ pub fn is_mtrr_msr(id: u32) -> bool { } /// Returns the count of variable MTRR entries specified by the list of `msrs`. -pub fn count_variable_mtrrs(msrs: &[Register]) -> usize { +pub fn count_variable_mtrrs(msrs: &BTreeMap<u32, u64>) -> usize { // Each variable MTRR takes up two MSRs (base + mask), so divide by 2. This will also count the // MTRRdefType entry, but that is only one extra and the division truncates, so it won't affect // the final count. - msrs.iter().filter(|msr| is_mtrr_msr(msr.id)).count() / 2 + msrs.keys().filter(|&msr| is_mtrr_msr(*msr)).count() / 2 } /// Returns a set of MSRs containing the MTRR configuration. -pub fn mtrr_msrs(vm: &dyn Vm, pci_start: u64) -> Vec<Register> { +pub fn set_mtrr_msrs(msrs: &mut BTreeMap<u32, u64>, vm: &dyn Vm, pci_start: u64) { // Set pci_start .. 4G as UC // all others are set to default WB let pci_len = (1 << 32) - pci_start; let vecs = get_mtrr_pairs(pci_start, pci_len); - let mut entries = Vec::new(); - let phys_mask: u64 = (1 << vm.get_guest_phys_addr_bits()) - 1; for (idx, (base, len)) in vecs.iter().enumerate() { let reg_idx = idx as u32 * 2; - entries.push(Register { - id: MTRR_PHYS_BASE_MSR + reg_idx, - value: base | MTRR_MEMTYPE_UC as u64, - }); + msrs.insert(MTRR_PHYS_BASE_MSR + reg_idx, base | MTRR_MEMTYPE_UC as u64); let mask: u64 = len.wrapping_neg() & phys_mask | MTRR_VAR_VALID; - entries.push(Register { - id: MTRR_PHYS_MASK_MSR + reg_idx, - value: mask, - }); + msrs.insert(MTRR_PHYS_MASK_MSR + reg_idx, mask); } // Disable fixed MTRRs and enable variable MTRRs, set default type as WB - entries.push(Register { - id: crate::msr_index::MSR_MTRRdefType, - value: MTRR_ENABLE | MTRR_MEMTYPE_WB as u64, - }); - entries + msrs.insert( + crate::msr_index::MSR_MTRRdefType, + MTRR_ENABLE | MTRR_MEMTYPE_WB as u64, + ); } /// Returns the default value of MSRs at reset. /// /// Currently only sets IA32_TSC to 0. -pub fn default_msrs() -> Vec<Register> { - vec![ - Register { - id: crate::msr_index::MSR_IA32_TSC, - value: 0x0, - }, - Register { - id: crate::msr_index::MSR_IA32_MISC_ENABLE, - value: crate::msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64, - }, - ] +pub fn set_default_msrs(msrs: &mut BTreeMap<u32, u64>) { + msrs.insert(crate::msr_index::MSR_IA32_TSC, 0x0); + msrs.insert( + crate::msr_index::MSR_IA32_MISC_ENABLE, + crate::msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64, + ); } /// Configure Model specific registers for long (64-bit) mode. -pub fn long_mode_msrs() -> Vec<Register> { - vec![ - Register { - id: crate::msr_index::MSR_IA32_SYSENTER_CS, - value: 0x0, - }, - Register { - id: crate::msr_index::MSR_IA32_SYSENTER_ESP, - value: 0x0, - }, - Register { - id: crate::msr_index::MSR_IA32_SYSENTER_EIP, - value: 0x0, - }, - // x86_64 specific msrs, we only run on x86_64 not x86 - Register { - id: crate::msr_index::MSR_STAR, - value: 0x0, - }, - Register { - id: crate::msr_index::MSR_CSTAR, - value: 0x0, - }, - Register { - id: crate::msr_index::MSR_KERNEL_GS_BASE, - value: 0x0, - }, - Register { - id: crate::msr_index::MSR_SYSCALL_MASK, - value: 0x0, - }, - Register { - id: crate::msr_index::MSR_LSTAR, - value: 0x0, - }, - // end of x86_64 specific code - Register { - id: crate::msr_index::MSR_IA32_TSC, - value: 0x0, - }, - Register { - id: crate::msr_index::MSR_IA32_MISC_ENABLE, - value: crate::msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64, - }, - ] +pub fn set_long_mode_msrs(msrs: &mut BTreeMap<u32, u64>) { + msrs.insert(crate::msr_index::MSR_IA32_SYSENTER_CS, 0x0); + msrs.insert(crate::msr_index::MSR_IA32_SYSENTER_ESP, 0x0); + msrs.insert(crate::msr_index::MSR_IA32_SYSENTER_EIP, 0x0); + + // x86_64 specific msrs, we only run on x86_64 not x86 + msrs.insert(crate::msr_index::MSR_STAR, 0x0); + msrs.insert(crate::msr_index::MSR_CSTAR, 0x0); + msrs.insert(crate::msr_index::MSR_KERNEL_GS_BASE, 0x0); + msrs.insert(crate::msr_index::MSR_SYSCALL_MASK, 0x0); + msrs.insert(crate::msr_index::MSR_LSTAR, 0x0); + // end of x86_64 specific code + + msrs.insert(crate::msr_index::MSR_IA32_TSC, 0x0); + msrs.insert( + crate::msr_index::MSR_IA32_MISC_ENABLE, + crate::msr_index::MSR_IA32_MISC_ENABLE_FAST_STRING as u64, + ); } const X86_CR0_PE: u64 = 0x1; diff --git a/x86_64/tests/integration/main.rs b/x86_64/tests/integration/main.rs index 4948f5916..9c617b27f 100644 --- a/x86_64/tests/integration/main.rs +++ b/x86_64/tests/integration/main.rs @@ -45,8 +45,8 @@ use x86_64::mptable; use x86_64::read_pci_mmio_before_32bit; use x86_64::read_pcie_cfg_mmio; use x86_64::regs::configure_segments_and_sregs; -use x86_64::regs::long_mode_msrs; -use x86_64::regs::mtrr_msrs; +use x86_64::regs::set_long_mode_msrs; +use x86_64::regs::set_mtrr_msrs; use x86_64::regs::setup_page_tables; use x86_64::smbios; use x86_64::X8664arch; @@ -281,9 +281,12 @@ where setup_cpuid(&hyp, &irq_chip, &vcpu, 0, 1, cpu_config).unwrap(); } - let mut msrs = long_mode_msrs(); - msrs.append(&mut mtrr_msrs(&vm, read_pci_mmio_before_32bit().start)); - vcpu.set_msrs(&msrs).unwrap(); + let mut msrs = BTreeMap::new(); + set_long_mode_msrs(&mut msrs); + set_mtrr_msrs(&mut msrs, &vm, read_pci_mmio_before_32bit().start); + for (msr_index, value) in msrs { + vcpu.set_msr(msr_index, value).unwrap(); + } let mut vcpu_regs = Regs { rip: start_addr.offset(), |