From d52a1b96a8c3639307779efb2d18ebae44f735e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 04:59:11 +0000 Subject: [PATCH 001/189] build(deps): bump proc-macro2 from 1.0.51 to 1.0.55 Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.51 to 1.0.55. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.51...1.0.55) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f8e2ca..95e88eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -567,9 +567,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "1d0dd4be24fcdcfeaa12a432d588dc59bbad6cad3510c67e74a2b6b2fc950564" dependencies = [ "unicode-ident", ] From 5f46669000495ddb8db9ecd0813da508fde4d2aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 04:59:22 +0000 Subject: [PATCH 002/189] build(deps): bump regex-syntax from 0.6.28 to 0.6.29 Bumps [regex-syntax](https://github.com/rust-lang/regex) from 0.6.28 to 0.6.29. - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/commits) --- updated-dependencies: - dependency-name: regex-syntax dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 95e88eb..f84b9f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -635,9 +635,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "rustc-hash" From fbe982d3a3537ff2c6b8ba2aaa82c45426637488 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 05:23:24 +0000 Subject: [PATCH 003/189] build(deps): bump io-lifetimes from 1.0.8 to 1.0.9 Bumps [io-lifetimes](https://github.com/sunfishcode/io-lifetimes) from 1.0.8 to 1.0.9. - [Release notes](https://github.com/sunfishcode/io-lifetimes/releases) - [Commits](https://github.com/sunfishcode/io-lifetimes/compare/v1.0.8...v1.0.9) --- updated-dependencies: - dependency-name: io-lifetimes dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f84b9f8..4c5f550 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -355,9 +355,9 @@ checksum = "ee87fd093563344074bacf24faa0bb0227fb6969fb223e922db798516de924d6" [[package]] name = "io-lifetimes" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd6da19f25979c7270e70fa95ab371ec3b701cd0eefc47667a09785b3c59155" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" dependencies = [ "hermit-abi 0.3.1", "libc", From e51b410ec7017e1b566b53f106488602bb86a26a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Apr 2023 10:50:30 +0000 Subject: [PATCH 004/189] build(deps): bump rust-vmm-ci from `c2f8c93` to `3640704` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `c2f8c93` to `3640704`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/c2f8c93e3796d8b3ea7dc339fad211457be9c238...3640704b0251e58c0d0165464bf25fc3f038054e) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index c2f8c93..3640704 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit c2f8c93e3796d8b3ea7dc339fad211457be9c238 +Subproject commit 3640704b0251e58c0d0165464bf25fc3f038054e From 54598ac0133c9b9c325d3bddb477a63bd1aeffaa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Apr 2023 04:58:11 +0000 Subject: [PATCH 005/189] build(deps): bump clap from 4.1.8 to 4.1.14 Bumps [clap](https://github.com/clap-rs/clap) from 4.1.8 to 4.1.14. - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/v4.1.8...v4.1.14) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 90 +++++++++++++++++++++--------------------------------- 1 file changed, 35 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c5f550..2c99085 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,7 +41,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 1.0.109", "which", ] @@ -91,40 +91,45 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.8" +version = "4.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +checksum = "906f7fe1da4185b7a282b2bc90172a496f9def1aca4545fe7526810741591e14" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "351f9ad9688141ed83dfd8f5fb998a06225ef444b48ff4dc43de6d409b7fd10b" dependencies = [ "bitflags", - "clap_derive", "clap_lex", "is-terminal", - "once_cell", "strsim", "termcolor", ] [[package]] name = "clap_derive" -version = "4.1.8" +version = "4.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" +checksum = "81d7dc0031c3a59a04fc2ba395c8e2dd463cba1859275f065d225f6122221b45" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.13", ] [[package]] name = "clap_lex" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" -dependencies = [ - "os_str_bytes", -] +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "dashmap" @@ -255,7 +260,7 @@ checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -488,12 +493,6 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - [[package]] name = "parking_lot" version = "0.12.1" @@ -541,30 +540,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" version = "1.0.55" @@ -576,9 +551,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -687,7 +662,7 @@ checksum = "079a83df15f85d89a68d64ae1238f142f172b1fa915d0d76b26a7cba1b659a69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -728,6 +703,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tempfile" version = "3.4.0" @@ -767,7 +753,7 @@ checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -776,12 +762,6 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - [[package]] name = "vhost" version = "0.6.0" From a4fd23186bb5d969f51a478d5dc6256f7b12486e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Apr 2023 04:57:59 +0000 Subject: [PATCH 006/189] build(deps): bump rustix from 0.36.9 to 0.36.12 Bumps [rustix](https://github.com/bytecodealliance/rustix) from 0.36.9 to 0.36.12. - [Release notes](https://github.com/bytecodealliance/rustix/releases) - [Commits](https://github.com/bytecodealliance/rustix/compare/v0.36.9...v0.36.12) --- updated-dependencies: - dependency-name: rustix dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 115 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c99085..7462b63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -184,6 +184,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -404,7 +415,7 @@ name = "libgpiod" version = "0.1.0" source = "git+https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/?rev=d8d3a84b2ddf#d8d3a84b2ddfc29670430fc73ff8483a44b8f61e" dependencies = [ - "errno", + "errno 0.2.8", "intmap", "libc", "libgpiod-sys", @@ -622,12 +633,12 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.36.9" +version = "0.36.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +checksum = "e0af200a3324fa5bcd922e84e9b55a298ea9f431a489f01961acdebc6e908f25" dependencies = [ "bitflags", - "errno", + "errno 0.3.1", "io-lifetimes", "libc", "linux-raw-sys", @@ -975,13 +986,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.1", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.1", ] [[package]] @@ -990,7 +1001,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.0", ] [[package]] @@ -999,13 +1019,28 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.1", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -1014,38 +1049,80 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" From 00cf8b7d30ee1716040510c549ea0ea0d209614d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Apr 2023 04:57:49 +0000 Subject: [PATCH 007/189] build(deps): bump clang-sys from 1.6.0 to 1.6.1 Bumps [clang-sys](https://github.com/KyleMayes/clang-sys) from 1.6.0 to 1.6.1. - [Release notes](https://github.com/KyleMayes/clang-sys/releases) - [Changelog](https://github.com/KyleMayes/clang-sys/blob/master/CHANGELOG.md) - [Commits](https://github.com/KyleMayes/clang-sys/compare/v1.6.0...v1.6.1) --- updated-dependencies: - dependency-name: clang-sys dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7462b63..65878b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,9 +80,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clang-sys" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", From 0df982c1abf59962a3574eb393f54b22145bf4a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 04:57:44 +0000 Subject: [PATCH 008/189] build(deps): bump libc from 0.2.139 to 0.2.141 Bumps [libc](https://github.com/rust-lang/libc) from 0.2.139 to 0.2.141. - [Release notes](https://github.com/rust-lang/libc/releases) - [Commits](https://github.com/rust-lang/libc/compare/0.2.139...0.2.141) --- updated-dependencies: - dependency-name: libc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65878b9..c44dc70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -406,9 +406,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libgpiod" From ad25a160f5eaf2fecef730a6c3fe10adeb286cdf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 04:57:37 +0000 Subject: [PATCH 009/189] build(deps): bump proc-macro2 from 1.0.55 to 1.0.56 Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.55 to 1.0.56. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.55...1.0.56) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c44dc70..0fb2ff3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -553,9 +553,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.55" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0dd4be24fcdcfeaa12a432d588dc59bbad6cad3510c67e74a2b6b2fc950564" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] From b42cc0a2a65797c7ae5c154e1a019cc9d54c10c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 04:57:36 +0000 Subject: [PATCH 010/189] build(deps): bump rust-vmm-ci from `3640704` to `8627b37` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `3640704` to `8627b37`. - [Release notes](https://github.com/rust-vmm/rust-vmm-ci/releases) - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/3640704b0251e58c0d0165464bf25fc3f038054e...8627b3766b2bedde4657c7e9ddfc6f95a20e6942) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 3640704..8627b37 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 3640704b0251e58c0d0165464bf25fc3f038054e +Subproject commit 8627b3766b2bedde4657c7e9ddfc6f95a20e6942 From 62268560c86fb6759e76d5bf1fa8d9f6b6a13723 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Apr 2023 04:57:31 +0000 Subject: [PATCH 011/189] build(deps): bump futures-core from 0.3.26 to 0.3.28 Bumps [futures-core](https://github.com/rust-lang/futures-rs) from 0.3.26 to 0.3.28. - [Release notes](https://github.com/rust-lang/futures-rs/releases) - [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.26...0.3.28) --- updated-dependencies: - dependency-name: futures-core dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0fb2ff3..a593b78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,9 +241,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" From f52cc9cfff98ecce4b9718e0f860ca5902c31ba0 Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Tue, 11 Apr 2023 20:40:05 +0530 Subject: [PATCH 012/189] vsock: Implement VhostUserBackend for VhostUserVsockBackend Implement VhostUserBackend instead of VhostUserBackendMut for VhostUserVsockBackend. VhostUserBackendMut trait is supposed to be used for structures without interior mutability. But VhostUserVsockBackend already uses Mutex to protect its threads, so it can implement the trait with interior mutability (i.e. VhostUserBackend). Signed-off-by: Priyansh Rathi --- crates/vsock/src/main.rs | 19 +++++++------------ crates/vsock/src/vhu_vsock.rs | 14 +++++++------- crates/vsock/src/vhu_vsock_thread.rs | 4 ++-- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/crates/vsock/src/main.rs b/crates/vsock/src/main.rs index 3d36c53..0f192f9 100644 --- a/crates/vsock/src/main.rs +++ b/crates/vsock/src/main.rs @@ -8,10 +8,7 @@ mod vhu_vsock; mod vhu_vsock_thread; mod vsock_conn; -use std::{ - convert::TryFrom, - sync::{Arc, RwLock}, -}; +use std::{convert::TryFrom, sync::Arc}; use clap::Parser; use log::{info, warn}; @@ -52,9 +49,7 @@ impl TryFrom for VsockConfig { /// vhost-user-vsock backend server. pub(crate) fn start_backend_server(config: VsockConfig) { loop { - let backend = Arc::new(RwLock::new( - VhostUserVsockBackend::new(config.clone()).unwrap(), - )); + let backend = Arc::new(VhostUserVsockBackend::new(config.clone()).unwrap()); let listener = Listener::new(config.get_socket_path(), true).unwrap(); @@ -67,7 +62,7 @@ pub(crate) fn start_backend_server(config: VsockConfig) { let mut vring_workers = daemon.get_epoll_handlers(); - for thread in backend.read().unwrap().threads.iter() { + for thread in backend.threads.iter() { thread .lock() .unwrap() @@ -89,7 +84,7 @@ pub(crate) fn start_backend_server(config: VsockConfig) { } // No matter the result, we need to shut down the worker thread. - backend.read().unwrap().exit_event.write(1).unwrap(); + backend.exit_event.write(1).unwrap(); } } @@ -142,7 +137,7 @@ mod tests { VSOCK_SOCKET_PATH.to_string(), ); - let backend = Arc::new(RwLock::new(VhostUserVsockBackend::new(config).unwrap())); + let backend = Arc::new(VhostUserVsockBackend::new(config).unwrap()); let daemon = VhostUserDaemon::new( String::from("vhost-user-vsock"), @@ -154,8 +149,8 @@ mod tests { let vring_workers = daemon.get_epoll_handlers(); // VhostUserVsockBackend support a single thread that handles the TX and RX queues - assert_eq!(backend.read().unwrap().threads.len(), 1); + assert_eq!(backend.threads.len(), 1); - assert_eq!(vring_workers.len(), backend.read().unwrap().threads.len()); + assert_eq!(vring_workers.len(), backend.threads.len()); } } diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index 102b3d1..41eea0c 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -8,7 +8,7 @@ use std::{ use thiserror::Error as ThisError; use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures}; -use vhost_user_backend::{VhostUserBackendMut, VringRwLock}; +use vhost_user_backend::{VhostUserBackend, VringRwLock}; use virtio_bindings::bindings::{ virtio_config::VIRTIO_F_NOTIFY_ON_EMPTY, virtio_config::VIRTIO_F_VERSION_1, virtio_ring::VIRTIO_RING_F_EVENT_IDX, @@ -226,7 +226,7 @@ impl VhostUserVsockBackend { } } -impl VhostUserBackendMut for VhostUserVsockBackend { +impl VhostUserBackend for VhostUserVsockBackend { fn num_queues(&self) -> usize { NUM_QUEUES } @@ -246,13 +246,13 @@ impl VhostUserBackendMut for VhostUserVsockBackend { VhostUserProtocolFeatures::CONFIG } - fn set_event_idx(&mut self, enabled: bool) { + fn set_event_idx(&self, enabled: bool) { for thread in self.threads.iter() { thread.lock().unwrap().event_idx = enabled; } } - fn update_memory(&mut self, atomic_mem: GuestMemoryAtomic) -> IoResult<()> { + fn update_memory(&self, atomic_mem: GuestMemoryAtomic) -> IoResult<()> { for thread in self.threads.iter() { thread.lock().unwrap().mem = Some(atomic_mem.clone()); } @@ -260,7 +260,7 @@ impl VhostUserBackendMut for VhostUserVsockBackend { } fn handle_event( - &mut self, + &self, device_event: u16, evset: EventSet, vrings: &[VringRwLock], @@ -344,7 +344,7 @@ mod tests { let backend = VhostUserVsockBackend::new(config); assert!(backend.is_ok()); - let mut backend = backend.unwrap(); + let backend = backend.unwrap(); assert_eq!(backend.num_queues(), NUM_QUEUES); assert_eq!(backend.max_queue_size(), QUEUE_SIZE); @@ -422,7 +422,7 @@ mod tests { VSOCK_SOCKET_PATH.to_string(), ); - let mut backend = VhostUserVsockBackend::new(config).unwrap(); + let backend = VhostUserVsockBackend::new(config).unwrap(); let mem = GuestMemoryAtomic::new( GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(), ); diff --git a/crates/vsock/src/vhu_vsock_thread.rs b/crates/vsock/src/vhu_vsock_thread.rs index 93d898f..e9a95b4 100644 --- a/crates/vsock/src/vhu_vsock_thread.rs +++ b/crates/vsock/src/vhu_vsock_thread.rs @@ -10,7 +10,7 @@ use std::{ net::{UnixListener, UnixStream}, prelude::{AsRawFd, FromRawFd, RawFd}, }, - sync::{Arc, RwLock}, + sync::Arc, }; use futures::executor::{ThreadPool, ThreadPoolBuilder}; @@ -31,7 +31,7 @@ use crate::{ vsock_conn::*, }; -type ArcVhostBknd = Arc>; +type ArcVhostBknd = Arc; pub(crate) struct VhostUserVsockThread { /// Guest memory map. From 802a9821bc7c7cf3a2d0daebcb16abf4fdc5ba43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Apr 2023 04:57:46 +0000 Subject: [PATCH 013/189] build(deps): bump regex from 1.7.1 to 1.8.1 Bumps [regex](https://github.com/rust-lang/regex) from 1.7.1 to 1.8.1. - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/1.7.1...1.8.1) --- updated-dependencies: - dependency-name: regex dependency-type: indirect update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a593b78..fe61617 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] @@ -610,9 +610,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", @@ -621,9 +621,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "rustc-hash" From c12f3bf0614ca12df5562f145284b02fcee93322 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Apr 2023 04:58:29 +0000 Subject: [PATCH 014/189] build(deps): bump windows_x86_64_gnu from 0.42.1 to 0.42.2 Bumps [windows_x86_64_gnu](https://github.com/microsoft/windows-rs) from 0.42.1 to 0.42.2. - [Release notes](https://github.com/microsoft/windows-rs/releases) - [Commits](https://github.com/microsoft/windows-rs/commits) --- updated-dependencies: - dependency-name: windows_x86_64_gnu dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe61617..9aaaff1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -990,7 +990,7 @@ dependencies = [ "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.1", "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.1", ] @@ -1023,7 +1023,7 @@ dependencies = [ "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.1", "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.1", ] @@ -1093,9 +1093,9 @@ checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" From 46b258ccec9f2057300e3ba1905d2a2530c05d3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Apr 2023 04:58:03 +0000 Subject: [PATCH 015/189] build(deps): bump clap from 4.1.14 to 4.2.4 Bumps [clap](https://github.com/clap-rs/clap) from 4.1.14 to 4.2.4. - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/v4.1.14...v4.2.4) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 109 ++++++++++++++++++++++++++++++++++------ crates/gpio/Cargo.toml | 2 +- crates/i2c/Cargo.toml | 2 +- crates/rng/Cargo.toml | 2 +- crates/vsock/Cargo.toml | 2 +- 5 files changed, 99 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9aaaff1..860e5d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,55 @@ dependencies = [ "memchr", ] +[[package]] +name = "anstream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "arc-swap" version = "1.6.0" @@ -91,9 +140,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.14" +version = "4.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "906f7fe1da4185b7a282b2bc90172a496f9def1aca4545fe7526810741591e14" +checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62" dependencies = [ "clap_builder", "clap_derive", @@ -102,22 +151,22 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.1.14" +version = "4.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "351f9ad9688141ed83dfd8f5fb998a06225ef444b48ff4dc43de6d409b7fd10b" +checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" dependencies = [ + "anstream", + "anstyle", "bitflags", "clap_lex", - "is-terminal", "strsim", - "termcolor", ] [[package]] name = "clap_derive" -version = "4.1.14" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d7dc0031c3a59a04fc2ba395c8e2dd463cba1859275f065d225f6122221b45" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck", "proc-macro2", @@ -131,6 +180,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "dashmap" version = "5.4.0" @@ -382,14 +437,14 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix", - "windows-sys 0.45.0", + "rustix 0.37.7", + "windows-sys 0.48.0", ] [[package]] @@ -447,6 +502,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "linux-raw-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" + [[package]] name = "lock_api" version = "0.4.9" @@ -641,7 +702,21 @@ dependencies = [ "errno 0.3.1", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustix" +version = "0.37.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d" +dependencies = [ + "bitflags", + "errno 0.3.1", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.4", "windows-sys 0.45.0", ] @@ -734,7 +809,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall", - "rustix", + "rustix 0.36.12", "windows-sys 0.42.0", ] @@ -773,6 +848,12 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "vhost" version = "0.6.0" diff --git a/crates/gpio/Cargo.toml b/crates/gpio/Cargo.toml index 1841ab9..ab37b34 100644 --- a/crates/gpio/Cargo.toml +++ b/crates/gpio/Cargo.toml @@ -12,7 +12,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.1", features = ["derive"] } +clap = { version = "4.2", features = ["derive"] } env_logger = "0.10" libc = "0.2" log = "0.4" diff --git a/crates/i2c/Cargo.toml b/crates/i2c/Cargo.toml index 90be5c9..8c5c13b 100644 --- a/crates/i2c/Cargo.toml +++ b/crates/i2c/Cargo.toml @@ -12,7 +12,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.1", features = ["derive"] } +clap = { version = "4.2", features = ["derive"] } env_logger = "0.10" libc = "0.2" log = "0.4" diff --git a/crates/rng/Cargo.toml b/crates/rng/Cargo.toml index 00ff57c..88398f7 100644 --- a/crates/rng/Cargo.toml +++ b/crates/rng/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0 OR BSD-3-Clause" edition = "2021" [dependencies] -clap = { version = "4.1", features = ["derive"] } +clap = { version = "4.2", features = ["derive"] } env_logger = "0.10" epoll = "4.3" libc = "0.2" diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index ae69186..b814843 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -11,7 +11,7 @@ edition = "2018" [dependencies] byteorder = "1" -clap = { version = "4.1", features = ["derive"] } +clap = { version = "4.2", features = ["derive"] } env_logger = "0.10" epoll = "4.3.1" futures = { version = "0.3", features = ["thread-pool"] } From 69fff75c9ee74d4d020e24a00ea5a0c9eef7e712 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 04:59:13 +0000 Subject: [PATCH 016/189] build(deps): bump getrandom from 0.2.8 to 0.2.9 Bumps [getrandom](https://github.com/rust-random/getrandom) from 0.2.8 to 0.2.9. - [Release notes](https://github.com/rust-random/getrandom/releases) - [Changelog](https://github.com/rust-random/getrandom/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-random/getrandom/compare/v0.2.8...v0.2.9) --- updated-dependencies: - dependency-name: getrandom dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 860e5d2..bceebba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -361,9 +361,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", From 0f296e4ff362c832d5609be4ded22d2949421b00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 04:58:42 +0000 Subject: [PATCH 017/189] build(deps): bump futures-task from 0.3.27 to 0.3.28 Bumps [futures-task](https://github.com/rust-lang/futures-rs) from 0.3.27 to 0.3.28. - [Release notes](https://github.com/rust-lang/futures-rs/releases) - [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.27...0.3.28) --- updated-dependencies: - dependency-name: futures-task dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bceebba..01b4734 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -337,9 +337,9 @@ checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" From 482dd8c5215edeb764838a4f698023e39ff483f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 04:59:01 +0000 Subject: [PATCH 018/189] build(deps): bump rustix from 0.36.12 to 0.36.13 Bumps [rustix](https://github.com/bytecodealliance/rustix) from 0.36.12 to 0.36.13. - [Release notes](https://github.com/bytecodealliance/rustix/releases) - [Commits](https://github.com/bytecodealliance/rustix/compare/v0.36.12...v0.36.13) --- updated-dependencies: - dependency-name: rustix dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 01b4734..160b2ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -694,9 +694,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.36.12" +version = "0.36.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0af200a3324fa5bcd922e84e9b55a298ea9f431a489f01961acdebc6e908f25" +checksum = "3a38f9520be93aba504e8ca974197f46158de5dcaa9fa04b57c57cd6a679d658" dependencies = [ "bitflags", "errno 0.3.1", @@ -809,7 +809,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall", - "rustix 0.36.12", + "rustix 0.36.13", "windows-sys 0.42.0", ] From 8efaa64ca3010785ff608a7dcb729600cfc548ed Mon Sep 17 00:00:00 2001 From: uran0sH Date: Fri, 14 Apr 2023 04:19:04 -0400 Subject: [PATCH 019/189] vsock: make TX buffer size configurable That buffer is used to store bytes coming from the guest before sending them to the Unix domain socket. Some use cases might want to increase or decrease this space, so it would be best to make it user-customizable. Users can use "--tx-buffer-size=" to configure TX buffer. Fixes: #319 Signed-off-by: uran0sH --- crates/vsock/README.md | 5 ++ crates/vsock/src/main.rs | 19 +++++-- crates/vsock/src/thread_backend.rs | 9 +++- crates/vsock/src/txbuf.rs | 31 ++++++----- crates/vsock/src/vhu_vsock.rs | 18 +++++-- crates/vsock/src/vhu_vsock_thread.rs | 33 +++++++----- crates/vsock/src/vsock_conn.rs | 80 ++++++++++++++++++++++------ 7 files changed, 145 insertions(+), 50 deletions(-) diff --git a/crates/vsock/README.md b/crates/vsock/README.md index f3e8cd8..722ad24 100644 --- a/crates/vsock/README.md +++ b/crates/vsock/README.md @@ -43,6 +43,7 @@ Run the vhost-user-vsock device: vhost-user-vsock --guest-cid= \ --socket= --uds-path= + [--tx-buffer-size=host packets)>] ``` Run VMM (e.g. QEMU): @@ -61,6 +62,10 @@ qemu-system-x86_64 \ ```sh shell1$ vhost-user-vsock --guest-cid=4 --uds-path=/tmp/vm4.vsock --socket=/tmp/vhost4.socket ``` +or if you want to configure the TX buffer size +```sh +shell1$ vhost-user-vsock --guest-cid=4 --uds-path=/tmp/vm4.vsock --socket=/tmp/vhost4.socket --tx-buffer-size=65536 +``` ```sh shell2$ qemu-system-x86_64 \ diff --git a/crates/vsock/src/main.rs b/crates/vsock/src/main.rs index 0f192f9..41c7ef4 100644 --- a/crates/vsock/src/main.rs +++ b/crates/vsock/src/main.rs @@ -32,6 +32,10 @@ struct VsockArgs { /// Unix socket to which a host-side application connects to. #[clap(long)] uds_path: String, + + /// The size of the buffer used for the TX virtqueue + #[clap(long, default_value_t = 64 * 1024)] + tx_buffer_size: u32, } impl TryFrom for VsockConfig { @@ -41,7 +45,12 @@ impl TryFrom for VsockConfig { let socket = cmd_args.socket.trim().to_string(); let uds_path = cmd_args.uds_path.trim().to_string(); - Ok(VsockConfig::new(cmd_args.guest_cid, socket, uds_path)) + Ok(VsockConfig::new( + cmd_args.guest_cid, + socket, + uds_path, + cmd_args.tx_buffer_size, + )) } } @@ -101,11 +110,12 @@ mod tests { use serial_test::serial; impl VsockArgs { - fn from_args(guest_cid: u64, socket: &str, uds_path: &str) -> Self { + fn from_args(guest_cid: u64, socket: &str, uds_path: &str, tx_buffer_size: u32) -> Self { VsockArgs { guest_cid, socket: socket.to_string(), uds_path: uds_path.to_string(), + tx_buffer_size, } } } @@ -113,7 +123,7 @@ mod tests { #[test] #[serial] fn test_vsock_config_setup() { - let args = VsockArgs::from_args(3, "/tmp/vhost4.socket", "/tmp/vm4.vsock"); + let args = VsockArgs::from_args(3, "/tmp/vhost4.socket", "/tmp/vm4.vsock", 64 * 1024); let config = VsockConfig::try_from(args); assert!(config.is_ok()); @@ -122,6 +132,7 @@ mod tests { assert_eq!(config.get_guest_cid(), 3); assert_eq!(config.get_socket_path(), "/tmp/vhost4.socket"); assert_eq!(config.get_uds_path(), "/tmp/vm4.vsock"); + assert_eq!(config.get_tx_buffer_size(), 64 * 1024); } #[test] @@ -130,11 +141,13 @@ mod tests { const CID: u64 = 3; const VHOST_SOCKET_PATH: &str = "test_vsock_server.socket"; const VSOCK_SOCKET_PATH: &str = "test_vsock_server.vsock"; + const CONN_TX_BUF_SIZE: u32 = 64 * 1024; let config = VsockConfig::new( CID, VHOST_SOCKET_PATH.to_string(), VSOCK_SOCKET_PATH.to_string(), + CONN_TX_BUF_SIZE, ); let backend = Arc::new(VhostUserVsockBackend::new(config).unwrap()); diff --git a/crates/vsock/src/thread_backend.rs b/crates/vsock/src/thread_backend.rs index f32df28..d101047 100644 --- a/crates/vsock/src/thread_backend.rs +++ b/crates/vsock/src/thread_backend.rs @@ -37,11 +37,12 @@ pub(crate) struct VsockThreadBackend { epoll_fd: i32, /// Set of allocated local ports. pub local_port_set: HashSet, + tx_buffer_size: u32, } impl VsockThreadBackend { /// New instance of VsockThreadBackend. - pub fn new(host_socket_path: String, epoll_fd: i32) -> Self { + pub fn new(host_socket_path: String, epoll_fd: i32, tx_buffer_size: u32) -> Self { Self { listener_map: HashMap::new(), conn_map: HashMap::new(), @@ -52,6 +53,7 @@ impl VsockThreadBackend { host_socket_path, epoll_fd, local_port_set: HashSet::new(), + tx_buffer_size, } } @@ -216,6 +218,7 @@ impl VsockThreadBackend { pkt.src_port(), self.epoll_fd, pkt.buf_alloc(), + self.tx_buffer_size, ); self.conn_map @@ -254,6 +257,7 @@ mod tests { use virtio_vsock::packet::{VsockPacket, PKT_HEADER_SIZE}; const DATA_LEN: usize = 16; + const CONN_TX_BUF_SIZE: u32 = 64 * 1024; #[test] #[serial] @@ -266,7 +270,8 @@ mod tests { let _listener = UnixListener::bind(VSOCK_PEER_PATH).unwrap(); let epoll_fd = epoll::create(false).unwrap(); - let mut vtp = VsockThreadBackend::new(VSOCK_SOCKET_PATH.to_string(), epoll_fd); + let mut vtp = + VsockThreadBackend::new(VSOCK_SOCKET_PATH.to_string(), epoll_fd, CONN_TX_BUF_SIZE); assert!(!vtp.pending_rx()); diff --git a/crates/vsock/src/txbuf.rs b/crates/vsock/src/txbuf.rs index ff55cd8..ef718d7 100644 --- a/crates/vsock/src/txbuf.rs +++ b/crates/vsock/src/txbuf.rs @@ -4,7 +4,7 @@ use std::{io::Write, num::Wrapping}; use vm_memory::{bitmap::BitmapSlice, VolatileSlice}; -use crate::vhu_vsock::{Error, Result, CONN_TX_BUF_SIZE}; +use crate::vhu_vsock::{Error, Result}; #[derive(Debug)] pub(crate) struct LocalTxBuf { @@ -18,14 +18,19 @@ pub(crate) struct LocalTxBuf { impl LocalTxBuf { /// Create a new instance of LocalTxBuf. - pub fn new() -> Self { + pub fn new(buf_size: u32) -> Self { Self { - buf: vec![0; CONN_TX_BUF_SIZE as usize], + buf: vec![0; buf_size as usize], head: Wrapping(0), tail: Wrapping(0), } } + /// Get the buffer size + pub fn get_buf_size(&self) -> u32 { + self.buf.len() as u32 + } + /// Check if the buf is empty. pub fn is_empty(&self) -> bool { self.len() == 0 @@ -34,16 +39,16 @@ impl LocalTxBuf { /// Add new data to the tx buffer, push all or none. /// Returns LocalTxBufFull error if space not sufficient. pub fn push(&mut self, data_buf: &VolatileSlice) -> Result<()> { - if CONN_TX_BUF_SIZE as usize - self.len() < data_buf.len() { + if self.get_buf_size() as usize - self.len() < data_buf.len() { // Tx buffer is full return Err(Error::LocalTxBufFull); } // Get index into buffer at which data can be inserted - let tail_idx = self.tail.0 as usize % CONN_TX_BUF_SIZE as usize; + let tail_idx = self.tail.0 as usize % self.get_buf_size() as usize; // Check if we can fit the data buffer between head and end of buffer - let len = std::cmp::min(CONN_TX_BUF_SIZE as usize - tail_idx, data_buf.len()); + let len = std::cmp::min(self.get_buf_size() as usize - tail_idx, data_buf.len()); let txbuf = &mut self.buf[tail_idx..tail_idx + len]; data_buf.copy_to(txbuf); @@ -67,10 +72,10 @@ impl LocalTxBuf { } // Get index into buffer from which data can be read - let head_idx = self.head.0 as usize % CONN_TX_BUF_SIZE as usize; + let head_idx = self.head.0 as usize % self.get_buf_size() as usize; // First write from head to end of buffer - let len = std::cmp::min(CONN_TX_BUF_SIZE as usize - head_idx, self.len()); + let len = std::cmp::min(self.get_buf_size() as usize - head_idx, self.len()); let written = stream .write(&self.buf[head_idx..(head_idx + len)]) .map_err(Error::LocalTxBufFlush)?; @@ -97,9 +102,11 @@ impl LocalTxBuf { mod tests { use super::*; + const CONN_TX_BUF_SIZE: u32 = 64 * 1024; + #[test] fn test_txbuf_len() { - let mut loc_tx_buf = LocalTxBuf::new(); + let mut loc_tx_buf = LocalTxBuf::new(CONN_TX_BUF_SIZE); // Zero length tx buf assert_eq!(loc_tx_buf.len(), 0); @@ -118,7 +125,7 @@ mod tests { #[test] fn test_txbuf_is_empty() { - let mut loc_tx_buf = LocalTxBuf::new(); + let mut loc_tx_buf = LocalTxBuf::new(CONN_TX_BUF_SIZE); // empty tx buffer assert!(loc_tx_buf.is_empty()); @@ -130,7 +137,7 @@ mod tests { #[test] fn test_txbuf_push() { - let mut loc_tx_buf = LocalTxBuf::new(); + let mut loc_tx_buf = LocalTxBuf::new(CONN_TX_BUF_SIZE); let mut buf = [0; CONN_TX_BUF_SIZE as usize]; // SAFETY: Safe as the buffer is guaranteed to be valid here. let data = unsafe { VolatileSlice::new(buf.as_mut_ptr(), buf.len()) }; @@ -168,7 +175,7 @@ mod tests { #[test] fn test_txbuf_flush_to() { - let mut loc_tx_buf = LocalTxBuf::new(); + let mut loc_tx_buf = LocalTxBuf::new(CONN_TX_BUF_SIZE); // data to be flushed let mut buf = vec![1; CONN_TX_BUF_SIZE as usize]; diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index 41eea0c..72033f8 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -34,10 +34,6 @@ const EVT_QUEUE_EVENT: u16 = 2; /// Notification coming from the backend. pub(crate) const BACKEND_EVENT: u16 = 3; -/// Vsock connection TX buffer capacity -/// TODO: Make this value configurable -pub(crate) const CONN_TX_BUF_SIZE: u32 = 64 * 1024; - /// CID of the host pub(crate) const VSOCK_HOST_CID: u64 = 2; @@ -141,16 +137,18 @@ pub(crate) struct VsockConfig { guest_cid: u64, socket: String, uds_path: String, + tx_buffer_size: u32, } impl VsockConfig { /// Create a new instance of the VsockConfig struct, containing the /// parameters to be fed into the vsock-backend server. - pub fn new(guest_cid: u64, socket: String, uds_path: String) -> Self { + pub fn new(guest_cid: u64, socket: String, uds_path: String, tx_buffer_size: u32) -> Self { Self { guest_cid, socket, uds_path, + tx_buffer_size, } } @@ -170,6 +168,10 @@ impl VsockConfig { pub fn get_socket_path(&self) -> String { String::from(&self.socket) } + + pub fn get_tx_buffer_size(&self) -> u32 { + self.tx_buffer_size + } } /// A local port and peer port pair used to retrieve @@ -212,6 +214,7 @@ impl VhostUserVsockBackend { let thread = Mutex::new(VhostUserVsockThread::new( config.get_uds_path(), config.get_guest_cid(), + config.get_tx_buffer_size(), )?); let queues_per_thread = vec![QUEUE_MASK]; @@ -328,6 +331,8 @@ mod tests { use vhost_user_backend::VringT; use vm_memory::GuestAddress; + const CONN_TX_BUF_SIZE: u32 = 64 * 1024; + #[test] #[serial] fn test_vsock_backend() { @@ -339,6 +344,7 @@ mod tests { CID, VHOST_SOCKET_PATH.to_string(), VSOCK_SOCKET_PATH.to_string(), + CONN_TX_BUF_SIZE, ); let backend = VhostUserVsockBackend::new(config); @@ -411,6 +417,7 @@ mod tests { CID, "/sys/not_allowed.socket".to_string(), "/sys/not_allowed.vsock".to_string(), + CONN_TX_BUF_SIZE, ); let backend = VhostUserVsockBackend::new(config); @@ -420,6 +427,7 @@ mod tests { CID, VHOST_SOCKET_PATH.to_string(), VSOCK_SOCKET_PATH.to_string(), + CONN_TX_BUF_SIZE, ); let backend = VhostUserVsockBackend::new(config).unwrap(); diff --git a/crates/vsock/src/vhu_vsock_thread.rs b/crates/vsock/src/vhu_vsock_thread.rs index e9a95b4..ab61630 100644 --- a/crates/vsock/src/vhu_vsock_thread.rs +++ b/crates/vsock/src/vhu_vsock_thread.rs @@ -24,10 +24,7 @@ use vmm_sys_util::epoll::EventSet; use crate::{ rxops::*, thread_backend::*, - vhu_vsock::{ - ConnMapKey, Error, Result, VhostUserVsockBackend, BACKEND_EVENT, CONN_TX_BUF_SIZE, - VSOCK_HOST_CID, - }, + vhu_vsock::{ConnMapKey, Error, Result, VhostUserVsockBackend, BACKEND_EVENT, VSOCK_HOST_CID}, vsock_conn::*, }; @@ -56,11 +53,13 @@ pub(crate) struct VhostUserVsockThread { pool: ThreadPool, /// host side port on which application listens. local_port: Wrapping, + /// The tx buffer size + tx_buffer_size: u32, } impl VhostUserVsockThread { /// Create a new instance of VhostUserVsockThread. - pub fn new(uds_path: String, guest_cid: u64) -> Result { + pub fn new(uds_path: String, guest_cid: u64, tx_buffer_size: u32) -> Result { // TODO: better error handling, maybe add a param to force the unlink let _ = std::fs::remove_file(uds_path.clone()); let host_sock = UnixListener::bind(&uds_path) @@ -81,13 +80,14 @@ impl VhostUserVsockThread { host_listener: host_sock, vring_worker: None, epoll_file, - thread_backend: VsockThreadBackend::new(uds_path, epoll_fd), + thread_backend: VsockThreadBackend::new(uds_path, epoll_fd, tx_buffer_size), guest_cid, pool: ThreadPoolBuilder::new() .pool_size(1) .create() .map_err(Error::CreateThreadPool)?, local_port: Wrapping(0), + tx_buffer_size, }; VhostUserVsockThread::epoll_register(epoll_fd, host_raw_fd, epoll::Events::EPOLLIN)?; @@ -246,6 +246,7 @@ impl VhostUserVsockThread { self.guest_cid, peer_port, self.get_epoll_fd(), + self.tx_buffer_size, ); new_conn.rx_queue.enqueue(RxOps::Request); new_conn.set_peer_port(peer_port); @@ -404,7 +405,7 @@ impl VhostUserVsockThread { let used_len = match VsockPacket::from_rx_virtq_chain( mem.deref(), &mut avail_desc, - CONN_TX_BUF_SIZE, + self.tx_buffer_size, ) { Ok(mut pkt) => { if self.thread_backend.recv_pkt(&mut pkt).is_ok() { @@ -502,7 +503,7 @@ impl VhostUserVsockThread { let pkt = match VsockPacket::from_tx_virtq_chain( mem.deref(), &mut avail_desc, - CONN_TX_BUF_SIZE, + self.tx_buffer_size, ) { Ok(pkt) => pkt, Err(e) => { @@ -588,6 +589,8 @@ mod tests { use vm_memory::GuestAddress; use vmm_sys_util::eventfd::EventFd; + const CONN_TX_BUF_SIZE: u32 = 64 * 1024; + impl VhostUserVsockThread { fn get_epoll_file(&self) -> &File { &self.epoll_file @@ -597,7 +600,8 @@ mod tests { #[test] #[serial] fn test_vsock_thread() { - let t = VhostUserVsockThread::new("test_vsock_thread.vsock".to_string(), 3); + let t = + VhostUserVsockThread::new("test_vsock_thread.vsock".to_string(), 3, CONN_TX_BUF_SIZE); assert!(t.is_ok()); let mut t = t.unwrap(); @@ -652,11 +656,16 @@ mod tests { #[test] #[serial] fn test_vsock_thread_failures() { - let t = VhostUserVsockThread::new("/sys/not_allowed.vsock".to_string(), 3); + let t = + VhostUserVsockThread::new("/sys/not_allowed.vsock".to_string(), 3, CONN_TX_BUF_SIZE); assert!(t.is_err()); - let mut t = - VhostUserVsockThread::new("test_vsock_thread_failures.vsock".to_string(), 3).unwrap(); + let mut t = VhostUserVsockThread::new( + "test_vsock_thread_failures.vsock".to_string(), + 3, + CONN_TX_BUF_SIZE, + ) + .unwrap(); assert!(VhostUserVsockThread::epoll_register(-1, -1, epoll::Events::EPOLLIN).is_err()); assert!(VhostUserVsockThread::epoll_modify(-1, -1, epoll::Events::EPOLLIN).is_err()); assert!(VhostUserVsockThread::epoll_unregister(-1, -1).is_err()); diff --git a/crates/vsock/src/vsock_conn.rs b/crates/vsock/src/vsock_conn.rs index 8436c95..25e3335 100644 --- a/crates/vsock/src/vsock_conn.rs +++ b/crates/vsock/src/vsock_conn.rs @@ -15,7 +15,7 @@ use crate::{ rxqueue::*, txbuf::*, vhu_vsock::{ - Error, Result, CONN_TX_BUF_SIZE, VSOCK_FLAGS_SHUTDOWN_RCV, VSOCK_FLAGS_SHUTDOWN_SEND, + Error, Result, VSOCK_FLAGS_SHUTDOWN_RCV, VSOCK_FLAGS_SHUTDOWN_SEND, VSOCK_OP_CREDIT_REQUEST, VSOCK_OP_CREDIT_UPDATE, VSOCK_OP_REQUEST, VSOCK_OP_RESPONSE, VSOCK_OP_RST, VSOCK_OP_RW, VSOCK_OP_SHUTDOWN, VSOCK_TYPE_STREAM, }, @@ -64,6 +64,7 @@ impl VsockConnection { guest_cid: u64, guest_port: u32, epoll_fd: RawFd, + tx_buffer_size: u32, ) -> Self { Self { stream, @@ -79,12 +80,13 @@ impl VsockConnection { peer_fwd_cnt: Wrapping(0), rx_cnt: Wrapping(0), epoll_fd, - tx_buf: LocalTxBuf::new(), + tx_buf: LocalTxBuf::new(tx_buffer_size), } } /// Create a new vsock connection object for connections initiated by /// an application running in the guest. + #[allow(clippy::too_many_arguments)] pub fn new_peer_init( stream: S, local_cid: u64, @@ -93,6 +95,7 @@ impl VsockConnection { guest_port: u32, epoll_fd: RawFd, peer_buf_alloc: u32, + tx_buffer_size: u32, ) -> Self { let mut rx_queue = RxQueue::new(); rx_queue.enqueue(RxOps::Response); @@ -110,7 +113,7 @@ impl VsockConnection { peer_fwd_cnt: Wrapping(0), rx_cnt: Wrapping(0), epoll_fd, - tx_buf: LocalTxBuf::new(), + tx_buf: LocalTxBuf::new(tx_buffer_size), } } @@ -329,7 +332,7 @@ impl VsockConnection { .set_src_port(self.local_port) .set_dst_port(self.peer_port) .set_type(VSOCK_TYPE_STREAM) - .set_buf_alloc(CONN_TX_BUF_SIZE) + .set_buf_alloc(self.tx_buf.get_buf_size()) .set_fwd_cnt(self.fwd_cnt.0) } @@ -362,6 +365,8 @@ mod tests { GuestMemoryMmap, }; + const CONN_TX_BUF_SIZE: u32 = 64 * 1024; + struct HeadParams { head_len: usize, data_len: u32, @@ -490,8 +495,15 @@ mod tests { fn test_vsock_conn_init() { // new locally inititated connection let dummy_file = VsockDummySocket::new(); - let mut conn_local = - VsockConnection::new_local_init(dummy_file, VSOCK_HOST_CID, 5000, 3, 5001, -1); + let mut conn_local = VsockConnection::new_local_init( + dummy_file, + VSOCK_HOST_CID, + 5000, + 3, + 5001, + -1, + CONN_TX_BUF_SIZE, + ); assert!(!conn_local.connect); assert_eq!(conn_local.peer_port, 5001); @@ -506,8 +518,16 @@ mod tests { // New connection initiated by the peer/guest let dummy_file = VsockDummySocket::new(); - let mut conn_peer = - VsockConnection::new_peer_init(dummy_file, VSOCK_HOST_CID, 5000, 3, 5001, -1, 65536); + let mut conn_peer = VsockConnection::new_peer_init( + dummy_file, + VSOCK_HOST_CID, + 5000, + 3, + 5001, + -1, + 65536, + CONN_TX_BUF_SIZE, + ); assert!(!conn_peer.connect); assert_eq!(conn_peer.peer_port, 5001); @@ -524,8 +544,15 @@ mod tests { fn test_vsock_conn_credit() { // new locally inititated connection let dummy_file = VsockDummySocket::new(); - let mut conn_local = - VsockConnection::new_local_init(dummy_file, VSOCK_HOST_CID, 5000, 3, 5001, -1); + let mut conn_local = VsockConnection::new_local_init( + dummy_file, + VSOCK_HOST_CID, + 5000, + 3, + 5001, + -1, + CONN_TX_BUF_SIZE, + ); assert_eq!(conn_local.peer_avail_credit(), 0); assert!(conn_local.need_credit_update_from_peer()); @@ -551,8 +578,15 @@ mod tests { // new locally inititated connection let dummy_file = VsockDummySocket::new(); - let conn_local = - VsockConnection::new_local_init(dummy_file, VSOCK_HOST_CID, 5000, 3, 5001, -1); + let conn_local = VsockConnection::new_local_init( + dummy_file, + VSOCK_HOST_CID, + 5000, + 3, + 5001, + -1, + CONN_TX_BUF_SIZE, + ); // write only descriptor chain let (mem, mut descr_chain) = prepare_desc_chain_vsock(true, &head_params, 2, 10); @@ -581,8 +615,15 @@ mod tests { // new locally inititated connection let dummy_file = VsockDummySocket::new(); - let mut conn_local = - VsockConnection::new_local_init(dummy_file, VSOCK_HOST_CID, 5000, 3, 5001, -1); + let mut conn_local = VsockConnection::new_local_init( + dummy_file, + VSOCK_HOST_CID, + 5000, + 3, + 5001, + -1, + CONN_TX_BUF_SIZE, + ); // write only descriptor chain let (mem, mut descr_chain) = prepare_desc_chain_vsock(true, &head_params, 1, 5); @@ -671,8 +712,15 @@ mod tests { // new locally inititated connection let dummy_file = VsockDummySocket::new(); - let mut conn_local = - VsockConnection::new_local_init(dummy_file, VSOCK_HOST_CID, 5000, 3, 5001, -1); + let mut conn_local = VsockConnection::new_local_init( + dummy_file, + VSOCK_HOST_CID, + 5000, + 3, + 5001, + -1, + CONN_TX_BUF_SIZE, + ); // write only descriptor chain let (mem, mut descr_chain) = prepare_desc_chain_vsock(false, &head_params, 1, 5); From 14166a35bedc7291fbb5f57d41e817c290969b95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 04:57:59 +0000 Subject: [PATCH 020/189] build(deps): bump tempfile from 3.4.0 to 3.5.0 Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.4.0 to 3.5.0. - [Changelog](https://github.com/Stebalien/tempfile/blob/master/NEWS) - [Commits](https://github.com/Stebalien/tempfile/commits) --- updated-dependencies: - dependency-name: tempfile dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 60 ++++++++++++------------------------------- crates/rng/Cargo.toml | 2 +- 2 files changed, 18 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 160b2ba..4807c61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -443,7 +443,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix 0.37.7", + "rustix", "windows-sys 0.48.0", ] @@ -496,12 +496,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - [[package]] name = "linux-raw-sys" version = "0.3.4" @@ -583,7 +577,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] @@ -669,6 +663,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.8.1" @@ -692,20 +695,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustix" -version = "0.36.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a38f9520be93aba504e8ca974197f46158de5dcaa9fa04b57c57cd6a679d658" -dependencies = [ - "bitflags", - "errno 0.3.1", - "io-lifetimes", - "libc", - "linux-raw-sys 0.1.4", - "windows-sys 0.45.0", -] - [[package]] name = "rustix" version = "0.37.7" @@ -716,7 +705,7 @@ dependencies = [ "errno 0.3.1", "io-lifetimes", "libc", - "linux-raw-sys 0.3.4", + "linux-raw-sys", "windows-sys 0.45.0", ] @@ -802,15 +791,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", - "rustix 0.36.13", - "windows-sys 0.42.0", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", ] [[package]] @@ -1061,21 +1050,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.1", -] - [[package]] name = "windows-sys" version = "0.45.0" diff --git a/crates/rng/Cargo.toml b/crates/rng/Cargo.toml index 88398f7..ce84dda 100644 --- a/crates/rng/Cargo.toml +++ b/crates/rng/Cargo.toml @@ -16,7 +16,7 @@ epoll = "4.3" libc = "0.2" log = "0.4" rand = "0.8.5" -tempfile = "3.4" +tempfile = "3.5" thiserror = "1.0" vhost = { version = "0.6", features = ["vhost-user-slave"] } vhost-user-backend = "0.8" From ef238f1bfe83d68cd48f37cff6f9707749f64fd6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 04:58:10 +0000 Subject: [PATCH 021/189] build(deps): bump thiserror from 1.0.38 to 1.0.40 Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.38 to 1.0.40. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.38...1.0.40) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4807c61..72b538d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -813,22 +813,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.13", ] [[package]] From 6edcd04c1dd2dd7e9069a6889626779686dab441 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 17:25:34 +0000 Subject: [PATCH 022/189] build(deps): bump anstream from 0.3.0 to 0.3.2 Bumps [anstream](https://github.com/rust-cli/anstyle) from 0.3.0 to 0.3.2. - [Commits](https://github.com/rust-cli/anstyle/compare/anstream-v0.3.0...anstream-v0.3.2) --- updated-dependencies: - dependency-name: anstream dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72b538d..2dcaa9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" dependencies = [ "anstyle", "anstyle-parse", @@ -52,9 +52,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ "anstyle", "windows-sys 0.48.0", From 500f617b2493d278de4fd77717fdc8ebb8d1f980 Mon Sep 17 00:00:00 2001 From: Yiyang Wu Date: Thu, 4 May 2023 22:15:23 +0800 Subject: [PATCH 023/189] vsock: Implement yaml config to support multiple vms setup This commit aims to allow the vhost-user-vsock to support local yaml configuration. It introduces a new parameter '--config ' to allow user to input a yaml configuration during startup and uses config-rs to parse it. Note that the configuration is currently made conflicted to the original input parameters. It introduces a new error -- ConfigParse inside the crates/vsock/src/vhu_vsock.rs to support runtime error handling and the new test_vsock_config_from_file() test. It includes a new README.md with a parameter specification and a config example in the Usage section. It also introduces serde_deserialize(yaml) for VsockParam to let config-rs directly pack the field specified in the array into the VsockParam as suggested in config-rs. The serde crate is added to crates/vsock/Cargo.toml correspondingly. This commit also changes the original #[clap] into #[arg] as suggested in clap-v4. Signed-off-by: Yiyang Wu --- Cargo.lock | 324 ++++++++++++++++++++++++++++++++++ crates/vsock/Cargo.toml | 3 + crates/vsock/README.md | 11 ++ crates/vsock/src/main.rs | 109 +++++++++--- crates/vsock/src/vhu_vsock.rs | 2 + 5 files changed, 426 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2dcaa9b..85191bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "1.0.1" @@ -66,12 +77,29 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "bindgen" version = "0.63.0" @@ -100,6 +128,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -186,6 +223,44 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "config" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" +dependencies = [ + "async-trait", + "json5", + "lazy_static", + "nom", + "pathdiff", + "ron", + "rust-ini", + "serde", + "serde_json", + "toml", + "yaml-rust", +] + +[[package]] +name = "cpufeatures" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "dashmap" version = "5.4.0" @@ -199,6 +274,22 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dlv-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" + [[package]] name = "either" version = "1.8.1" @@ -359,6 +450,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.9" @@ -381,6 +482,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] [[package]] name = "heck" @@ -409,6 +513,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.12" @@ -447,6 +561,23 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -496,6 +627,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.3.4" @@ -559,6 +696,16 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +dependencies = [ + "dlv-list", + "hashbrown", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -582,12 +729,62 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "peeking_take_while" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pest" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "pest_meta" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -689,6 +886,27 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +[[package]] +name = "ron" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" +dependencies = [ + "base64", + "bitflags", + "serde", +] + +[[package]] +name = "rust-ini" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -709,12 +927,62 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "serde_json" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "serial_test" version = "1.0.0" @@ -740,6 +1008,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.1.0" @@ -831,18 +1110,51 @@ dependencies = [ "syn 2.0.13", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +[[package]] +name = "unsafe-libyaml" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" + [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "vhost" version = "0.6.0" @@ -931,10 +1243,13 @@ version = "0.1.0" dependencies = [ "byteorder", "clap", + "config", "env_logger", "epoll", "futures", "log", + "serde", + "serde_yaml", "serial_test", "thiserror", "vhost", @@ -1181,3 +1496,12 @@ name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index b814843..e91f1cd 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -24,6 +24,9 @@ virtio-queue = "0.7" virtio-vsock = "0.2.1" vm-memory = "0.10" vmm-sys-util = "0.11" +config = "0.13" +serde = "1" +serde_yaml = "0.9" [dev-dependencies] virtio-queue = { version = "0.7", features = ["test-utils"] } diff --git a/crates/vsock/README.md b/crates/vsock/README.md index 722ad24..5190ccd 100644 --- a/crates/vsock/README.md +++ b/crates/vsock/README.md @@ -44,6 +44,17 @@ vhost-user-vsock --guest-cid= \ --socket= --uds-path= [--tx-buffer-size=host packets)>] + --config= +``` + +Configuration Example + +```yaml +vms: + - guest_cid: 3 + socket: /tmp/vhost3.socket + uds_path: /tmp/vm3.sock + tx_buffer_size: 65536 ``` Run VMM (e.g. QEMU): diff --git a/crates/vsock/src/main.rs b/crates/vsock/src/main.rs index 41c7ef4..3d365d1 100644 --- a/crates/vsock/src/main.rs +++ b/crates/vsock/src/main.rs @@ -10,47 +10,79 @@ mod vsock_conn; use std::{convert::TryFrom, sync::Arc}; -use clap::Parser; +use crate::vhu_vsock::{Error, Result, VhostUserVsockBackend, VsockConfig}; +use clap::{Args, Parser}; use log::{info, warn}; +use serde::Deserialize; use vhost::{vhost_user, vhost_user::Listener}; use vhost_user_backend::VhostUserDaemon; use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; -use crate::vhu_vsock::{Error, Result, VhostUserVsockBackend, VsockConfig}; - -#[derive(Parser, Debug)] -#[clap(version, about, long_about = None)] -struct VsockArgs { +#[derive(Args, Debug, Deserialize)] +struct VsockParam { /// Context identifier of the guest which uniquely identifies the device for its lifetime. - #[clap(long, default_value_t = 3)] + #[arg(long, default_value_t = 3, conflicts_with = "config")] guest_cid: u64, /// Unix socket to which a hypervisor connects to and sets up the control path with the device. - #[clap(long)] + #[arg(long, conflicts_with = "config")] socket: String, /// Unix socket to which a host-side application connects to. - #[clap(long)] + #[arg(long, conflicts_with = "config")] uds_path: String, /// The size of the buffer used for the TX virtqueue - #[clap(long, default_value_t = 64 * 1024)] + #[clap(long, default_value_t = 64 * 1024, conflicts_with = "config")] tx_buffer_size: u32, } +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct VsockArgs { + #[command(flatten)] + param: Option, + + /// Load from a given configuration file + #[arg(long)] + config: Option, +} + +impl VsockArgs { + pub fn parse_config(&self) -> Option { + if let Some(c) = &self.config { + let b = config::Config::builder() + .add_source(config::File::new(c.as_str(), config::FileFormat::Yaml)) + .build(); + if let Ok(s) = b { + let mut v = s.get::>("vms").unwrap(); + if v.len() == 1 { + return v.pop().map(|vm| { + VsockConfig::new(vm.guest_cid, vm.socket, vm.uds_path, vm.tx_buffer_size) + }); + } + } + } + None + } +} + impl TryFrom for VsockConfig { type Error = Error; fn try_from(cmd_args: VsockArgs) -> Result { - let socket = cmd_args.socket.trim().to_string(); - let uds_path = cmd_args.uds_path.trim().to_string(); - - Ok(VsockConfig::new( - cmd_args.guest_cid, - socket, - uds_path, - cmd_args.tx_buffer_size, - )) + // we try to use the configuration first, if failed, then fall back to the manual settings. + match cmd_args.parse_config() { + Some(c) => Ok(c), + _ => cmd_args.param.map_or(Err(Error::ConfigParse), |p| { + Ok(Self::new( + p.guest_cid, + p.socket.trim().to_string(), + p.uds_path.trim().to_string(), + p.tx_buffer_size, + )) + }), + } } } @@ -108,14 +140,25 @@ fn main() { mod tests { use super::*; use serial_test::serial; + use std::fs::File; + use std::io::Write; impl VsockArgs { fn from_args(guest_cid: u64, socket: &str, uds_path: &str, tx_buffer_size: u32) -> Self { VsockArgs { - guest_cid, - socket: socket.to_string(), - uds_path: uds_path.to_string(), - tx_buffer_size, + param: Some(VsockParam { + guest_cid, + socket: socket.to_string(), + uds_path: uds_path.to_string(), + tx_buffer_size, + }), + config: None, + } + } + fn from_file(config: &str) -> Self { + VsockArgs { + param: None, + config: Some(config.to_string()), } } } @@ -135,6 +178,26 @@ mod tests { assert_eq!(config.get_tx_buffer_size(), 64 * 1024); } + #[test] + #[serial] + fn test_vsock_config_setup_from_file() { + let mut yaml = File::create("./config.yaml").unwrap(); + yaml.write_all( + b"vms: + - guest_cid: 4 + socket: /tmp/vhost4.socket + uds_path: /tmp/vm4.vsock + tx_buffer_size: 65536", + ) + .unwrap(); + let args = VsockArgs::from_file("./config.yaml"); + let config = VsockConfig::try_from(args).unwrap(); + assert_eq!(config.get_guest_cid(), 4); + assert_eq!(config.get_socket_path(), "/tmp/vhost4.socket"); + assert_eq!(config.get_uds_path(), "/tmp/vm4.vsock"); + std::fs::remove_file("./config.yaml").unwrap(); + } + #[test] #[serial] fn test_vsock_server() { diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index 72033f8..e9b12a3 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -122,6 +122,8 @@ pub(crate) enum Error { EmptyBackendRxQ, #[error("Failed to create an EventFd")] EventFdCreate(std::io::Error), + #[error("Failed to parse a configuration file")] + ConfigParse, } impl std::convert::From for std::io::Error { From 49d90112a42f566fc64c884990e85f14a721c954 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 May 2023 04:57:40 +0000 Subject: [PATCH 024/189] build(deps): bump futures from 0.3.26 to 0.3.28 Bumps [futures](https://github.com/rust-lang/futures-rs) from 0.3.26 to 0.3.28. - [Release notes](https://github.com/rust-lang/futures-rs/releases) - [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.26...0.3.28) --- updated-dependencies: - dependency-name: futures dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 85191bb..d1db329 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -362,9 +362,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -377,9 +377,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -393,9 +393,9 @@ checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -405,26 +405,26 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.13", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" @@ -434,9 +434,9 @@ checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", From 0a1e4e2c0f352b29fc9de1e4103947262d1bb928 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 May 2023 04:57:34 +0000 Subject: [PATCH 025/189] build(deps): bump cpufeatures from 0.2.6 to 0.2.7 Bumps [cpufeatures](https://github.com/RustCrypto/utils) from 0.2.6 to 0.2.7. - [Commits](https://github.com/RustCrypto/utils/compare/cpufeatures-v0.2.6...cpufeatures-v0.2.7) --- updated-dependencies: - dependency-name: cpufeatures dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1db329..095d39f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,9 +244,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] From 0d5be1a439f2c79b94cbc29935d950e1a6e2d571 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 May 2023 08:39:40 +0000 Subject: [PATCH 026/189] build(deps): bump windows_x86_64_msvc from 0.42.1 to 0.42.2 Bumps [windows_x86_64_msvc](https://github.com/microsoft/windows-rs) from 0.42.1 to 0.42.2. - [Release notes](https://github.com/microsoft/windows-rs/releases) - [Commits](https://github.com/microsoft/windows-rs/commits) --- updated-dependencies: - dependency-name: windows_x86_64_msvc dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 095d39f..be52444 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1395,7 +1395,7 @@ dependencies = [ "windows_i686_msvc 0.42.1", "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.1", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -1487,9 +1487,9 @@ checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" From b2fa8d91ddc99a020169424d2b516c6230c8fb30 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Thu, 18 May 2023 11:03:30 +0200 Subject: [PATCH 027/189] vsock: deal with process_tx error gracefully It's possible to receive backend events before the VMM contacts us to activate the vrings. Trying to call process_tx in this state will trigger a NoMemoryConfigured error which will end crashing the worker thread. As NoMemoryConfigured is a transitory error, deal with it gracefully printing a warning but continuing the normal execution. Signed-off-by: Sergio Lopez --- crates/vsock/src/vhu_vsock.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index e9b12a3..8e0863e 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -6,6 +6,7 @@ use std::{ u16, u32, u64, u8, }; +use log::warn; use thiserror::Error as ThisError; use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures}; use vhost_user_backend::{VhostUserBackend, VringRwLock}; @@ -289,7 +290,14 @@ impl VhostUserBackend for VhostUserVsockBackend { EVT_QUEUE_EVENT => {} BACKEND_EVENT => { thread.process_backend_evt(evset); - thread.process_tx(vring_tx, evt_idx)?; + if let Err(e) = thread.process_tx(vring_tx, evt_idx) { + match e { + Error::NoMemoryConfigured => { + warn!("Received a backend event before vring initialization") + } + _ => return Err(e.into()), + } + } } _ => { return Err(Error::HandleUnknownEvent.into()); From 9e8a4efdb9b692ec6221c18003f54b9d3fdc4536 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Thu, 18 May 2023 12:08:17 +0200 Subject: [PATCH 028/189] vsock: close incoming UDS conns if we aren't ready It's possible to receive an incoming UDS connection before the VMM has contacted us to initialize the vrings. In this case, close the incoming connection so the client is aware of we aren't yet ready, and to avoid having a lingering incomplete connection around. Signed-off-by: Sergio Lopez --- crates/vsock/src/vhu_vsock_thread.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/vsock/src/vhu_vsock_thread.rs b/crates/vsock/src/vhu_vsock_thread.rs index ab61630..6d13e02 100644 --- a/crates/vsock/src/vhu_vsock_thread.rs +++ b/crates/vsock/src/vhu_vsock_thread.rs @@ -181,10 +181,9 @@ impl VhostUserVsockThread { fn handle_event(&mut self, fd: RawFd, evset: epoll::Events) { if fd == self.host_sock { // This is a new connection initiated by an application running on the host - self.host_listener - .accept() - .map_err(Error::UnixAccept) - .and_then(|(stream, _)| { + let conn = self.host_listener.accept().map_err(Error::UnixAccept); + if self.mem.is_some() { + conn.and_then(|(stream, _)| { stream .set_nonblocking(true) .map(|_| stream) @@ -194,6 +193,13 @@ impl VhostUserVsockThread { .unwrap_or_else(|err| { warn!("Unable to accept new local connection: {:?}", err); }); + } else { + // If we aren't ready to process requests, accept and immediately close + // the connection. + conn.map(drop).unwrap_or_else(|err| { + warn!("Error closing an incoming connection: {:?}", err); + }); + } } else { // Check if the stream represented by fd has already established a // connection with the application running in the guest From d39e99cdd74f3ae45590da0ac4b7c572db0ea2a3 Mon Sep 17 00:00:00 2001 From: Li Zebin Date: Thu, 18 May 2023 00:49:25 +0800 Subject: [PATCH 029/189] docs: fix the wrong file link in vsock README.md Signed-off-by: Li Zebin --- crates/vsock/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/vsock/README.md b/crates/vsock/README.md index 5190ccd..14d084b 100644 --- a/crates/vsock/README.md +++ b/crates/vsock/README.md @@ -33,7 +33,7 @@ the crate are split into various files as described below: - [vsock_conn.rs](src/vsock_conn.rs) - Module introduces a **VsockConnection** structure that represents a single vsock connection between the guest and the host. It also processes packets according to their type. -- [vhu_vsock.rs](src/lib.rs) +- [vhu_vsock.rs](src/vhu_vsock.rs) - exposes the main vhost user vsock backend interface. ## Usage From 3c33937d914f446bb95f15e77616aa8d11815e0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 04:57:56 +0000 Subject: [PATCH 030/189] build(deps): bump digest from 0.10.6 to 0.10.7 Bumps [digest](https://github.com/RustCrypto/traits) from 0.10.6 to 0.10.7. - [Commits](https://github.com/RustCrypto/traits/compare/digest-v0.10.6...digest-v0.10.7) --- updated-dependencies: - dependency-name: digest dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be52444..b5e3349 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -276,9 +276,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", From 6292965c81e24e2ef240bae690e8cc127c0c4f1e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 04:57:36 +0000 Subject: [PATCH 031/189] build(deps): bump windows_i686_msvc from 0.42.1 to 0.42.2 Bumps [windows_i686_msvc](https://github.com/microsoft/windows-rs) from 0.42.1 to 0.42.2. - [Release notes](https://github.com/microsoft/windows-rs/releases) - [Commits](https://github.com/microsoft/windows-rs/commits) --- updated-dependencies: - dependency-name: windows_i686_msvc dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b5e3349..ae6a21e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1392,7 +1392,7 @@ dependencies = [ "windows_aarch64_gnullvm 0.42.1", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", + "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", @@ -1451,9 +1451,9 @@ checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" From a0921b26ab21d87f06bedc3bb6af1e14ca3dc3cb Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 24 May 2023 10:31:59 +0530 Subject: [PATCH 032/189] Run cargo update Update all cargo dependencies with "cargo update" Signed-off-by: Viresh Kumar --- Cargo.lock | 118 ++++++++++++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae6a21e..654e36b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,7 +85,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.16", ] [[package]] @@ -177,9 +177,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.4" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62" +checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc" dependencies = [ "clap_builder", "clap_derive", @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.2.4" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" +checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990" dependencies = [ "anstream", "anstyle", @@ -201,21 +201,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.2.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.16", ] [[package]] name = "clap_lex" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "colorchoice" @@ -417,7 +417,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.16", ] [[package]] @@ -540,13 +540,13 @@ checksum = "ee87fd093563344074bacf24faa0bb0227fb6969fb223e922db798516de924d6" [[package]] name = "io-lifetimes" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -592,9 +592,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.141" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "libgpiod" @@ -635,9 +635,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.3.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "lock_api" @@ -743,9 +743,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pest" -version = "2.5.7" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122" +checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" dependencies = [ "thiserror", "ucd-trie", @@ -753,9 +753,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.7" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15" +checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" dependencies = [ "pest", "pest_generator", @@ -763,22 +763,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.7" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e" +checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.16", ] [[package]] name = "pest_meta" -version = "2.5.7" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e" +checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" dependencies = [ "once_cell", "pest", @@ -805,18 +805,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" dependencies = [ "proc-macro2", ] @@ -871,9 +871,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974" dependencies = [ "aho-corasick", "memchr", @@ -882,9 +882,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] name = "ron" @@ -915,16 +915,16 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.37.7" +version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ "bitflags", "errno 0.3.1", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -941,29 +941,29 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.16", ] [[package]] name = "serde_json" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -1059,9 +1059,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.13" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" dependencies = [ "proc-macro2", "quote", @@ -1107,7 +1107,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.16", ] [[package]] @@ -1133,9 +1133,9 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unsafe-libyaml" @@ -1371,7 +1371,7 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets 0.42.1", + "windows-targets 0.42.2", ] [[package]] @@ -1385,13 +1385,13 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm 0.42.1", + "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.1", + "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm 0.42.2", @@ -1415,9 +1415,9 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" @@ -1439,9 +1439,9 @@ checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" From bea6e0981c148450bc49b2dac4e3614124ab7b57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 May 2023 10:22:54 +0000 Subject: [PATCH 033/189] build(deps): bump vhost from 0.6.0 to 0.7.0 Bumps [vhost](https://github.com/rust-vmm/vhost) from 0.6.0 to 0.7.0. - [Release notes](https://github.com/rust-vmm/vhost/releases) - [Commits](https://github.com/rust-vmm/vhost/compare/vhost-v0.6.0...vhost-v0.7.0) --- updated-dependencies: - dependency-name: vhost dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 48 ++++++++++++++++++++++++++++++----------- crates/gpio/Cargo.toml | 2 +- crates/i2c/Cargo.toml | 2 +- crates/rng/Cargo.toml | 2 +- crates/vsock/Cargo.toml | 2 +- 5 files changed, 39 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 654e36b..c8af154 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1163,7 +1163,19 @@ checksum = "c9b791c5b0717a0558888a4cf7240cea836f39a99cb342e12ce633dcaa078072" dependencies = [ "bitflags", "libc", - "vm-memory", + "vm-memory 0.10.0", + "vmm-sys-util", +] + +[[package]] +name = "vhost" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f81f436bca4541f4d33172e1202882c9d437db34ed17fc6d84c8ff2bde21f5" +dependencies = [ + "bitflags", + "libc", + "vm-memory 0.11.0", "vmm-sys-util", ] @@ -1177,11 +1189,11 @@ dependencies = [ "libgpiod", "log", "thiserror", - "vhost", + "vhost 0.7.0", "vhost-user-backend", "virtio-bindings 0.2.0", "virtio-queue", - "vm-memory", + "vm-memory 0.10.0", "vmm-sys-util", ] @@ -1194,11 +1206,11 @@ dependencies = [ "libc", "log", "thiserror", - "vhost", + "vhost 0.7.0", "vhost-user-backend", "virtio-bindings 0.2.0", "virtio-queue", - "vm-memory", + "vm-memory 0.10.0", "vmm-sys-util", ] @@ -1214,11 +1226,11 @@ dependencies = [ "rand", "tempfile", "thiserror", - "vhost", + "vhost 0.7.0", "vhost-user-backend", "virtio-bindings 0.2.0", "virtio-queue", - "vm-memory", + "vm-memory 0.10.0", "vmm-sys-util", ] @@ -1230,10 +1242,10 @@ checksum = "9f237b91db4ac339d639fb43398b52d785fa51e3c7760ac9425148863c1f4303" dependencies = [ "libc", "log", - "vhost", + "vhost 0.6.0", "virtio-bindings 0.1.0", "virtio-queue", - "vm-memory", + "vm-memory 0.10.0", "vmm-sys-util", ] @@ -1252,12 +1264,12 @@ dependencies = [ "serde_yaml", "serial_test", "thiserror", - "vhost", + "vhost 0.7.0", "vhost-user-backend", "virtio-bindings 0.2.0", "virtio-queue", "virtio-vsock", - "vm-memory", + "vm-memory 0.10.0", "vmm-sys-util", ] @@ -1281,7 +1293,7 @@ checksum = "3ba81e2bcc21c0d2fc5e6683e79367e26ad219197423a498df801d79d5ba77bd" dependencies = [ "log", "virtio-bindings 0.1.0", - "vm-memory", + "vm-memory 0.10.0", "vmm-sys-util", ] @@ -1293,7 +1305,7 @@ checksum = "ba7254bb0f6111fa84cb24bbf1dfb327ad02b1056ce8ed7f13962b8d0ca3aaa2" dependencies = [ "virtio-bindings 0.1.0", "virtio-queue", - "vm-memory", + "vm-memory 0.10.0", ] [[package]] @@ -1307,6 +1319,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "vm-memory" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6ea57fe00f9086c59eeeb68e102dd611686bc3c28520fa465996d4d4bdce07" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "vmm-sys-util" version = "0.11.1" diff --git a/crates/gpio/Cargo.toml b/crates/gpio/Cargo.toml index ab37b34..20c50d6 100644 --- a/crates/gpio/Cargo.toml +++ b/crates/gpio/Cargo.toml @@ -17,7 +17,7 @@ env_logger = "0.10" libc = "0.2" log = "0.4" thiserror = "1.0" -vhost = { version = "0.6", features = ["vhost-user-slave"] } +vhost = { version = "0.7", features = ["vhost-user-slave"] } vhost-user-backend = "0.8" virtio-bindings = "0.2" virtio-queue = "0.7" diff --git a/crates/i2c/Cargo.toml b/crates/i2c/Cargo.toml index 8c5c13b..7e9c570 100644 --- a/crates/i2c/Cargo.toml +++ b/crates/i2c/Cargo.toml @@ -17,7 +17,7 @@ env_logger = "0.10" libc = "0.2" log = "0.4" thiserror = "1.0" -vhost = { version = "0.6", features = ["vhost-user-slave"] } +vhost = { version = "0.7", features = ["vhost-user-slave"] } vhost-user-backend = "0.8" virtio-bindings = "0.2" virtio-queue = "0.7" diff --git a/crates/rng/Cargo.toml b/crates/rng/Cargo.toml index ce84dda..b77b6d8 100644 --- a/crates/rng/Cargo.toml +++ b/crates/rng/Cargo.toml @@ -18,7 +18,7 @@ log = "0.4" rand = "0.8.5" tempfile = "3.5" thiserror = "1.0" -vhost = { version = "0.6", features = ["vhost-user-slave"] } +vhost = { version = "0.7", features = ["vhost-user-slave"] } vhost-user-backend = "0.8" virtio-bindings = "0.2" virtio-queue = "0.7" diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index e91f1cd..71fb347 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -17,7 +17,7 @@ epoll = "4.3.1" futures = { version = "0.3", features = ["thread-pool"] } log = "0.4" thiserror = "1.0" -vhost = { version = "0.6", features = ["vhost-user-slave"] } +vhost = { version = "0.7", features = ["vhost-user-slave"] } vhost-user-backend = "0.8" virtio-bindings = "0.2" virtio-queue = "0.7" From 675789ef699fd0c3338f1e0f6f7d77b885b1d31c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 24 May 2023 10:45:26 +0530 Subject: [PATCH 034/189] Update versions of all rust-vmm crates Update versions of all rust-vmm crates to get away with build issues. Signed-off-by: Viresh Kumar --- Cargo.lock | 64 ++++++++++++++++++++--------------------- crates/gpio/Cargo.toml | 10 +++---- crates/i2c/Cargo.toml | 10 +++---- crates/rng/Cargo.toml | 10 +++---- crates/vsock/Cargo.toml | 10 +++---- 5 files changed, 52 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8af154..f633091 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1155,18 +1155,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "vhost" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b791c5b0717a0558888a4cf7240cea836f39a99cb342e12ce633dcaa078072" -dependencies = [ - "bitflags", - "libc", - "vm-memory 0.10.0", - "vmm-sys-util", -] - [[package]] name = "vhost" version = "0.7.0" @@ -1189,11 +1177,11 @@ dependencies = [ "libgpiod", "log", "thiserror", - "vhost 0.7.0", + "vhost", "vhost-user-backend", "virtio-bindings 0.2.0", - "virtio-queue", - "vm-memory 0.10.0", + "virtio-queue 0.8.0", + "vm-memory 0.11.0", "vmm-sys-util", ] @@ -1206,11 +1194,11 @@ dependencies = [ "libc", "log", "thiserror", - "vhost 0.7.0", + "vhost", "vhost-user-backend", "virtio-bindings 0.2.0", - "virtio-queue", - "vm-memory 0.10.0", + "virtio-queue 0.8.0", + "vm-memory 0.11.0", "vmm-sys-util", ] @@ -1226,26 +1214,26 @@ dependencies = [ "rand", "tempfile", "thiserror", - "vhost 0.7.0", + "vhost", "vhost-user-backend", "virtio-bindings 0.2.0", - "virtio-queue", - "vm-memory 0.10.0", + "virtio-queue 0.8.0", + "vm-memory 0.11.0", "vmm-sys-util", ] [[package]] name = "vhost-user-backend" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f237b91db4ac339d639fb43398b52d785fa51e3c7760ac9425148863c1f4303" +checksum = "a5d3b7affe04f61d19b03c5db823287855789b687218fec139699a0c7f7f2790" dependencies = [ "libc", "log", - "vhost 0.6.0", - "virtio-bindings 0.1.0", - "virtio-queue", - "vm-memory 0.10.0", + "vhost", + "virtio-bindings 0.2.0", + "virtio-queue 0.8.0", + "vm-memory 0.11.0", "vmm-sys-util", ] @@ -1264,12 +1252,12 @@ dependencies = [ "serde_yaml", "serial_test", "thiserror", - "vhost 0.7.0", + "vhost", "vhost-user-backend", "virtio-bindings 0.2.0", - "virtio-queue", + "virtio-queue 0.8.0", "virtio-vsock", - "vm-memory 0.10.0", + "vm-memory 0.11.0", "vmm-sys-util", ] @@ -1297,6 +1285,18 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "virtio-queue" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91aebb1df33db33cbf04d4c2445e4f78d0b0c8e65acfd16a4ee95ef63ca252f8" +dependencies = [ + "log", + "virtio-bindings 0.2.0", + "vm-memory 0.11.0", + "vmm-sys-util", +] + [[package]] name = "virtio-vsock" version = "0.2.1" @@ -1304,7 +1304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba7254bb0f6111fa84cb24bbf1dfb327ad02b1056ce8ed7f13962b8d0ca3aaa2" dependencies = [ "virtio-bindings 0.1.0", - "virtio-queue", + "virtio-queue 0.7.1", "vm-memory 0.10.0", ] @@ -1314,7 +1314,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "688a70366615b45575a424d9c665561c1b5ab2224d494f706b6a6812911a827c" dependencies = [ - "arc-swap", "libc", "winapi", ] @@ -1325,6 +1324,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d6ea57fe00f9086c59eeeb68e102dd611686bc3c28520fa465996d4d4bdce07" dependencies = [ + "arc-swap", "libc", "winapi", ] diff --git a/crates/gpio/Cargo.toml b/crates/gpio/Cargo.toml index 20c50d6..aba760d 100644 --- a/crates/gpio/Cargo.toml +++ b/crates/gpio/Cargo.toml @@ -18,15 +18,15 @@ libc = "0.2" log = "0.4" thiserror = "1.0" vhost = { version = "0.7", features = ["vhost-user-slave"] } -vhost-user-backend = "0.8" +vhost-user-backend = "0.9" virtio-bindings = "0.2" -virtio-queue = "0.7" -vm-memory = "0.10" +virtio-queue = "0.8" +vm-memory = "0.11" vmm-sys-util = "0.11" [target.'cfg(target_env = "gnu")'.dependencies] libgpiod = { git = "https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/", rev = "d8d3a84b2ddf" } [dev-dependencies] -virtio-queue = { version = "0.7", features = ["test-utils"] } -vm-memory = { version = "0.10", features = ["backend-mmap", "backend-atomic"] } +virtio-queue = { version = "0.8", features = ["test-utils"] } +vm-memory = { version = "0.11", features = ["backend-mmap", "backend-atomic"] } diff --git a/crates/i2c/Cargo.toml b/crates/i2c/Cargo.toml index 7e9c570..9608761 100644 --- a/crates/i2c/Cargo.toml +++ b/crates/i2c/Cargo.toml @@ -18,12 +18,12 @@ libc = "0.2" log = "0.4" thiserror = "1.0" vhost = { version = "0.7", features = ["vhost-user-slave"] } -vhost-user-backend = "0.8" +vhost-user-backend = "0.9" virtio-bindings = "0.2" -virtio-queue = "0.7" -vm-memory = "0.10" +virtio-queue = "0.8" +vm-memory = "0.11" vmm-sys-util = "0.11" [dev-dependencies] -virtio-queue = { version = "0.7", features = ["test-utils"] } -vm-memory = { version = "0.10", features = ["backend-mmap", "backend-atomic"] } +virtio-queue = { version = "0.8", features = ["test-utils"] } +vm-memory = { version = "0.11", features = ["backend-mmap", "backend-atomic"] } diff --git a/crates/rng/Cargo.toml b/crates/rng/Cargo.toml index b77b6d8..fa9a99c 100644 --- a/crates/rng/Cargo.toml +++ b/crates/rng/Cargo.toml @@ -19,12 +19,12 @@ rand = "0.8.5" tempfile = "3.5" thiserror = "1.0" vhost = { version = "0.7", features = ["vhost-user-slave"] } -vhost-user-backend = "0.8" +vhost-user-backend = "0.9" virtio-bindings = "0.2" -virtio-queue = "0.7" -vm-memory = "0.10" +virtio-queue = "0.8" +vm-memory = "0.11" vmm-sys-util = "0.11" [dev-dependencies] -virtio-queue = { version = "0.7", features = ["test-utils"] } -vm-memory = { version = "0.10", features = ["backend-mmap", "backend-atomic"] } +virtio-queue = { version = "0.8", features = ["test-utils"] } +vm-memory = { version = "0.11", features = ["backend-mmap", "backend-atomic"] } diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index 71fb347..50c3699 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -18,16 +18,16 @@ futures = { version = "0.3", features = ["thread-pool"] } log = "0.4" thiserror = "1.0" vhost = { version = "0.7", features = ["vhost-user-slave"] } -vhost-user-backend = "0.8" +vhost-user-backend = "0.9" virtio-bindings = "0.2" -virtio-queue = "0.7" -virtio-vsock = "0.2.1" -vm-memory = "0.10" +virtio-queue = "0.8" +virtio-vsock = "0.3.0" +vm-memory = "0.11" vmm-sys-util = "0.11" config = "0.13" serde = "1" serde_yaml = "0.9" [dev-dependencies] -virtio-queue = { version = "0.7", features = ["test-utils"] } +virtio-queue = { version = "0.8", features = ["test-utils"] } serial_test = "1.0" From 1872c98ecb64147e8d830c2640616e027420808b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 May 2023 07:05:01 +0000 Subject: [PATCH 035/189] build(deps): bump unicode-ident from 1.0.8 to 1.0.9 Bumps [unicode-ident](https://github.com/dtolnay/unicode-ident) from 1.0.8 to 1.0.9. - [Release notes](https://github.com/dtolnay/unicode-ident/releases) - [Commits](https://github.com/dtolnay/unicode-ident/compare/1.0.8...1.0.9) --- updated-dependencies: - dependency-name: unicode-ident dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 78 +++++++++++++++++------------------------------------- 1 file changed, 25 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f633091..e8248ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1133,9 +1133,9 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unsafe-libyaml" @@ -1163,7 +1163,7 @@ checksum = "84f81f436bca4541f4d33172e1202882c9d437db34ed17fc6d84c8ff2bde21f5" dependencies = [ "bitflags", "libc", - "vm-memory 0.11.0", + "vm-memory", "vmm-sys-util", ] @@ -1179,9 +1179,9 @@ dependencies = [ "thiserror", "vhost", "vhost-user-backend", - "virtio-bindings 0.2.0", - "virtio-queue 0.8.0", - "vm-memory 0.11.0", + "virtio-bindings", + "virtio-queue", + "vm-memory", "vmm-sys-util", ] @@ -1196,9 +1196,9 @@ dependencies = [ "thiserror", "vhost", "vhost-user-backend", - "virtio-bindings 0.2.0", - "virtio-queue 0.8.0", - "vm-memory 0.11.0", + "virtio-bindings", + "virtio-queue", + "vm-memory", "vmm-sys-util", ] @@ -1216,9 +1216,9 @@ dependencies = [ "thiserror", "vhost", "vhost-user-backend", - "virtio-bindings 0.2.0", - "virtio-queue 0.8.0", - "vm-memory 0.11.0", + "virtio-bindings", + "virtio-queue", + "vm-memory", "vmm-sys-util", ] @@ -1231,9 +1231,9 @@ dependencies = [ "libc", "log", "vhost", - "virtio-bindings 0.2.0", - "virtio-queue 0.8.0", - "vm-memory 0.11.0", + "virtio-bindings", + "virtio-queue", + "vm-memory", "vmm-sys-util", ] @@ -1254,37 +1254,19 @@ dependencies = [ "thiserror", "vhost", "vhost-user-backend", - "virtio-bindings 0.2.0", - "virtio-queue 0.8.0", + "virtio-bindings", + "virtio-queue", "virtio-vsock", - "vm-memory 0.11.0", + "vm-memory", "vmm-sys-util", ] -[[package]] -name = "virtio-bindings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff512178285488516ed85f15b5d0113a7cdb89e9e8a760b269ae4f02b84bd6b" - [[package]] name = "virtio-bindings" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b9084faf91b9aa9676ae2cac8f1432df2839d9566e6f19f29dbc13a8b831dff" -[[package]] -name = "virtio-queue" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ba81e2bcc21c0d2fc5e6683e79367e26ad219197423a498df801d79d5ba77bd" -dependencies = [ - "log", - "virtio-bindings 0.1.0", - "vm-memory 0.10.0", - "vmm-sys-util", -] - [[package]] name = "virtio-queue" version = "0.8.0" @@ -1292,30 +1274,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91aebb1df33db33cbf04d4c2445e4f78d0b0c8e65acfd16a4ee95ef63ca252f8" dependencies = [ "log", - "virtio-bindings 0.2.0", - "vm-memory 0.11.0", + "virtio-bindings", + "vm-memory", "vmm-sys-util", ] [[package]] name = "virtio-vsock" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7254bb0f6111fa84cb24bbf1dfb327ad02b1056ce8ed7f13962b8d0ca3aaa2" +checksum = "cb198c4dd87bf0b4f6b5d8cb41284fca13763a5a1a7e5b8a7ccce45e46d4cf73" dependencies = [ - "virtio-bindings 0.1.0", - "virtio-queue 0.7.1", - "vm-memory 0.10.0", -] - -[[package]] -name = "vm-memory" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688a70366615b45575a424d9c665561c1b5ab2224d494f706b6a6812911a827c" -dependencies = [ - "libc", - "winapi", + "virtio-bindings", + "virtio-queue", + "vm-memory", ] [[package]] From 2e425dc5099b426d737449252bb4de8fa40c33c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 May 2023 07:05:01 +0000 Subject: [PATCH 036/189] build(deps): bump io-lifetimes from 1.0.10 to 1.0.11 Bumps [io-lifetimes](https://github.com/sunfishcode/io-lifetimes) from 1.0.10 to 1.0.11. - [Commits](https://github.com/sunfishcode/io-lifetimes/compare/v1.0.10...v1.0.11) --- updated-dependencies: - dependency-name: io-lifetimes dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e8248ed..c12322a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -540,9 +540,9 @@ checksum = "ee87fd093563344074bacf24faa0bb0227fb6969fb223e922db798516de924d6" [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", From 305bb1064ef2ffe5eb7b79833172f4f50466e399 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 May 2023 04:57:42 +0000 Subject: [PATCH 037/189] build(deps): bump rust-vmm-ci from `8627b37` to `285971e` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `8627b37` to `285971e`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/8627b3766b2bedde4657c7e9ddfc6f95a20e6942...285971e8c716512d6e35ac47a009a49fc3c75660) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 8627b37..285971e 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 8627b3766b2bedde4657c7e9ddfc6f95a20e6942 +Subproject commit 285971e8c716512d6e35ac47a009a49fc3c75660 From ef68e502429bbc5490a24c53e73e5ebca2b33144 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 04:59:19 +0000 Subject: [PATCH 038/189] build(deps): bump regex from 1.8.2 to 1.8.3 Bumps [regex](https://github.com/rust-lang/regex) from 1.8.2 to 1.8.3. - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/1.8.2...1.8.3) --- updated-dependencies: - dependency-name: regex dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c12322a..75cda6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -871,9 +871,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974" +checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" dependencies = [ "aho-corasick", "memchr", From 08d55d7a4eceb2944cfbec92f1d77728021670f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 04:59:33 +0000 Subject: [PATCH 039/189] build(deps): bump once_cell from 1.17.1 to 1.18.0 Bumps [once_cell](https://github.com/matklad/once_cell) from 1.17.1 to 1.18.0. - [Changelog](https://github.com/matklad/once_cell/blob/master/CHANGELOG.md) - [Commits](https://github.com/matklad/once_cell/compare/v1.17.1...v1.18.0) --- updated-dependencies: - dependency-name: once_cell dependency-type: indirect update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 75cda6d..4a4e275 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -692,9 +692,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "ordered-multimap" From 7b8ef38b7758fd94fed48cca48a4665a94c56614 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jun 2023 05:00:31 +0000 Subject: [PATCH 040/189] build(deps): bump log from 0.4.17 to 0.4.18 Bumps [log](https://github.com/rust-lang/log) from 0.4.17 to 0.4.18. - [Release notes](https://github.com/rust-lang/log/releases) - [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/log/compare/0.4.17...0.4.18) --- updated-dependencies: - dependency-name: log dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a4e275..6e5b685 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -651,12 +651,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "memchr" From 98c99b604e1fbde5f37270a3036387cb30cd101f Mon Sep 17 00:00:00 2001 From: Gaelan Steele Date: Tue, 20 Jul 2021 01:32:13 -0700 Subject: [PATCH 041/189] scsi: Initial boilerplate. Co-developed-by: Erik Schilling Signed-off-by: Erik Schilling Signed-off-by: Gaelan Steele --- Cargo.lock | 95 ++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 1 + crates/scsi/Cargo.toml | 31 ++++++++++++++ crates/scsi/src/main.rs | 3 ++ rust-vmm-ci | 2 +- 5 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 crates/scsi/Cargo.toml create mode 100644 crates/scsi/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 6e5b685..2c95bb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -687,6 +687,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "once_cell" version = "1.18.0" @@ -800,6 +821,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.58" @@ -1116,6 +1147,23 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" + +[[package]] +name = "toml_edit" +version = "0.19.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + [[package]] name = "typenum" version = "1.16.0" @@ -1176,7 +1224,7 @@ dependencies = [ "thiserror", "vhost", "vhost-user-backend", - "virtio-bindings", + "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "virtio-queue", "vm-memory", "vmm-sys-util", @@ -1193,7 +1241,7 @@ dependencies = [ "thiserror", "vhost", "vhost-user-backend", - "virtio-bindings", + "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "virtio-queue", "vm-memory", "vmm-sys-util", @@ -1213,7 +1261,26 @@ dependencies = [ "thiserror", "vhost", "vhost-user-backend", - "virtio-bindings", + "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "virtio-queue", + "vm-memory", + "vmm-sys-util", +] + +[[package]] +name = "vhost-device-scsi" +version = "0.1.0" +dependencies = [ + "clap", + "env_logger", + "epoll", + "log", + "num_enum", + "tempfile", + "thiserror", + "vhost", + "vhost-user-backend", + "virtio-bindings 0.2.0 (git+https://github.com/rust-vmm/vm-virtio?rev=467c8ec99375a5f4e08b85b18257cd7e0bac1dc0)", "virtio-queue", "vm-memory", "vmm-sys-util", @@ -1228,7 +1295,7 @@ dependencies = [ "libc", "log", "vhost", - "virtio-bindings", + "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "virtio-queue", "vm-memory", "vmm-sys-util", @@ -1251,7 +1318,7 @@ dependencies = [ "thiserror", "vhost", "vhost-user-backend", - "virtio-bindings", + "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "virtio-queue", "virtio-vsock", "vm-memory", @@ -1264,6 +1331,11 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b9084faf91b9aa9676ae2cac8f1432df2839d9566e6f19f29dbc13a8b831dff" +[[package]] +name = "virtio-bindings" +version = "0.2.0" +source = "git+https://github.com/rust-vmm/vm-virtio?rev=467c8ec99375a5f4e08b85b18257cd7e0bac1dc0#467c8ec99375a5f4e08b85b18257cd7e0bac1dc0" + [[package]] name = "virtio-queue" version = "0.8.0" @@ -1271,7 +1343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91aebb1df33db33cbf04d4c2445e4f78d0b0c8e65acfd16a4ee95ef63ca252f8" dependencies = [ "log", - "virtio-bindings", + "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "vm-memory", "vmm-sys-util", ] @@ -1282,7 +1354,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb198c4dd87bf0b4f6b5d8cb41284fca13763a5a1a7e5b8a7ccce45e46d4cf73" dependencies = [ - "virtio-bindings", + "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "virtio-queue", "vm-memory", ] @@ -1488,6 +1560,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +dependencies = [ + "memchr", +] + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index 4d1a538..633fa4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,6 @@ members = [ "crates/gpio", "crates/i2c", "crates/rng", + "crates/scsi", "crates/vsock", ] diff --git a/crates/scsi/Cargo.toml b/crates/scsi/Cargo.toml new file mode 100644 index 0000000..682c9ca --- /dev/null +++ b/crates/scsi/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "vhost-device-scsi" +version = "0.1.0" +authors = ["Gaelan Steele ", "Erik Schilling "] +description = "vhost scsi backend device" +repository = "https://github.com/rust-vmm/vhost-device" +readme = "README.md" +keywords = ["scsi", "vhost", "virt", "backend"] +license = "Apache-2.0 OR BSD-3-Clause" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "4.2", features = ["derive"] } +env_logger = "0.10" +epoll = "4.3" +log = "0.4" +num_enum = "0.5" +thiserror = "1.0" +vhost = { version = "0.7", features = ["vhost-user-slave"] } +vhost-user-backend = "0.9" +# until the scsi bindings hit a release, we have to use the commit that adds them as rev. +virtio-bindings = { git = "https://github.com/rust-vmm/vm-virtio", rev = "467c8ec99375a5f4e08b85b18257cd7e0bac1dc0" } +virtio-queue = "0.8" +vm-memory = "0.11" +vmm-sys-util = "0.11" + +[dev-dependencies] +tempfile = "3.2.0" + diff --git a/crates/scsi/src/main.rs b/crates/scsi/src/main.rs new file mode 100644 index 0000000..5bf256e --- /dev/null +++ b/crates/scsi/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello world"); +} diff --git a/rust-vmm-ci b/rust-vmm-ci index 285971e..8627b37 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 285971e8c716512d6e35ac47a009a49fc3c75660 +Subproject commit 8627b3766b2bedde4657c7e9ddfc6f95a20e6942 From a72a0a74e07d9106a7f6da91897d8e7f185ecdac Mon Sep 17 00:00:00 2001 From: Gaelan Steele Date: Tue, 20 Jul 2021 01:34:52 -0700 Subject: [PATCH 042/189] scsi: Add high-level scsi abstraction This defines the basic interface that any scsi device will have to implement (along with some sensing constants that may be useful to share). The vast majority of this work was done by Gaelan Steele as part of a GSoC project [1][2]. [1] https://github.com/rust-vmm/vhost-device/pull/4 [2] https://gist.github.com/Gaelan/febec4e4606e1320026a0924c3bf74d0 Co-developed-by: Erik Schilling Signed-off-by: Erik Schilling Signed-off-by: Gaelan Steele --- crates/scsi/src/lib.rs | 1 + crates/scsi/src/scsi/mod.rs | 73 +++++++++++++++++++++++++++++++++++ crates/scsi/src/scsi/sense.rs | 35 +++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 crates/scsi/src/lib.rs create mode 100644 crates/scsi/src/scsi/mod.rs create mode 100644 crates/scsi/src/scsi/sense.rs diff --git a/crates/scsi/src/lib.rs b/crates/scsi/src/lib.rs new file mode 100644 index 0000000..b967efa --- /dev/null +++ b/crates/scsi/src/lib.rs @@ -0,0 +1 @@ +pub mod scsi; diff --git a/crates/scsi/src/scsi/mod.rs b/crates/scsi/src/scsi/mod.rs new file mode 100644 index 0000000..0c05d58 --- /dev/null +++ b/crates/scsi/src/scsi/mod.rs @@ -0,0 +1,73 @@ +pub mod sense; + +use std::io::{self, Read, Write}; + +use self::sense::SenseTriple; + +#[derive(PartialEq, Eq, Debug, Clone, Copy)] +pub enum TaskAttr { + Simple, + Ordered, + HeadOfQueue, + Aca, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct CmdOutput { + pub status: u8, + pub status_qualifier: u16, + pub sense: Vec, +} + +impl CmdOutput { + pub const fn ok() -> Self { + Self { + status: 0, + status_qualifier: 0, + sense: Vec::new(), + } + } + + pub fn check_condition(sense: SenseTriple) -> Self { + Self { + status: 2, + status_qualifier: 0, + sense: sense.to_fixed_sense(), + } + } +} + +pub struct Request<'a> { + pub id: u64, + pub cdb: &'a [u8], + pub task_attr: TaskAttr, + pub crn: u8, + pub prio: u8, +} + +/// An transport-level error encountered while processing a SCSI command. +/// +/// This is only for transport-level errors; anything else should be handled by +/// returning a CHECK CONDITION status at the SCSI level. +#[derive(Debug)] +pub enum CmdError { + /// The provided CDB is too short for its operation code. + CdbTooShort, + /// An error occurred while writing to the provided data in writer. + DataIn(io::Error), +} + +/// A transport-independent implementation of a SCSI target. +/// +/// Currently, we only support emulated targets (see the `emulation` module), +/// but other implementations of this trait could implement pass-through to +/// iSCSI targets or SCSI devices on the host. +pub trait Target: Send + Sync { + fn execute_command( + &mut self, + lun: u16, + data_out: &mut dyn Read, + data_in: &mut dyn Write, + req: Request, + ) -> Result; +} diff --git a/crates/scsi/src/scsi/sense.rs b/crates/scsi/src/scsi/sense.rs new file mode 100644 index 0000000..7ced47e --- /dev/null +++ b/crates/scsi/src/scsi/sense.rs @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub struct SenseTriple(u8, u8, u8); + +impl SenseTriple { + pub fn to_fixed_sense(self) -> Vec { + vec![ + 0x70, // response code (fixed, current); valid bit (0) + 0x0, // reserved + self.0, // sk; various upper bits 0 + 0x0, 0x0, 0x0, 0x0, // information + 0xa, // add'l sense length + 0x0, 0x0, 0x0, 0x0, // cmd-specific information + self.1, // asc + self.2, // ascq + 0x0, // field-replacable unit code + 0x0, 0x0, 0x0, // sense-key-sepcific information + ] + } +} + +const NO_SENSE: u8 = 0; +const MEDIUM_ERROR: u8 = 0x3; +const ILLEGAL_REQUEST: u8 = 0x5; + +pub const NO_ADDITIONAL_SENSE_INFORMATION: SenseTriple = SenseTriple(NO_SENSE, 0, 0); + +pub const INVALID_COMMAND_OPERATION_CODE: SenseTriple = SenseTriple(ILLEGAL_REQUEST, 0x20, 0x0); +pub const LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE: SenseTriple = SenseTriple(ILLEGAL_REQUEST, 0x21, 0x0); +pub const INVALID_FIELD_IN_CDB: SenseTriple = SenseTriple(ILLEGAL_REQUEST, 0x24, 0x0); +pub const LOGICAL_UNIT_NOT_SUPPORTED: SenseTriple = SenseTriple(ILLEGAL_REQUEST, 0x21, 0x0); +pub const SAVING_PARAMETERS_NOT_SUPPORTED: SenseTriple = SenseTriple(ILLEGAL_REQUEST, 0x39, 0x0); + +pub const UNRECOVERED_READ_ERROR: SenseTriple = SenseTriple(MEDIUM_ERROR, 0x11, 0x0); From be1eaf3f799db1be2062904b7efdfe3bcfc71118 Mon Sep 17 00:00:00 2001 From: Gaelan Steele Date: Wed, 8 Mar 2023 16:00:10 +0100 Subject: [PATCH 043/189] scsi: Add an file-based target implementation This implements the previously defined interface by emulating the commands against a file-backed block device. The vast majority of this work was done by Gaelan Steele as part of a GSoC project [1][2]. [1] https://github.com/rust-vmm/vhost-device/pull/4 [2] https://gist.github.com/Gaelan/febec4e4606e1320026a0924c3bf74d0 Co-developed-by: Erik Schilling Signed-off-by: Erik Schilling Signed-off-by: Gaelan Steele --- crates/scsi/src/lib.rs | 4 +- .../scsi/src/scsi/emulation/block_device.rs | 632 ++++++++++++++++++ crates/scsi/src/scsi/emulation/command.rs | 577 ++++++++++++++++ crates/scsi/src/scsi/emulation/missing_lun.rs | 62 ++ crates/scsi/src/scsi/emulation/mod.rs | 9 + crates/scsi/src/scsi/emulation/mode_page.rs | 48 ++ .../scsi/src/scsi/emulation/response_data.rs | 107 +++ crates/scsi/src/scsi/emulation/target.rs | 143 ++++ crates/scsi/src/scsi/mod.rs | 3 + crates/scsi/src/scsi/sense.rs | 2 + 10 files changed, 1586 insertions(+), 1 deletion(-) create mode 100644 crates/scsi/src/scsi/emulation/block_device.rs create mode 100644 crates/scsi/src/scsi/emulation/command.rs create mode 100644 crates/scsi/src/scsi/emulation/missing_lun.rs create mode 100644 crates/scsi/src/scsi/emulation/mod.rs create mode 100644 crates/scsi/src/scsi/emulation/mode_page.rs create mode 100644 crates/scsi/src/scsi/emulation/response_data.rs create mode 100644 crates/scsi/src/scsi/emulation/target.rs diff --git a/crates/scsi/src/lib.rs b/crates/scsi/src/lib.rs index b967efa..25dcad9 100644 --- a/crates/scsi/src/lib.rs +++ b/crates/scsi/src/lib.rs @@ -1 +1,3 @@ -pub mod scsi; +// We do not use any of this yet +#[allow(dead_code)] +mod scsi; diff --git a/crates/scsi/src/scsi/emulation/block_device.rs b/crates/scsi/src/scsi/emulation/block_device.rs new file mode 100644 index 0000000..66777e7 --- /dev/null +++ b/crates/scsi/src/scsi/emulation/block_device.rs @@ -0,0 +1,632 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use std::{ + convert::{TryFrom, TryInto}, + fs::File, + io::{self, Read, Write}, + num::{NonZeroU32, NonZeroU64, TryFromIntError}, + ops::{Add, Div, Mul, Sub}, + os::unix::prelude::*, +}; + +use log::{debug, error, warn}; + +use super::{ + command::{ + parse_opcode, CommandType, LunSpecificCommand, ModePageSelection, ModeSensePageControl, + ParseOpcodeResult, ReportSupportedOpCodesMode, SenseFormat, VpdPage, OPCODES, + }, + mode_page::ModePage, + response_data::{respond_standard_inquiry_data, SilentlyTruncate}, + target::{LogicalUnit, LunRequest}, +}; +use crate::scsi::{sense, CmdError, CmdOutput, TaskAttr}; + +pub(crate) enum MediumRotationRate { + Unreported, + NonRotating, +} + +#[derive(Clone, Copy, PartialEq, PartialOrd)] +pub(crate) struct ByteOffset(u64); +impl From for ByteOffset { + fn from(value: u64) -> Self { + ByteOffset(value) + } +} +impl From for u64 { + fn from(value: ByteOffset) -> Self { + value.0 + } +} +impl Div for ByteOffset { + type Output = BlockOffset; + + fn div(self, rhs: BlockSize) -> Self::Output { + BlockOffset(self.0 / NonZeroU64::from(rhs.0)) + } +} + +#[derive(Clone, Copy, PartialEq, PartialOrd)] +pub(crate) struct BlockSize(NonZeroU32); +impl From for u32 { + fn from(value: BlockSize) -> Self { + u32::from(value.0) + } +} +impl TryFrom for BlockSize { + type Error = TryFromIntError; + + fn try_from(value: u32) -> Result { + Ok(BlockSize(NonZeroU32::try_from(value)?)) + } +} + +#[derive(Clone, Copy, PartialEq, PartialOrd)] +pub(crate) struct BlockOffset(u64); +impl From for u64 { + fn from(value: BlockOffset) -> Self { + value.0 + } +} +impl From for BlockOffset { + fn from(value: u64) -> Self { + BlockOffset(value) + } +} +impl Add for BlockOffset { + type Output = BlockOffset; + + fn add(self, rhs: BlockOffset) -> Self::Output { + BlockOffset(self.0 + rhs.0) + } +} +impl Sub for BlockOffset { + type Output = Self; + + fn sub(self, rhs: BlockOffset) -> Self::Output { + BlockOffset(self.0 - rhs.0) + } +} +impl Mul for BlockOffset { + type Output = ByteOffset; + + fn mul(self, rhs: BlockSize) -> Self::Output { + ByteOffset(self.0 * u64::from(NonZeroU64::from(rhs.0))) + } +} + +pub(crate) trait BlockDeviceBackend: Send + Sync { + fn read_exact_at(&mut self, buf: &mut [u8], offset: ByteOffset) -> io::Result<()>; + fn size_in_blocks(&mut self) -> io::Result; + fn block_size(&self) -> BlockSize; + fn sync(&mut self) -> io::Result<()>; +} + +pub(crate) struct FileBackend { + file: File, + block_size: BlockSize, +} + +impl FileBackend { + pub fn new(file: File) -> Self { + Self { + file, + block_size: BlockSize::try_from(512).expect("512 is valid BlockSize"), + } + } +} + +impl BlockDeviceBackend for FileBackend { + fn read_exact_at(&mut self, buf: &mut [u8], offset: ByteOffset) -> io::Result<()> { + self.file.read_exact_at(buf, u64::from(offset)) + } + + fn size_in_blocks(&mut self) -> io::Result { + let len = ByteOffset::from(self.file.metadata()?.len()); + assert!(u64::from(len) % NonZeroU64::from(self.block_size.0) == 0); + Ok(len / self.block_size) + } + + fn block_size(&self) -> BlockSize { + self.block_size + } + + fn sync(&mut self) -> io::Result<()> { + self.file.sync_data() + } +} + +pub(crate) struct BlockDevice { + backend: T, + write_protected: bool, + rotation_rate: MediumRotationRate, +} + +impl BlockDevice { + pub(crate) const fn new(backend: T) -> Self { + Self { + backend, + write_protected: false, + rotation_rate: MediumRotationRate::Unreported, + } + } + + fn read_blocks(&mut self, lba: BlockOffset, blocks: BlockOffset) -> io::Result> { + // TODO: Ideally, this would be a read_vectored directly into guest + // address space. Instead, we have an allocation and several copies. + + let mut ret = vec![ + 0; + usize::try_from(u64::from(blocks * self.backend.block_size())) + .expect("block length in bytes should fit usize") + ]; + + self.backend + .read_exact_at(&mut ret[..], lba * self.backend.block_size())?; + + Ok(ret) + } + + pub fn set_write_protected(&mut self, wp: bool) { + self.write_protected = wp; + } + + pub fn set_solid_state(&mut self, rotation_rate: MediumRotationRate) { + self.rotation_rate = rotation_rate; + } +} + +impl LogicalUnit for BlockDevice { + fn execute_command( + &mut self, + data_in: &mut SilentlyTruncate<&mut dyn Write>, + _data_out: &mut dyn Read, + req: LunRequest, + command: LunSpecificCommand, + ) -> Result { + if req.crn != 0 { + // CRN is a weird bit of the protocol we wouldn't ever expect to be used over + // virtio-scsi; but it's allowed to set it non-zero + warn!("Received non-zero CRN: {}", req.crn); + } + + if req.task_attr != TaskAttr::Simple { + // virtio-scsi spec allows us to treat all task attrs as SIMPLE. + warn!("Ignoring non-simple task attr of {:?}", req.task_attr); + } + + if req.prio != 0 { + // My reading of SAM-6 is that priority is purely advisory, so it's fine to + // ignore it. + warn!("Ignoring non-zero priority of {}.", req.prio); + } + + if req.naca { + // We don't support NACA, and say as much in our INQUIRY data, so if + // we get it that's an error. + warn!("Driver set NACA bit, which is unsupported."); + return Ok(CmdOutput::check_condition(sense::INVALID_FIELD_IN_CDB)); + } + + debug!("Incoming command: {:?}", command); + + match command { + LunSpecificCommand::TestUnitReady => Ok(CmdOutput::ok()), + LunSpecificCommand::ReadCapacity10 => { + match self.backend.size_in_blocks() { + Ok(size) => { + // READ CAPACITY (10) returns a 32-bit LBA, which may not be enough. If it + // isn't, we're supposed to return 0xffff_ffff and hope the driver gets the + // memo and uses the newer READ CAPACITY (16). + + // n.b. this is the last block, ie (length-1), not length + let final_block: u32 = u64::from(size - BlockOffset(1)) + .try_into() + .unwrap_or(0xffff_ffff); + let block_size: u32 = u32::from(self.backend.block_size()); + + data_in + .write_all(&u32::to_be_bytes(final_block)) + .map_err(CmdError::DataIn)?; + data_in + .write_all(&u32::to_be_bytes(block_size)) + .map_err(CmdError::DataIn)?; + + Ok(CmdOutput::ok()) + } + Err(e) => { + error!("Error getting image size: {}", e); + // TODO: Is this a reasonable sense code to send? + Ok(CmdOutput::check_condition(sense::UNRECOVERED_READ_ERROR)) + } + } + } + LunSpecificCommand::ReadCapacity16 => { + match self.backend.size_in_blocks() { + Ok(size) => { + // n.b. this is the last block, ie (length-1), not length + let final_block = u64::from(size - BlockOffset(1)); + let block_size = u32::from(self.backend.block_size()); + + data_in + .write_all(&u64::to_be_bytes(final_block)) + .map_err(CmdError::DataIn)?; + data_in + .write_all(&u32::to_be_bytes(block_size)) + .map_err(CmdError::DataIn)?; + + // no protection stuff; 1-to-1 logical/physical blocks + data_in.write_all(&[0, 0]).map_err(CmdError::DataIn)?; + + // top 2 bits: thin provisioning stuff; other 14 bits are lowest + // aligned LBA, which is zero + data_in + .write_all(&[0b1100_0000, 0]) + .map_err(CmdError::DataIn)?; + + // reserved + data_in.write_all(&[0; 16]).map_err(CmdError::DataIn)?; + + Ok(CmdOutput::ok()) + } + Err(e) => { + error!("Error getting image size: {}", e); + // TODO: Is this a reasonable sense code to send? + Ok(CmdOutput::check_condition(sense::UNRECOVERED_READ_ERROR)) + } + } + } + LunSpecificCommand::ModeSense6 { mode_page, pc, dbd } => { + // we use this for the pages array if we only need a single element; lifetime + // rules mean it has to be declared here + let single_page_array: [ModePage; 1]; + + let pages = match mode_page { + ModePageSelection::Single(x) => { + single_page_array = [x]; + &single_page_array + } + ModePageSelection::AllPageZeros => ModePage::ALL_ZERO, + }; + + let pages_len: u32 = pages.iter().map(|x| u32::from(x.page_length() + 2)).sum(); + // SPC-6r05, 7.5.6: "Logical units that support more than 256 bytes of block + // descriptors and mode pages should implement ten-byte mode commands. The MODE + // DATA LENGTH field in the six-byte CDB header limits the transferred data to + // 256 bytes." + // Unclear what exactly we're supposed to do if we have more than 256 bytes of + // mode pages and get sent a MODE SENSE (6). In any case, we don't at the + // moment; if we ever get that much, this unwrap() will start + // crashing us and we can figure out what to do. + let pages_len = u8::try_from(pages_len).unwrap(); + + // mode parameter header + data_in + .write_all(&[ + pages_len + 3, // size in bytes after this one + 0, // medium type - 0 for SBC + if self.write_protected { + 0b1001_0000 // WP, support DPOFUA + } else { + 0b0001_0000 // support DPOFUA + }, + 0, // block desc length + ]) + .map_err(CmdError::DataIn)?; + + if !dbd { + // TODO: Block descriptors are optional, so we currently + // don't provide them. Does any driver + // actually use them? + } + + for page in pages { + match pc { + ModeSensePageControl::Current | ModeSensePageControl::Default => { + page.write(data_in).map_err(CmdError::DataIn)?; + } + ModeSensePageControl::Changeable => { + // SPC-6 6.14.3: "If the logical unit does not + // implement changeable parameters mode pages and + // the device server receives a MODE SENSE command + // with 01b in the PC field, then the device server + // shall terminate the command with CHECK CONDITION + // status, with the sense key set to ILLEGAL + // REQUEST, and the additional sense code set to + // INVALID FIELD IN CDB." + return Ok(CmdOutput::check_condition(sense::INVALID_FIELD_IN_CDB)); + } + ModeSensePageControl::Saved => { + return Ok(CmdOutput::check_condition( + sense::SAVING_PARAMETERS_NOT_SUPPORTED, + )) + } + } + } + + Ok(CmdOutput::ok()) + } + LunSpecificCommand::Read10 { + dpo, + fua, + lba, + transfer_length, + } => { + if dpo { + // DPO is just a hint that the guest probably won't access + // this any time soon, so we can ignore it + debug!("Silently ignoring DPO flag"); + } + + if fua { + // Somewhat weirdly, SCSI supports FUA on reads. Here's the + // key bit: "A force unit access (FUA) bit set to one + // specifies that the device server shall read the logical + // blocks from… the medium. If the FUA bit is set to one + // and a volatile cache contains a more recent version of a + // logical block than… the medium, then, before reading the + // logical block, the device server shall write the logical + // block to… the medium." + + // I guess the idea is that you can read something back, and + // be absolutely sure what you just read will persist. + + // So for our purposes, we need to make sure whatever we + // return has been saved to disk. fsync()ing the whole image + // is a bit blunt, but does the trick. + + if let Err(e) = self.backend.sync() { + error!("Error syncing file: {}", e); + return Ok(CmdOutput::check_condition(sense::TARGET_FAILURE)); + } + } + + // Ignore group number: AFAICT, it's for separating reads from different + // workloads in performance metrics, and we don't report anything like that + + let size = match self.backend.size_in_blocks() { + Ok(size) => size, + Err(e) => { + error!("Error getting image size for read: {}", e); + return Ok(CmdOutput::check_condition(sense::UNRECOVERED_READ_ERROR)); + } + }; + + let lba = BlockOffset(lba.into()); + let transfer_length = BlockOffset(transfer_length.into()); + + if lba + transfer_length > size { + return Ok(CmdOutput::check_condition( + sense::LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, + )); + } + + let read_result = self.read_blocks(lba, transfer_length); + + match read_result { + Ok(bytes) => { + data_in.write_all(&bytes[..]).map_err(CmdError::DataIn)?; + Ok(CmdOutput::ok()) + } + Err(e) => { + error!("Error reading image: {}", e); + Ok(CmdOutput::check_condition(sense::UNRECOVERED_READ_ERROR)) + } + } + } + LunSpecificCommand::Inquiry(page_code) => { + // top 3 bits 0: peripheral device code = exists and ready + // bottom 5 bits 0: device type = block device + data_in.write_all(&[0]).map_err(CmdError::DataIn)?; + + if let Some(code) = page_code { + let mut out = vec![]; + match code { + VpdPage::SupportedVpdPages => { + out.push(VpdPage::SupportedVpdPages.into()); + out.push(VpdPage::BlockDeviceCharacteristics.into()); + out.push(VpdPage::LogicalBlockProvisioning.into()); + } + VpdPage::BlockDeviceCharacteristics => { + let rotation_rate: u16 = match self.rotation_rate { + MediumRotationRate::Unreported => 0, + MediumRotationRate::NonRotating => 1, + }; + out.extend_from_slice(&rotation_rate.to_be_bytes()); + // nothing worth setting in the rest + out.extend_from_slice(&[0; 58]); + } + VpdPage::LogicalBlockProvisioning => { + out.push(0); // don't support threshold sets + out.push(0b1110_0100); // support unmapping w/ UNMAP + // and WRITE SAME (10 & 16), + // don't support anchored + // LBAs or group descriptors + out.push(0b0000_0010); // thin provisioned + out.push(0); // no threshold % support + } + _ => return Ok(CmdOutput::check_condition(sense::INVALID_FIELD_IN_CDB)), + } + + data_in + .write_all(&[code.into()]) + .map_err(CmdError::DataIn)?; + data_in + .write_all( + &u16::try_from(out.len()) + .expect("VPD page < 2^16 bits") + .to_be_bytes(), + ) + .map_err(CmdError::DataIn)?; + data_in.write_all(&out).map_err(CmdError::DataIn)?; + } else { + respond_standard_inquiry_data(data_in).map_err(CmdError::DataIn)?; + } + + Ok(CmdOutput::ok()) + } + LunSpecificCommand::ReportSupportedOperationCodes { rctd, mode } => { + // helpers for output data format + fn one_command_supported( + data_in: &mut impl Write, + ty: CommandType, + ) -> io::Result<()> { + data_in.write_all(&[0])?; // unused flags + data_in.write_all(&[0b0000_0011])?; // supported, don't set a bunch of flags + let tpl = ty.cdb_template(); + data_in.write_all( + &u16::try_from(tpl.len()) + .expect("length of TPL to be same as CDB") + .to_be_bytes(), + )?; + data_in.write_all(tpl)?; + Ok(()) + } + + fn one_command_not_supported(data_in: &mut impl Write) -> io::Result<()> { + data_in.write_all(&[0])?; // unused flags + data_in.write_all(&[0b0000_0001])?; // not supported + data_in.write_all(&[0; 2])?; // cdb len + Ok(()) + } + + fn timeout_descriptor(data_in: &mut impl Write) -> io::Result<()> { + // timeout descriptor + data_in.write_all(&0xa_u16.to_be_bytes())?; // len + data_in.write_all(&[0, 0])?; // reserved, cmd specific + data_in.write_all(&0_u32.to_be_bytes())?; + data_in.write_all(&0_u32.to_be_bytes())?; + Ok(()) + } + + match mode { + ReportSupportedOpCodesMode::All => { + let cmd_len = if rctd { 20 } else { 8 }; + let len = u32::try_from(OPCODES.len() * cmd_len) + .expect("less than (2^32 / 20) ~= 2^27 opcodes"); + data_in + .write_all(&len.to_be_bytes()) + .map_err(CmdError::DataIn)?; + + for &(ty, (opcode, sa)) in OPCODES { + data_in.write_all(&[opcode]).map_err(CmdError::DataIn)?; + data_in.write_all(&[0]).map_err(CmdError::DataIn)?; // reserved + data_in + .write_all(&sa.unwrap_or(0).to_be_bytes()) + .map_err(CmdError::DataIn)?; + data_in.write_all(&[0]).map_err(CmdError::DataIn)?; // reserved + + let ctdp: u8 = if rctd { 0b10 } else { 0b00 }; + let servactv = u8::from(sa.is_some()); + data_in + .write_all(&[ctdp | servactv]) + .map_err(CmdError::DataIn)?; + + data_in + .write_all( + &u16::try_from(ty.cdb_template().len()) + .expect("length of TPL to be same as CDB") + .to_be_bytes(), + ) + .map_err(CmdError::DataIn)?; + + if rctd { + timeout_descriptor(data_in).map_err(CmdError::DataIn)?; + } + } + } + ReportSupportedOpCodesMode::OneCommand(opcode) => match parse_opcode(opcode) { + ParseOpcodeResult::Command(ty) => { + one_command_supported(data_in, ty).map_err(CmdError::DataIn)?; + + if rctd { + timeout_descriptor(data_in).map_err(CmdError::DataIn)?; + } + } + ParseOpcodeResult::ServiceAction(_) => { + return Ok(CmdOutput::check_condition(sense::INVALID_FIELD_IN_CDB)); + } + ParseOpcodeResult::Invalid => { + warn!("Reporting that we don't support command {:#2x}. It might be worth adding.", opcode); + one_command_not_supported(data_in).map_err(CmdError::DataIn)?; + } + }, + ReportSupportedOpCodesMode::OneServiceAction(opcode, sa) => { + match parse_opcode(opcode) { + ParseOpcodeResult::Command(_) => { + return Ok(CmdOutput::check_condition(sense::INVALID_FIELD_IN_CDB)) + } + ParseOpcodeResult::ServiceAction(unparsed_sa) => { + if let Some(ty) = unparsed_sa.parse(sa) { + one_command_supported(data_in, ty).map_err(CmdError::DataIn)?; + + if rctd { + timeout_descriptor(data_in).map_err(CmdError::DataIn)?; + } + } else { + warn!("Reporting that we don't support command {:#2x}/{:#2x}. It might be worth adding.", opcode, sa); + one_command_not_supported(data_in).map_err(CmdError::DataIn)?; + } + } + ParseOpcodeResult::Invalid => { + // the spec isn't super clear what we're supposed to do here, but I + // think an invalid opcode is one for which our implementation + // "does not implement service actions", so we say invalid field in + // CDB + warn!("Reporting that we don't support command {:#2x}/{:#2x}. It might be worth adding.", opcode, sa); + return Ok(CmdOutput::check_condition(sense::INVALID_FIELD_IN_CDB)); + } + } + } + ReportSupportedOpCodesMode::OneCommandOrServiceAction(opcode, sa) => { + match parse_opcode(opcode) { + ParseOpcodeResult::Command(ty) => { + if sa == 0 { + one_command_supported(data_in, ty).map_err(CmdError::DataIn)?; + + if rctd { + timeout_descriptor(data_in).map_err(CmdError::DataIn)?; + } + } else { + one_command_not_supported(data_in).map_err(CmdError::DataIn)?; + } + } + ParseOpcodeResult::ServiceAction(unparsed_sa) => { + if let Some(ty) = unparsed_sa.parse(sa) { + one_command_supported(data_in, ty).map_err(CmdError::DataIn)?; + + if rctd { + timeout_descriptor(data_in).map_err(CmdError::DataIn)?; + } + } else { + warn!("Reporting that we don't support command {:#2x}/{:#2x}. It might be worth adding.", opcode, sa); + one_command_not_supported(data_in).map_err(CmdError::DataIn)?; + } + } + ParseOpcodeResult::Invalid => { + warn!("Reporting that we don't support command {:#2x}[/{:#2x}]. It might be worth adding.", opcode, sa); + one_command_not_supported(data_in).map_err(CmdError::DataIn)?; + } + } + } + } + Ok(CmdOutput::ok()) + } + LunSpecificCommand::RequestSense(format) => { + match format { + SenseFormat::Fixed => { + data_in + .write_all(&sense::NO_ADDITIONAL_SENSE_INFORMATION.to_fixed_sense()) + .map_err(CmdError::DataIn)?; + Ok(CmdOutput::ok()) + } + SenseFormat::Descriptor => { + // Don't support desciptor format. + Ok(CmdOutput::check_condition(sense::INVALID_FIELD_IN_CDB)) + } + } + } + } + } +} diff --git a/crates/scsi/src/scsi/emulation/command.rs b/crates/scsi/src/scsi/emulation/command.rs new file mode 100644 index 0000000..5572174 --- /dev/null +++ b/crates/scsi/src/scsi/emulation/command.rs @@ -0,0 +1,577 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +//! Data structures and parsing code for SCSI commands. A rough overview: +//! We need to deal with opcodes in two places: in parsing commands themselves, +//! and in implementing REPORT SUPPORTED OPERATION CODES. Therefore, we parse +//! commands in two steps. First, we parse the opcode (and sometimes service +//! action) into a `CommandType` (a C-style enum containing just the commands, +//! not their parameters), then using that, we parse the rest of the CDB and +//! obtain a `Cdb`, which consists of a `Command`, an enum representing a +//! command and its parameters, along with some fields shared across many or all +//! commands. + +use std::convert::{TryFrom, TryInto}; + +use log::warn; +use num_enum::TryFromPrimitive; + +use crate::scsi::emulation::mode_page::ModePage; + +/// One of the modes supported by SCSI's REPORT LUNS command. +#[derive(PartialEq, Eq, TryFromPrimitive, Debug, Copy, Clone)] +#[repr(u8)] +pub(crate) enum ReportLunsSelectReport { + NoWellKnown = 0x0, + WellKnownOnly = 0x1, + All = 0x2, + Administrative = 0x10, + TopLevel = 0x11, + SameConglomerate = 0x12, +} + +/// A type of "vital product data" page returned by SCSI's INQUIRY command. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub(crate) enum VpdPage { + Ascii(u8), + Ata, // * + BlockDeviceCharacteristics, // * + BlockDeviceCharacteristicsExt, + BlockLimits, // * + BlockLimitsExt, + CfaProfile, + DeviceConstituents, + DeviceIdentification, // * + ExtendedInquiry, + FormatPresets, + LogicalBlockProvisioning, // * + ManagementNetworkAddresses, + ModePagePolicy, + PowerCondition, + PowerConsumption, + PortocolSpecificLogicalUnit, + ProtocolSpecificPort, + Referrals, + ScsiFeatureSets, + ScsiPorts, + SoftwareInterfaceIdentification, + SupportedVpdPages, // * + ThirdPartyCopy, + UnitSerialNumber, // * + ZonedBlockDeviceCharacteristics, // * +} +// starred ones are ones Linux will use if available + +#[derive(PartialEq, Eq, TryFromPrimitive, Debug, Copy, Clone)] +#[repr(u8)] +pub(crate) enum ModeSensePageControl { + Current = 0b00, + Changeable = 0b01, + Default = 0b10, + Saved = 0b11, +} + +impl TryFrom for VpdPage { + type Error = (); + + fn try_from(val: u8) -> Result { + match val { + 0x00 => Ok(Self::SupportedVpdPages), + 0x1..=0x7f => Ok(Self::Ascii(val)), + 0x80 => Ok(Self::UnitSerialNumber), + 0x83 => Ok(Self::DeviceIdentification), + 0x84 => Ok(Self::SoftwareInterfaceIdentification), + 0x85 => Ok(Self::ManagementNetworkAddresses), + 0x86 => Ok(Self::ExtendedInquiry), + 0x87 => Ok(Self::ModePagePolicy), + 0x88 => Ok(Self::ScsiPorts), + 0x89 => Ok(Self::Ata), + 0x8a => Ok(Self::PowerCondition), + 0x8b => Ok(Self::DeviceConstituents), + 0x8c => Ok(Self::CfaProfile), + 0x8d => Ok(Self::PowerConsumption), + 0x8f => Ok(Self::ThirdPartyCopy), + 0x90 => Ok(Self::PortocolSpecificLogicalUnit), + 0x91 => Ok(Self::ProtocolSpecificPort), + 0x92 => Ok(Self::ScsiFeatureSets), + 0xb0 => Ok(Self::BlockLimits), + 0xb1 => Ok(Self::BlockDeviceCharacteristics), + 0xb2 => Ok(Self::LogicalBlockProvisioning), + 0xb3 => Ok(Self::Referrals), + 0xb5 => Ok(Self::BlockDeviceCharacteristicsExt), + 0xb6 => Ok(Self::ZonedBlockDeviceCharacteristics), + 0xb7 => Ok(Self::BlockLimitsExt), + 0xb8 => Ok(Self::FormatPresets), + _ => Err(()), + } + } +} + +impl From for u8 { + fn from(pc: VpdPage) -> Self { + match pc { + VpdPage::Ascii(val) => val, + VpdPage::Ata => 0x89, + VpdPage::BlockDeviceCharacteristics => 0xb1, + VpdPage::BlockDeviceCharacteristicsExt => 0xb5, + VpdPage::BlockLimits => 0xb0, + VpdPage::BlockLimitsExt => 0xb7, + VpdPage::CfaProfile => 0x8c, + VpdPage::DeviceConstituents => 0x8b, + VpdPage::DeviceIdentification => 0x83, + VpdPage::ExtendedInquiry => 0x86, + VpdPage::FormatPresets => 0xb8, + VpdPage::LogicalBlockProvisioning => 0xb2, + VpdPage::ManagementNetworkAddresses => 0x85, + VpdPage::ModePagePolicy => 0x87, + VpdPage::PowerCondition => 0x8a, + VpdPage::PowerConsumption => 0x8d, + VpdPage::PortocolSpecificLogicalUnit => 0x90, + VpdPage::ProtocolSpecificPort => 0x91, + VpdPage::Referrals => 0xb3, + VpdPage::ScsiFeatureSets => 0x92, + VpdPage::ScsiPorts => 0x88, + VpdPage::SoftwareInterfaceIdentification => 0x84, + VpdPage::SupportedVpdPages => 0x00, + VpdPage::ThirdPartyCopy => 0x8f, + VpdPage::UnitSerialNumber => 0x80, + VpdPage::ZonedBlockDeviceCharacteristics => 0xb6, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub(crate) enum SenseFormat { + Fixed, + Descriptor, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub(crate) enum ModePageSelection { + AllPageZeros, + Single(ModePage), +} + +#[derive(Debug)] +pub(crate) enum LunIndependentCommand { + ReportLuns(ReportLunsSelectReport), +} + +#[derive(Debug)] +pub(crate) enum LunSpecificCommand { + Inquiry(Option), + ModeSense6 { + pc: ModeSensePageControl, + mode_page: ModePageSelection, + /// Disable block descriptors + dbd: bool, + }, + Read10 { + /// Disable page out (i.e. hint that this page won't be accessed again + /// soon, so we shouldn't bother caching it) + dpo: bool, + /// Force unit access (i.e. bypass cache) + fua: bool, + lba: u32, + transfer_length: u16, + }, + ReadCapacity10, + ReadCapacity16, + ReportSupportedOperationCodes { + /// SCSI RCTD bit: whether we should include timeout descriptors. + rctd: bool, + mode: ReportSupportedOpCodesMode, + }, + RequestSense(SenseFormat), + TestUnitReady, +} + +#[derive(Debug)] +pub(crate) enum Command { + LunIndependentCommand(LunIndependentCommand), + LunSpecificCommand(LunSpecificCommand), +} + +#[derive(Clone, Copy, Debug)] +pub(crate) enum CommandType { + Inquiry, + ModeSense6, + Read10, + ReadCapacity10, + ReadCapacity16, + ReportLuns, + ReportSupportedOperationCodes, + RequestSense, + TestUnitReady, +} + +pub(crate) const OPCODES: &[(CommandType, (u8, Option))] = &[ + (CommandType::TestUnitReady, (0x0, None)), + (CommandType::RequestSense, (0x3, None)), + (CommandType::Inquiry, (0x12, None)), + (CommandType::ModeSense6, (0x1a, None)), + (CommandType::ReadCapacity10, (0x25, None)), + (CommandType::Read10, (0x28, None)), + (CommandType::ReadCapacity16, (0x9e, Some(0x10))), + (CommandType::ReportLuns, (0xa0, None)), + ( + CommandType::ReportSupportedOperationCodes, + (0xa3, Some(0xc)), + ), +]; + +#[derive(Debug, Clone, Copy)] +pub(crate) struct UnparsedServiceAction(u8); +impl UnparsedServiceAction { + pub fn parse(self, service_action: u16) -> Option { + OPCODES + .iter() + .find(|(_, opcode)| *opcode == (self.0, Some(service_action))) + .map(|&(ty, _)| ty) + } +} + +/// See `parse_opcode` +#[derive(Debug, Clone, Copy)] +pub(crate) enum ParseOpcodeResult { + /// The opcode represents a single command. + Command(CommandType), + /// The opcode requires a service action. + ServiceAction(UnparsedServiceAction), + /// The opcode is invalid. + Invalid, +} + +/// Determine the command that corresponds to a SCSI opcode. +/// +/// This is a little weird. Most SCSI commands are just identified by the +/// opcode - the first byte of the CDB - but some opcodes require a second +/// byte, called the service action. Generally, each distinct service action +/// value is treated as a first-class command. But there's some weirdness +/// around parsing, especially with invalid commands: sometimes, we're +/// expected to behave differently for a valid opcode with an invalid +/// service action vs an invalid opcode. +/// +/// To allow for this, we have a two-step parsing API. First, a caller +/// calls `parse_opcode` with the first byte of the CDB. This could return +/// three things: +/// - `Command`: the opcode corresponded to a single-byte command; we're done. +/// - `Invalid`: the opcode isn't recognized at all; we're done. +/// - `ServiceAction`: the opcode is the first byte of a service action; the +/// caller needs to call .parse() on the `UnparsedServiceAction` we returned +/// with the service action byte. +pub(crate) fn parse_opcode(opcode: u8) -> ParseOpcodeResult { + let found = OPCODES.iter().find(|(_, (x, _))| *x == opcode); + match found { + Some(&(ty, (_, None))) => ParseOpcodeResult::Command(ty), + Some((_, (_, Some(_)))) => { + // we found some service action that uses this opcode; so this is a + // service action opcode, and we need the service action + ParseOpcodeResult::ServiceAction(UnparsedServiceAction(opcode)) + } + None => ParseOpcodeResult::Invalid, + } +} + +impl CommandType { + fn from_cdb(cdb: &[u8]) -> Result { + // TODO: Variable-length CDBs put the service action in a different + // place. This'll need to change if we ever support those. IIRC, Linux + // doesn't ever use them, so it may never be relevant. + match parse_opcode(cdb[0]) { + ParseOpcodeResult::Command(ty) => Ok(ty), + ParseOpcodeResult::ServiceAction(sa) => sa + .parse(u16::from(cdb[1] & 0b0001_1111)) + .ok_or(ParseError::InvalidField), + ParseOpcodeResult::Invalid => Err(ParseError::InvalidCommand), + } + } + + /// Return the SCSI "CDB usage data" (see SPC-6 6.34.3) for this command + /// type. + /// + /// Basically, this consists of a structure the size of the CDB for the + /// command, starting with the opcode and service action (if any), then + /// proceeding to a bitmap of fields we recognize. + pub const fn cdb_template(self) -> &'static [u8] { + match self { + Self::TestUnitReady => &[ + 0x0, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0100, + ], + Self::RequestSense => &[ + 0x3, + 0b0000_0001, + 0b0000_0000, + 0b0000_0000, + 0b1111_1111, + 0b0000_0100, + ], + Self::ReportLuns => &[ + 0xa0, + 0b0000_0000, + 0b1111_1111, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b0000_0000, + 0b0000_0100, + ], + Self::ReadCapacity10 => &[ + 0x25, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0100, + ], + Self::ReadCapacity16 => &[ + 0x9e, + 0x10, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b0000_0000, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b0000_0000, + 0b0000_0100, + ], + Self::ModeSense6 => &[ + 0x1a, + 0b0000_1000, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b0000_0100, + ], + Self::Read10 => &[ + 0x28, + 0b1111_1100, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b0011_1111, + 0b1111_1111, + 0b1111_1111, + 0b0000_0100, + ], + Self::Inquiry => &[ + 0x12, + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b0000_0100, + ], + Self::ReportSupportedOperationCodes => &[ + 0xa3, + 0xc, + 0b1000_0111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b0000_0000, + 0b0000_0100, + ], + } + } +} + +#[derive(Debug)] +pub(crate) struct Cdb { + pub command: Command, + pub allocation_length: Option, + pub naca: bool, +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub(crate) enum ParseError { + /// The opcode (specifically the first byte of the CDB) is unknown, i.e. we + /// should respond with INVALID COMMAND OPERATION CODE + InvalidCommand, + /// Another field of the CDB (including the service action, if any) is + /// invalid, i.e. we should respond with INVALID FIELD IN CDB. + InvalidField, + /// The CDB has fewer bytes than necessary for its opcode. + TooSmall, +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub(crate) enum ReportSupportedOpCodesMode { + All, + OneCommand(u8), + OneServiceAction(u8, u16), + OneCommandOrServiceAction(u8, u16), +} + +impl Cdb { + // TODO: do we want to ensure reserved fields are 0? SCSI allows, but + // doesn't require, us to do so. + pub(crate) fn parse(cdb: &[u8]) -> Result { + let ct = CommandType::from_cdb(cdb)?; + if cdb.len() < ct.cdb_template().len() { + return Err(ParseError::TooSmall); + } + // Shrink the cdb down to its size, so accidentally accessing fields past the + // length panics + let cdb = &cdb[..ct.cdb_template().len()]; + + // unwraps below are safe: they're just calling TryFrom to convert from slices + // to fixed-size arrays; in each case, we're using constant indexes and we + // verified above that they're in bounds, so none of them can panic at runtime + + match ct { + CommandType::Inquiry => { + // INQUIRY + let evpd = match cdb[1] { + 0 => false, + 1 => true, + // obselete or reserved bits set + _ => return Err(ParseError::InvalidField), + }; + let page_code_raw = cdb[2]; + let page_code = match (evpd, page_code_raw) { + (false, 0) => None, + (true, pc) => Some(pc.try_into().map_err(|_| ParseError::InvalidField)?), + (false, _) => return Err(ParseError::InvalidField), + }; + Ok(Self { + command: Command::LunSpecificCommand(LunSpecificCommand::Inquiry(page_code)), + allocation_length: Some(u32::from(u16::from_be_bytes( + cdb[3..5].try_into().unwrap(), + ))), + naca: (cdb[5] & 0b0000_0100) != 0, + }) + } + CommandType::ModeSense6 => { + let dbd = match cdb[1] { + 0b0000_1000 => true, + 0b0000_0000 => false, + _ => return Err(ParseError::InvalidField), + }; + let pc = (cdb[2] & 0b1100_0000) >> 6; + let page_code = cdb[2] & 0b0011_1111; + let subpage_code = cdb[3]; + let mode: ModePageSelection = match (page_code, subpage_code) { + (0x8, 0x0) => ModePageSelection::Single(ModePage::Caching), + (0x3f, 0x0) => ModePageSelection::AllPageZeros, + _ => { + warn!( + "Rejecting request for unknown mode page {:#2x}/{:#2x}.", + page_code, subpage_code + ); + return Err(ParseError::InvalidField); + } + }; + Ok(Self { + command: Command::LunSpecificCommand(LunSpecificCommand::ModeSense6 { + pc: pc.try_into().map_err(|_| ParseError::InvalidField)?, + mode_page: mode, + dbd, + }), + allocation_length: Some(u32::from(cdb[4])), + naca: (cdb[5] & 0b0000_0100) != 0, + }) + } + CommandType::Read10 => { + if cdb[1] & 0b1110_0100 != 0 { + // Features (protection and rebuild assist) we don't + // support; the standard says to respond with INVALID + // FIELD IN CDB for these if unsupported + return Err(ParseError::InvalidField); + } + Ok(Self { + command: Command::LunSpecificCommand(LunSpecificCommand::Read10 { + dpo: cdb[1] & 0b0001_0000 != 0, + fua: cdb[1] & 0b0000_1000 != 0, + lba: u32::from_be_bytes(cdb[2..6].try_into().unwrap()), + transfer_length: u16::from_be_bytes(cdb[7..9].try_into().unwrap()), + }), + allocation_length: None, + naca: (cdb[9] & 0b0000_0100) != 0, + }) + } + CommandType::ReadCapacity10 => Ok(Self { + command: Command::LunSpecificCommand(LunSpecificCommand::ReadCapacity10), + allocation_length: None, + naca: (cdb[9] & 0b0000_0100) != 0, + }), + CommandType::ReadCapacity16 => Ok(Self { + command: Command::LunSpecificCommand(LunSpecificCommand::ReadCapacity16), + allocation_length: Some(u32::from_be_bytes(cdb[10..14].try_into().unwrap())), + naca: (cdb[15] & 0b0000_0100) != 0, + }), + CommandType::ReportLuns => Ok(Self { + command: Command::LunIndependentCommand(LunIndependentCommand::ReportLuns( + cdb[2].try_into().map_err(|_| ParseError::InvalidField)?, + )), + allocation_length: Some(u32::from_be_bytes(cdb[6..10].try_into().unwrap())), + naca: (cdb[9] & 0b0000_0100) != 0, + }), + CommandType::ReportSupportedOperationCodes => { + let rctd = cdb[2] & 0b1000_0000 != 0; + let mode = match cdb[2] & 0b0000_0111 { + 0b000 => ReportSupportedOpCodesMode::All, + 0b001 => ReportSupportedOpCodesMode::OneCommand(cdb[3]), + 0b010 => ReportSupportedOpCodesMode::OneServiceAction( + cdb[3], + u16::from_be_bytes(cdb[4..6].try_into().unwrap()), + ), + 0b011 => ReportSupportedOpCodesMode::OneCommandOrServiceAction( + cdb[3], + u16::from_be_bytes(cdb[4..6].try_into().unwrap()), + ), + _ => return Err(ParseError::InvalidField), + }; + + Ok(Self { + command: Command::LunSpecificCommand( + LunSpecificCommand::ReportSupportedOperationCodes { rctd, mode }, + ), + allocation_length: Some(u32::from_be_bytes(cdb[6..10].try_into().unwrap())), + naca: (cdb[11] & 0b0000_0100) != 0, + }) + } + CommandType::RequestSense => { + let format = if cdb[1] & 0b0000_0001 == 1 { + SenseFormat::Descriptor + } else { + SenseFormat::Fixed + }; + Ok(Self { + command: Command::LunSpecificCommand(LunSpecificCommand::RequestSense(format)), + allocation_length: Some(u32::from(cdb[4])), + naca: (cdb[5] & 0b0000_0100) != 0, + }) + } + CommandType::TestUnitReady => Ok(Self { + command: Command::LunSpecificCommand(LunSpecificCommand::TestUnitReady), + allocation_length: None, + naca: (cdb[5] & 0b0000_0100) != 0, + }), + } + } +} diff --git a/crates/scsi/src/scsi/emulation/missing_lun.rs b/crates/scsi/src/scsi/emulation/missing_lun.rs new file mode 100644 index 0000000..cb94baa --- /dev/null +++ b/crates/scsi/src/scsi/emulation/missing_lun.rs @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use std::io::{Read, Write}; + +use super::{ + command::{LunSpecificCommand, SenseFormat}, + response_data::{respond_standard_inquiry_data, SilentlyTruncate}, + target::{LogicalUnit, LunRequest}, +}; +use crate::scsi::{sense, CmdError, CmdError::DataIn, CmdOutput}; + +pub(crate) struct MissingLun; + +impl LogicalUnit for MissingLun { + fn execute_command( + &mut self, + data_in: &mut SilentlyTruncate<&mut dyn Write>, + _data_out: &mut dyn Read, + _req: LunRequest, + cmd: LunSpecificCommand, + ) -> Result { + match cmd { + LunSpecificCommand::Inquiry(page_code) => { + // peripheral qualifier 0b011: logical unit not accessible + // device type 0x1f: unknown/no device type + data_in.write_all(&[0b0110_0000 | 0x1f]).map_err(DataIn)?; + match page_code { + Some(_) => { + // SPC-6 7.7.2: "If the PERIPHERAL QUALIFIER field is + // not set to 000b, the contents of the PAGE LENGTH + // field and the VPD parameters are outside the + // scope of this standard." + // + // Returning a 0 length and no data seems sensible enough. + data_in.write_all(&[0]).map_err(DataIn)?; + } + None => { + respond_standard_inquiry_data(data_in).map_err(DataIn)?; + } + } + Ok(CmdOutput::ok()) + } + LunSpecificCommand::RequestSense(format) => { + match format { + SenseFormat::Fixed => { + data_in + .write_all(&sense::LOGICAL_UNIT_NOT_SUPPORTED.to_fixed_sense()) + .map_err(DataIn)?; + Ok(CmdOutput::ok()) + } + SenseFormat::Descriptor => { + // Don't support desciptor format. + Ok(CmdOutput::check_condition(sense::INVALID_FIELD_IN_CDB)) + } + } + } + _ => Ok(CmdOutput::check_condition( + sense::LOGICAL_UNIT_NOT_SUPPORTED, + )), + } + } +} diff --git a/crates/scsi/src/scsi/emulation/mod.rs b/crates/scsi/src/scsi/emulation/mod.rs new file mode 100644 index 0000000..377b9a4 --- /dev/null +++ b/crates/scsi/src/scsi/emulation/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +pub(crate) mod block_device; +mod command; +pub(crate) mod missing_lun; +pub(crate) mod mode_page; +mod response_data; +pub(crate) mod target; + diff --git a/crates/scsi/src/scsi/emulation/mode_page.rs b/crates/scsi/src/scsi/emulation/mode_page.rs new file mode 100644 index 0000000..e0c30e7 --- /dev/null +++ b/crates/scsi/src/scsi/emulation/mode_page.rs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use std::io::{self, Write}; + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub(crate) enum ModePage { + Caching, +} + +impl ModePage { + pub(crate) const ALL_ZERO: &'static [Self] = &[Self::Caching]; + + pub(crate) const fn page_code(self) -> (u8, u8) { + match self { + Self::Caching => (0x8, 0), + } + } + + pub(crate) const fn page_length(self) -> u8 { + match self { + Self::Caching => 0x12, + } + } + + pub(crate) fn write(self, data_in: &mut impl Write) -> io::Result<()> { + assert_eq!(self.page_code().1, 0, "Subpages aren't supported yet."); + + data_in.write_all(&[ + self.page_code().0, // top 2 bits: no subpage, saving not supported + self.page_length(), // page length + ])?; + + match self { + Self::Caching => { + data_in.write_all(&[ + // Writeback Cache Enable, lots of bits zero + // n.b. kernel logs will show WCE off; it always says + // that for read-only devices, which we are rn + 0b0000_0100, + ])?; + // various cache fine-tuning stuff we can't really control + data_in.write_all(&[0; 0x11])?; + } + } + + Ok(()) + } +} diff --git a/crates/scsi/src/scsi/emulation/response_data.rs b/crates/scsi/src/scsi/emulation/response_data.rs new file mode 100644 index 0000000..352d069 --- /dev/null +++ b/crates/scsi/src/scsi/emulation/response_data.rs @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +//! Some helpers for writing response data, shared between `BlockDevice` and +//! `MissingLun` + +use std::{cmp::min, convert::TryFrom, io, io::Write}; + +/// A wrapper around a `Write` that silently truncates its input after a given +/// number of bytes. This matches the semantics of SCSI's ALLOCATION LENGTH +/// field; anything beyond the allocation length is silently omitted. +pub struct SilentlyTruncate(W, usize); + +impl SilentlyTruncate { + pub const fn new(writer: W, len: usize) -> Self { + Self(writer, len) + } +} + +impl Write for SilentlyTruncate { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + if self.1 == 0 { + // our goal is to silently fail, so once we've stopped actually + // writing, just pretend all writes work + return Ok(buf.len()); + } + let len = min(buf.len(), self.1); + let buf = &buf[..len]; + let written = self.0.write(buf)?; + self.1 -= written; + Ok(written) + } + + fn flush(&mut self) -> std::io::Result<()> { + self.0.flush() + } +} + +fn encode_lun(lun: u16) -> [u8; 8] { + let lun = u8::try_from(lun).expect("more than 255 LUNs are currently unsupported"); + [0, lun, 0, 0, 0, 0, 0, 0] +} + +/// Write the response data for a REPORT LUNS command. +pub fn respond_report_luns(data_in: &mut impl Write, luns: T) -> io::Result<()> +where + T: IntoIterator, + T::IntoIter: ExactSizeIterator, +{ + let iter = luns.into_iter(); + data_in.write_all( + &(u32::try_from(iter.len() * 8)) + .expect("less than 256 LUNS") + .to_be_bytes(), + )?; + data_in.write_all(&[0; 4])?; // reserved + for lun in iter { + data_in.write_all(&encode_lun(lun))?; + } + Ok(()) +} + +/// Write the response data for a standard (i.e. not VPD) inquiry, excluding the +/// first byte (the peripheal qualifier and device type). +pub fn respond_standard_inquiry_data(data_in: &mut impl Write) -> io::Result<()> { + // TODO: Feature bits here we might want to support: + // - NormACA + // - command queueing + data_in.write_all(&[ + // various bits: not removable, not part of a + // conglomerate, no info on hotpluggability + 0, + 0x7, // version: SPC-6 + // bits: don't support NormACA, support modern LUN format + // INQUIRY data version 2 + 0b0001_0000 | 0x2, + 91, // additional INQURIY data length + // bunch of feature bits we don't support: + 0, + 0, + 0, + ])?; + + // TODO: register this or another name with T10 + data_in.write_all(b"rust-vmm")?; + data_in.write_all(b"vhost-user-scsi ")?; + data_in.write_all(b"v0 ")?; + + // The Linux kernel doesn't request any more than this, so any data we return + // after this point is mostly academic. + + data_in.write_all(&[0; 22])?; + + let product_descs: &[u16; 8] = &[ + 0x00c0, // SAM-6 (no version claimed) + 0x05c0, // SPC-5 (no version claimed) + 0x0600, // SBC-4 (no version claimed) + 0x0, 0x0, 0x0, 0x0, 0x0, + ]; + + for desc in product_descs { + data_in.write_all(&desc.to_be_bytes())?; + } + + data_in.write_all(&[0; 22])?; + + Ok(()) +} diff --git a/crates/scsi/src/scsi/emulation/target.rs b/crates/scsi/src/scsi/emulation/target.rs new file mode 100644 index 0000000..82e660c --- /dev/null +++ b/crates/scsi/src/scsi/emulation/target.rs @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use std::convert::TryFrom; +use std::io::{Read, Write}; + +use log::error; + +use super::{ + command::{ + Cdb, Command, LunIndependentCommand, LunSpecificCommand, ParseError, ReportLunsSelectReport, + }, + missing_lun::MissingLun, + response_data::{respond_report_luns, SilentlyTruncate}, +}; +use crate::scsi::{sense, CmdError, CmdOutput, Request, Target, TaskAttr}; + +pub(crate) struct LunRequest { + pub _id: u64, + pub task_attr: TaskAttr, + pub crn: u8, + pub prio: u8, + pub _allocation_length: Option, + pub naca: bool, +} + +/// A single logical unit of an emulated SCSI device. +pub(crate) trait LogicalUnit: Send + Sync { + /// Process a SCSI command sent to this logical unit. + /// + /// # Return value + /// This function returns a Result, but it should return Err only in limited + /// circumstances: when something goes wrong at the transport level, such + /// as writes to `req.data_in` failing or `req.cdb` being too short. + /// Any other errors, such as invalid SCSI commands or I/O errors + /// accessing an underlying file, should result in an Ok return value + /// with a `CmdOutput` representing a SCSI-level error (i.e. CHECK + /// CONDITION status, and appropriate sense data). + fn execute_command( + &mut self, + data_in: &mut SilentlyTruncate<&mut dyn Write>, + data_out: &mut dyn Read, + parameters: LunRequest, + command: LunSpecificCommand, + ) -> Result; +} + +/// A SCSI target implemented by emulating a device within vhost-user-scsi. +pub(crate) struct EmulatedTarget { + luns: Vec>, +} + +impl EmulatedTarget { + pub(crate) fn new() -> Self { + Self { luns: Vec::new() } + } + + pub(crate) fn add_lun(&mut self, logical_unit: Box) { + self.luns.push(logical_unit); + } + + pub(crate) fn luns(&self) -> impl Iterator + ExactSizeIterator + '_ { + // unwrap is safe: we limit LUNs at 256 + self.luns + .iter() + .enumerate() + .map(|(idx, _logical_unit)| u16::try_from(idx).unwrap()) + } +} + +impl Default for EmulatedTarget { + fn default() -> Self { + Self::new() + } +} + +impl Target for EmulatedTarget { + fn execute_command( + &mut self, + lun: u16, + data_out: &mut dyn Read, + data_in: &mut dyn Write, + req: Request, + ) -> Result { + match Cdb::parse(req.cdb) { + Ok(cdb) => { + let mut data_in = SilentlyTruncate::new( + data_in, + cdb.allocation_length.map_or(usize::MAX, |x| x as usize), + ); + + match cdb.command { + Command::LunIndependentCommand(cmd) => match cmd { + LunIndependentCommand::ReportLuns(select_report) => { + match select_report { + ReportLunsSelectReport::NoWellKnown + | ReportLunsSelectReport::All => { + respond_report_luns(&mut data_in, self.luns()) + .map_err(CmdError::DataIn)?; + } + ReportLunsSelectReport::WellKnownOnly + | ReportLunsSelectReport::Administrative + | ReportLunsSelectReport::TopLevel + | ReportLunsSelectReport::SameConglomerate => { + respond_report_luns(&mut data_in, vec![].into_iter()) + .map_err(CmdError::DataIn)?; + } + } + Ok(CmdOutput::ok()) + } + }, + Command::LunSpecificCommand(cmd) => { + let req = LunRequest { + _id: req.id, + task_attr: req.task_attr, + crn: req.crn, + prio: req.prio, + _allocation_length: cdb.allocation_length, + naca: cdb.naca, + }; + match self.luns.get_mut(lun as usize) { + Some(lun) => lun.execute_command(&mut data_in, data_out, req, cmd), + None => MissingLun.execute_command(&mut data_in, data_out, req, cmd), + } + } + } + } + Err(ParseError::InvalidCommand) => { + error!("Rejecting CDB for unknown command: {:?}", req.cdb); + Ok(CmdOutput::check_condition( + sense::INVALID_COMMAND_OPERATION_CODE, + )) + } + // TODO: SCSI has a provision for INVALID FIELD IN CDB to include the + // index of the invalid field, but it's not clear if that's mandatory. + // In any case, QEMU omits it. + Err(ParseError::InvalidField) => { + error!("Rejecting CDB with invalid field: {:?}", req.cdb); + Ok(CmdOutput::check_condition(sense::INVALID_FIELD_IN_CDB)) + } + Err(ParseError::TooSmall) => Err(CmdError::CdbTooShort), + } + } +} diff --git a/crates/scsi/src/scsi/mod.rs b/crates/scsi/src/scsi/mod.rs index 0c05d58..9c1f158 100644 --- a/crates/scsi/src/scsi/mod.rs +++ b/crates/scsi/src/scsi/mod.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +pub mod emulation; pub mod sense; use std::io::{self, Read, Write}; diff --git a/crates/scsi/src/scsi/sense.rs b/crates/scsi/src/scsi/sense.rs index 7ced47e..ad7da96 100644 --- a/crates/scsi/src/scsi/sense.rs +++ b/crates/scsi/src/scsi/sense.rs @@ -22,6 +22,7 @@ impl SenseTriple { const NO_SENSE: u8 = 0; const MEDIUM_ERROR: u8 = 0x3; +const HARDWARE_ERROR: u8 = 0x4; const ILLEGAL_REQUEST: u8 = 0x5; pub const NO_ADDITIONAL_SENSE_INFORMATION: SenseTriple = SenseTriple(NO_SENSE, 0, 0); @@ -33,3 +34,4 @@ pub const LOGICAL_UNIT_NOT_SUPPORTED: SenseTriple = SenseTriple(ILLEGAL_REQUEST, pub const SAVING_PARAMETERS_NOT_SUPPORTED: SenseTriple = SenseTriple(ILLEGAL_REQUEST, 0x39, 0x0); pub const UNRECOVERED_READ_ERROR: SenseTriple = SenseTriple(MEDIUM_ERROR, 0x11, 0x0); +pub const TARGET_FAILURE: SenseTriple = SenseTriple(HARDWARE_ERROR, 0x44, 0x0); From 1de6d02fb8221e300a2f343e99ae5722cb85adbd Mon Sep 17 00:00:00 2001 From: Gaelan Steele Date: Wed, 8 Mar 2023 16:02:08 +0100 Subject: [PATCH 044/189] scsi: Add tests for the emulated target The vast majority of this work was done by Gaelan Steele as part of a GSoC project [1][2]. [1] https://github.com/rust-vmm/vhost-device/pull/4 [2] https://gist.github.com/Gaelan/febec4e4606e1320026a0924c3bf74d0 Co-developed-by: Erik Schilling Signed-off-by: Erik Schilling Signed-off-by: Gaelan Steele --- coverage_config_x86_64.json | 2 +- crates/scsi/src/scsi/emulation/mod.rs | 2 + .../scsi/src/scsi/emulation/tests/bad_lun.rs | 198 +++++++++ .../scsi/src/scsi/emulation/tests/generic.rs | 107 +++++ crates/scsi/src/scsi/emulation/tests/mod.rs | 384 ++++++++++++++++ .../tests/report_supported_operation_codes.rs | 420 ++++++++++++++++++ 6 files changed, 1112 insertions(+), 1 deletion(-) create mode 100644 crates/scsi/src/scsi/emulation/tests/bad_lun.rs create mode 100644 crates/scsi/src/scsi/emulation/tests/generic.rs create mode 100644 crates/scsi/src/scsi/emulation/tests/mod.rs create mode 100644 crates/scsi/src/scsi/emulation/tests/report_supported_operation_codes.rs diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index e3d7752..9cf6dcc 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 67.6, + "coverage_score": 69.6, "exclude_path": "", "crate_features": "" } diff --git a/crates/scsi/src/scsi/emulation/mod.rs b/crates/scsi/src/scsi/emulation/mod.rs index 377b9a4..d697842 100644 --- a/crates/scsi/src/scsi/emulation/mod.rs +++ b/crates/scsi/src/scsi/emulation/mod.rs @@ -7,3 +7,5 @@ pub(crate) mod mode_page; mod response_data; pub(crate) mod target; +#[cfg(test)] +mod tests; diff --git a/crates/scsi/src/scsi/emulation/tests/bad_lun.rs b/crates/scsi/src/scsi/emulation/tests/bad_lun.rs new file mode 100644 index 0000000..38b7e4a --- /dev/null +++ b/crates/scsi/src/scsi/emulation/tests/bad_lun.rs @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use super::{do_command_fail_lun, do_command_in_lun, null_image}; +use crate::scsi::{ + emulation::{block_device::BlockDevice, target::EmulatedTarget}, + sense, +}; + +#[test] +fn test_report_luns() { + let mut target = EmulatedTarget::new(); + for _ in 0..5 { + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + } + + let select_reports = &[0x0, 0x2]; // all but well known, all + + for &sr in select_reports { + do_command_in_lun( + &mut target, + 6, + &[ + 0xa0, // REPORT LUNS + 0, // reserved + sr, // select report + 0, 0, 0, // reserved + 0, 0, 1, 0, // alloc length: 256 + 0, 0, + ], + &[], + &[ + 0, 0, 0, 40, // length: 5*8 = 40 + 0, 0, 0, 0, // reserved + 0, 0, 0, 0, 0, 0, 0, 0, // LUN 0 + 0, 1, 0, 0, 0, 0, 0, 0, // LUN 1 + 0, 2, 0, 0, 0, 0, 0, 0, // LUN 2 + 0, 3, 0, 0, 0, 0, 0, 0, // LUN 3 + 0, 4, 0, 0, 0, 0, 0, 0, // LUN 4 + ], + ); + } +} + +#[test] +fn test_report_luns_empty() { + let mut target = EmulatedTarget::new(); + for _ in 0..5 { + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + } + + // well-known only and several modes explictly defined to return an empty list + // for all but ceratin types of recieving LUNs + let select_reports = &[0x1, 0x10, 0x11, 0x12]; + + for &sr in select_reports { + do_command_in_lun( + &mut target, + 6, + &[ + 0xa0, // REPORT LUNS + 0, // reserved + sr, // select report + 0, 0, 0, // reserved + 0, 0, 1, 0, // alloc length: 256 + 0, 0, + ], + &[], + &[ + 0, 0, 0, 0, // length: 0 + 0, 0, 0, 0, // reserved + ], + ); + } +} + +#[test] +fn test_request_sense() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in_lun( + &mut target, + 1, + &[ + 0x3, // REQUEST SENSE + 0, // fixed format sense data + 0, 0, // reserved + 255, // alloc length + 0, // control + ], + &[], + &sense::LOGICAL_UNIT_NOT_SUPPORTED.to_fixed_sense(), + ); +} + +#[test] +fn test_request_sense_descriptor_format() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_fail_lun( + &mut target, + 1, + &[ + 0x3, // REQUEST SENSE + 1, // descriptor format sense data + 0, 0, // reserved + 255, // alloc length + 0, // control + ], + sense::INVALID_FIELD_IN_CDB, + ); +} + +#[test] +fn test_inquiry() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in_lun( + &mut target, + 1, + &[ + 0x12, // INQUIRY + 0, // EVPD bit: 0 + 0, // page code + 1, 0, // alloc length: 256 + 0, // control + ], + &[], + // some empty comments to get rustfmt to do something vaguely sensible + &[ + 0x7f, // device not accessible, unknown type + 0, // features + 0x7, // version + 0x12, // response data format v2, HiSup = 1 + 91, // addl length + 0, 0, 0, // unsupported features + // vendor + b'r', b'u', b's', b't', b'-', b'v', b'm', b'm', // + // product + b'v', b'h', b'o', b's', b't', b'-', b'u', b's', b'e', b'r', b'-', b's', b'c', b's', + b'i', b' ', // + // revision + b'v', b'0', b' ', b' ', // + // reserved/obselete/vendor specific + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // version descriptors + 0x0, 0xc0, // SAM-6 + 0x05, 0xc0, // SPC-5 (no code assigned for 6 yet) + 0x06, 0x0, // SBC-4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + // reserved + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + ); +} + +#[test] +fn test_other_command() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_fail_lun( + &mut target, + 1, + &[ + 0, // TEST UNIT READY + 0, 0, 0, 0, // reserved + 0, // control + ], + sense::LOGICAL_UNIT_NOT_SUPPORTED, + ); +} + +#[test] +fn test_invalid_command() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_fail_lun( + &mut target, + 1, + &[ + 0xff, // vendor specific + 0, 0, 0, 0, // reserved + 0, // control + ], + sense::INVALID_COMMAND_OPERATION_CODE, + ); +} diff --git a/crates/scsi/src/scsi/emulation/tests/generic.rs b/crates/scsi/src/scsi/emulation/tests/generic.rs new file mode 100644 index 0000000..19f4bd4 --- /dev/null +++ b/crates/scsi/src/scsi/emulation/tests/generic.rs @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +//! Tests for stuff shared between commands. + +use std::io::ErrorKind; + +use super::{do_command_fail, test_image}; +use crate::scsi::{ + emulation::{block_device::BlockDevice, target::EmulatedTarget}, + sense, CmdError, Request, Target, TaskAttr, +}; + +#[test] +fn test_invalid_opcode() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(test_image()); + target.add_lun(Box::new(dev)); + + do_command_fail( + &mut target, + &[ + 0xff, // vendor specific, unused by us + 0, 0, 0, 0, 0, + ], + sense::INVALID_COMMAND_OPERATION_CODE, + ); +} + +#[test] +fn test_invalid_service_action() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(test_image()); + target.add_lun(Box::new(dev)); + + do_command_fail( + &mut target, + &[ + 0xa3, // MAINTAINANCE IN + 0x1f, // vendor specific, unused by us + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + sense::INVALID_FIELD_IN_CDB, + ); +} + +#[test] +fn test_short_data_out_buffer() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(test_image()); + target.add_lun(Box::new(dev)); + + let mut data_in: &mut [u8] = &mut []; + let mut data_out: &[u8] = &[0_u8; 511]; + + let res = target.execute_command( + 0, + &mut data_out, + &mut data_in, + Request { + id: 0, + cdb: &[ + 0x28, // READ (10) + 0, // flags + 0, 0, 0, 15, // LBA: 5 + 0, // reserved, group # + 0, 1, // transfer length: 1 + 0, // control + ], + task_attr: TaskAttr::Simple, + crn: 0, + prio: 0, + }, + ); + + if let CmdError::DataIn(e) = res.unwrap_err() { + assert_eq!(e.kind(), ErrorKind::WriteZero); + } else { + panic!(); + } +} + +#[test] +fn test_short_cdb() { + let mut target: EmulatedTarget = EmulatedTarget::new(); + let dev = BlockDevice::new(test_image()); + target.add_lun(Box::new(dev)); + + let mut data_in: &mut [u8] = &mut []; + let mut data_out: &[u8] = &[]; + + let res = target.execute_command( + 0, + &mut data_out, + &mut data_in, + Request { + id: 0, + cdb: &[ + 0x28, // READ (10) + ], + task_attr: TaskAttr::Simple, + crn: 0, + prio: 0, + }, + ); + + assert!(matches!(res.unwrap_err(), CmdError::CdbTooShort)); +} diff --git a/crates/scsi/src/scsi/emulation/tests/mod.rs b/crates/scsi/src/scsi/emulation/tests/mod.rs new file mode 100644 index 0000000..b112ba0 --- /dev/null +++ b/crates/scsi/src/scsi/emulation/tests/mod.rs @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +#![cfg(test)] + +mod bad_lun; +mod generic; +mod report_supported_operation_codes; + +use std::{fs::File, io::Write}; + +use tempfile::tempfile; + +use super::{ + block_device::{BlockDevice, FileBackend}, + target::EmulatedTarget, +}; +use crate::scsi::{ + sense::{self, SenseTriple}, + CmdOutput, Request, Target, TaskAttr, +}; + +fn null_image() -> FileBackend { + FileBackend::new(File::open("/dev/null").unwrap()) +} + +fn test_image() -> FileBackend { + let mut f = tempfile().unwrap(); + // generate 16 512-byte sectors, each of which consist of a single + // repeated hex character, i.e. + // sector 00: 0000000....0000 + // sector 15: fffffff....ffff + for chr in b'0'..=b'9' { + f.write_all(&[chr; 512]).unwrap(); + } + for chr in b'a'..=b'f' { + f.write_all(&[chr; 512]).unwrap(); + } + FileBackend::new(f) +} + +fn do_command_in_lun( + target: &mut EmulatedTarget, + lun: u16, + cdb: &[u8], + data_out: &[u8], + expected_data_in: &[u8], +) { + let mut data_in = Vec::new(); + + let res = target.execute_command( + lun, + &mut &data_out[..], + &mut data_in, + Request { + id: 0, + cdb, + task_attr: TaskAttr::Simple, + crn: 0, + prio: 0, + }, + ); + + assert_eq!(res.unwrap(), CmdOutput::ok()); + assert_eq!(&data_in, expected_data_in); +} + +fn do_command_fail_lun( + target: &mut EmulatedTarget, + lun: u16, + cdb: &[u8], + expected_error: SenseTriple, +) { + let mut data_in = Vec::new(); + let mut data_out: &[u8] = &[]; + + let res = target.execute_command( + lun, + &mut data_out, + &mut data_in, + Request { + id: 0, + cdb, + task_attr: TaskAttr::Simple, + crn: 0, + prio: 0, + }, + ); + + assert_eq!(res.unwrap(), CmdOutput::check_condition(expected_error)); + assert_eq!(&data_in, &[]); +} + +fn do_command_in( + target: &mut EmulatedTarget, + cdb: &[u8], + data_out: &[u8], + expected_data_in: &[u8], +) { + do_command_in_lun(target, 0, cdb, data_out, expected_data_in); +} + +fn do_command_fail(target: &mut EmulatedTarget, cdb: &[u8], expected_error: SenseTriple) { + do_command_fail_lun(target, 0, cdb, expected_error); +} + +#[test] +fn test_test_unit_ready() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in(&mut target, &[0, 0, 0, 0, 0, 0], &[], &[]); +} + +#[test] +fn test_report_luns() { + let mut target = EmulatedTarget::new(); + for _ in 0..5 { + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + } + + do_command_in( + &mut target, + &[ + 0xa0, // REPORT LUNS + 0, // reserved + 0, // select report + 0, 0, 0, // reserved + 0, 0, 1, 0, // alloc length: 256 + 0, 0, + ], + &[], + &[ + 0, 0, 0, 40, // length: 5*8 = 40 + 0, 0, 0, 0, // reserved + 0, 0, 0, 0, 0, 0, 0, 0, // LUN 0 + 0, 1, 0, 0, 0, 0, 0, 0, // LUN 1 + 0, 2, 0, 0, 0, 0, 0, 0, // LUN 2 + 0, 3, 0, 0, 0, 0, 0, 0, // LUN 3 + 0, 4, 0, 0, 0, 0, 0, 0, // LUN 4 + ], + ); +} + +#[test] +fn test_read_10() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(test_image()); + target.add_lun(Box::new(dev)); + + // TODO: this test relies on the default logical block size of 512. We should + // make that explicit. + + do_command_in( + &mut target, + &[ + 0x28, // READ (10) + 0, // flags + 0, 0, 0, 5, // LBA: 5 + 0, // reserved, group # + 0, 1, // transfer length: 1 + 0, // control + ], + &[], + &[b'5'; 512], + ); +} + +#[test] +fn test_read_10_last_block() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(test_image()); + target.add_lun(Box::new(dev)); + + // TODO: this test relies on the default logical block size of 512. We should + // make that explicit. + + do_command_in( + &mut target, + &[ + 0x28, // READ (10) + 0, // flags + 0, 0, 0, 15, // LBA: 5 + 0, // reserved, group # + 0, 1, // transfer length: 1 + 0, // control + ], + &[], + &[b'f'; 512], + ); +} + +#[test] +fn test_read_10_out_of_range() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(test_image()); + target.add_lun(Box::new(dev)); + + // TODO: this test relies on the default logical block size of 512. We should + // make that explicit. + + do_command_fail( + &mut target, + &[ + 0x28, // READ (10) + 0, // flags + 0, 0, 0, 16, // LBA: 16 + 0, // reserved, group # + 0, 1, // transfer length: 1 + 0, // control + ], + sense::LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, + ); +} + +#[test] +fn test_read_10_cross_out() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + // TODO: this test relies on the default logical block size of 512. We should + // make that explicit. + + do_command_fail( + &mut target, + &[ + 0x28, // READ (10) + 0, // flags + 0, 0, 0, 15, // LBA: 15 + 0, // reserved, group # + 0, 2, // transfer length: 2 + 0, // control + ], + sense::LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, + ); +} + +#[test] +fn test_read_capacity_10() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(test_image()); + target.add_lun(Box::new(dev)); + + // TODO: this test relies on the default logical block size of 512. We should + // make that explicit. + + // TODO: we should test behavior with ≥ 2 TiB images. But not sure how we + // can do that reliably without risking using 2 TiB of disk + + do_command_in( + &mut target, + &[ + 0x25, // READ CAPACITY (10) + 0, 0, 0, 0, 0, 0, 0, 0, // flags + 0, // control + ], + &[], + &[ + 0, 0, 0, 15, // returned LBA (last valid LBA), + 0, 0, 2, 0, // block size (512) + ], + ); +} + +#[test] +fn test_read_capacity_16() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(test_image()); + target.add_lun(Box::new(dev)); + + // TODO: this test relies on the default logical block size of 512. We should + // make that explicit. + + do_command_in( + &mut target, + &[ + 0x9e, 0x10, // READ CAPACITY (16) + 0, 0, 0, 0, 0, 0, 0, 0, // obsolete + 0, 0, 0, 32, // allocation length: 32 + 0, // obselete/reserved + 0, // control + ], + &[], + &[ + 0, 0, 0, 0, 0, 0, 0, 15, // returned LBA (last valid LBA), + 0, 0, 2, 0, // block size (512) + 0, // reserved, zoned stuff, protection stuff + 0, // one PB per LB + 0xc0, // thin provisioning, unmapped blocks read 0 + 0, // LBA 0 is aligned (top bits above) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // reserved + ], + ); +} + +#[test] +fn test_inquiry() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0x12, // INQUIRY + 0, // EVPD bit: 0 + 0, // page code + 1, 0, // alloc length: 256 + 0, // control + ], + &[], + // some empty comments to get rustfmt to do something vaguely sensible + &[ + 0, // accessible; direct acccess block device + 0, // features + 0x7, // version + 0x12, // response data format v2, HiSup = 1 + 91, // addl length + 0, 0, 0, // unsupported features + // vendor + b'r', b'u', b's', b't', b'-', b'v', b'm', b'm', // + // product + b'v', b'h', b'o', b's', b't', b'-', b'u', b's', b'e', b'r', b'-', b's', b'c', b's', + b'i', b' ', // + // revision + b'v', b'0', b' ', b' ', // + // reserved/obselete/vendor specific + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // version descriptors + 0x0, 0xc0, // SAM-6 + 0x05, 0xc0, // SPC-5 (no code assigned for 6 yet) + 0x06, 0, // SBC-4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + // reserved + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + ); +} + +#[test] +fn test_request_sense() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0x3, // INQUIRY + 0, // desc bit: 0 + 0, 0, // reserved + 255, // alloc length + 0, // control + ], + &[], + // We'll always return this - modern SCSI has autosense, so any errors are sent with the + // response to the command that caused them (and therefore immediately cleared), and + // REQUEST SENSE returns an actual error only under some exceptional circumstances + // we don't implement. + &sense::NO_ADDITIONAL_SENSE_INFORMATION.to_fixed_sense(), + ); +} + +#[test] +fn test_request_sense_descriptor_format() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_fail( + &mut target, + &[ + 0x3, // INQUIRY + 1, // desc bit: 1 + 0, 0, // reserved + 255, // alloc length + 0, // control + ], + // We don't support descriptor format sense data. + sense::INVALID_FIELD_IN_CDB, + ); +} diff --git a/crates/scsi/src/scsi/emulation/tests/report_supported_operation_codes.rs b/crates/scsi/src/scsi/emulation/tests/report_supported_operation_codes.rs new file mode 100644 index 0000000..016d6a6 --- /dev/null +++ b/crates/scsi/src/scsi/emulation/tests/report_supported_operation_codes.rs @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use super::{do_command_fail, do_command_in, null_image}; +use crate::scsi::{ + emulation::{block_device::BlockDevice, target::EmulatedTarget}, + sense, +}; + +#[test] +fn test_one_command() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0b1, // reporting options: one command + 0, 1, 2, // opcode: TEST UNIT READY, SA ignored + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + &[], + &[ + 0, 0b11, // flags, supported + 0, 6, // cdb len + 0, 0, 0, 0, 0, 0b0100, // usage data + ], + ); +} + +#[test] +fn test_one_command_with_timeout_descriptor() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0x81, // request timeout descs, reporting options: one command + 0, 1, 2, // opcode: TEST UNIT READY, SA ignored + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + &[], + &[ + 0, 0b11, // flags, supported + 0, 6, // cdb len + 0, 0, 0, 0, 0, 0b0100, // usage data + 0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // no timeouts + ], + ); +} + +#[test] +fn test_one_command_unsupported() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0b1, // reporting options: one command + 0xff, 1, 2, // opcode: vendor specific, SA ignored + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + &[], + &[ + 0, 0b01, // flags, not supported + 0, 0, // cdb len + ], + ); +} + +#[test] +fn test_one_command_valid_service_action() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_fail( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0b1, // reporting options: one command + 0x9e, 0, 0x10, // SERVICE ACTION IN (16), READ CAPACITY (16) + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + sense::INVALID_FIELD_IN_CDB, + ); +} + +#[test] +fn test_one_command_invalid_service_action() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_fail( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0b1, // reporting options: one command + 0x9e, 0, 0xff, // SERVICE ACTION IN (16), invalid + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + sense::INVALID_FIELD_IN_CDB, + ); +} + +#[test] +fn test_one_service_action() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0b10, // reporting options: one service action + 0x9e, 0, 0x10, // SERVICE ACTION IN (16), READ CAPACITY (16) + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + &[], + &[ + 0, 0b11, // flags, supported + 0, 16, // cdb len + 0x9e, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, + 0b0100, // usage data + ], + ); +} + +#[test] +fn test_one_service_action_with_timeout_descriptor() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0x82, // request timeout descs, reporting options: one service action + 0x9e, 0, 0x10, // SERVICE ACTION IN (16), READ CAPACITY (16) + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + &[], + &[ + 0, 0b11, // flags, supported + 0, 16, // cdb len + 0x9e, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, + 0b0100, // usage data + 0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // no timeouts + ], + ); +} + +#[test] +fn test_one_service_action_unknown_opcode() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + // not entirely sure this behavior is correct; see comment in implementation + do_command_fail( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0b10, // reporting options: one service action + 0xff, 1, 2, // opcode: vendor specific, unimplemented + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + sense::INVALID_FIELD_IN_CDB, + ); +} + +#[test] +fn test_one_service_action_unknown_service_action() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0b10, // reporting options: one service action + 0x9e, 0, 0xff, // SERVICE ACTION IN (16), invalid SA + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + &[], + &[ + 0, 0b01, // flags, not supported + 0, 0, // cdb len + ], + ); +} + +#[test] +fn test_one_service_action_not_service_action() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_fail( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0b10, // reporting options: one service action + 0, 1, 2, // TEST UNIT READY + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + sense::INVALID_FIELD_IN_CDB, + ); +} + +// rest of these tests are for "mode 3", which the spec calls 011b and our +// implementation calls OneCommandOrServiceAction, but that's a mouthful so just +// use "mode 3" for test names + +#[test] +fn test_mode_3_opcode_without_service_action() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0b11, // reporting options: mode 3 + 0, 0, 0, // opcode: TEST UNIT READY, SA: 0 + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + &[], + &[ + 0, 0b11, // flags, supported + 0, 6, // cdb len + 0, 0, 0, 0, 0, 0b0100, // usage data + ], + ); +} + +#[test] +fn test_mode_3_with_timeout_descriptor() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0x83, // request timeout descs, reporting options: mode 3 + 0, 0, 0, // opcode: TEST UNIT READY, SA: 0 + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + &[], + &[ + 0, 0b11, // flags, supported + 0, 6, // cdb len + 0, 0, 0, 0, 0, 0b0100, // usage data + 0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // no timeouts + ], + ); +} + +#[test] +fn test_mode_3_opcode_with_unnecessary_service_action() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0b11, // reporting options: mode 3 + 0, 0, 1, // opcode: TEST UNIT READY, SA: 1 + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + &[], + &[ + 0, 0b01, // flags, not supported + 0, 0, // cdb len + ], + ); +} + +#[test] +fn test_mode_3_invalid_opcode() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0b11, // reporting options: mode 3 + 0xff, 0, 0, // opcode: vendor specific + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + &[], + &[ + 0, 0b01, // flags, not supported + 0, 0, // cdb len + ], + ); +} + +#[test] +fn test_mode_3_service_action() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0b11, // reporting options: mode 3 + 0x9e, 0, 0x10, // opcode: SERVICE ACTION IN (16), READ CAPACITY (16) + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + &[], + &[ + 0, 0b11, // flags, supported + 0, 16, // cdb len + 0x9e, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, + 0b0100, // usage data + ], + ); +} + +#[test] +fn test_mode_3_service_action_with_timeout_descriptor() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0x83, // request timeout desc, tireporting options: mode 3 + 0x9e, 0, 0x10, // opcode: SERVICE ACTION IN (16), READ CAPACITY (16) + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + &[], + &[ + 0, 0b11, // flags, supported + 0, 16, // cdb len + 0x9e, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0, + 0b0100, // usage data + 0, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // no timeouts + ], + ); +} + +#[test] +fn test_mode_3_invalid_service_action() { + let mut target = EmulatedTarget::new(); + let dev = BlockDevice::new(null_image()); + target.add_lun(Box::new(dev)); + + do_command_in( + &mut target, + &[ + 0xa3, 0x0c, // REPORT SUPPORTED OPERATION CODES + 0b11, // reporting options: mode 3 + 0x9e, 0, 0xff, // opcode: SERVICE ACTION IN (16), invalid SA + 0, 0, 1, 0, // allocation length: 256 + 0, // reserved + 0, // control + ], + &[], + &[ + 0, 0b01, // flags, not supported + 0, 0, // cdb len + ], + ); +} From 7abcad198be841b2a0991bc7b047cfba50757c8e Mon Sep 17 00:00:00 2001 From: Gaelan Steele Date: Wed, 8 Mar 2023 16:08:15 +0100 Subject: [PATCH 045/189] scsi: Add virtio daemon This adds the virtio-specific parts that use the previously formed interfaces and scsi emulation in order to build a daemon that offers files from the host system as drives to the guest. The vast majority of this work was done by Gaelan Steele as part of a GSoC project [1][2]. [1] https://github.com/rust-vmm/vhost-device/pull/4 [2] https://gist.github.com/Gaelan/febec4e4606e1320026a0924c3bf74d0 Co-developed-by: Erik Schilling Signed-off-by: Erik Schilling Signed-off-by: Gaelan Steele --- crates/scsi/CHANGELOG.md | 3 + crates/scsi/src/lib.rs | 3 - crates/scsi/src/main.rs | 129 ++++++++++++++- crates/scsi/src/vhu_scsi.rs | 283 ++++++++++++++++++++++++++++++++ crates/scsi/src/virtio.rs | 313 ++++++++++++++++++++++++++++++++++++ 5 files changed, 726 insertions(+), 5 deletions(-) create mode 100644 crates/scsi/CHANGELOG.md delete mode 100644 crates/scsi/src/lib.rs create mode 100644 crates/scsi/src/vhu_scsi.rs create mode 100644 crates/scsi/src/virtio.rs diff --git a/crates/scsi/CHANGELOG.md b/crates/scsi/CHANGELOG.md new file mode 100644 index 0000000..d471959 --- /dev/null +++ b/crates/scsi/CHANGELOG.md @@ -0,0 +1,3 @@ +# Upcoming Release + +- First initial daemon implementation. diff --git a/crates/scsi/src/lib.rs b/crates/scsi/src/lib.rs deleted file mode 100644 index 25dcad9..0000000 --- a/crates/scsi/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -// We do not use any of this yet -#[allow(dead_code)] -mod scsi; diff --git a/crates/scsi/src/main.rs b/crates/scsi/src/main.rs index 5bf256e..bfb8ec2 100644 --- a/crates/scsi/src/main.rs +++ b/crates/scsi/src/main.rs @@ -1,3 +1,128 @@ -fn main() { - println!("Hello world"); +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +mod scsi; +mod vhu_scsi; +mod virtio; + +use std::{ + fs::File, + path::PathBuf, + sync::{Arc, RwLock}, +}; + +use clap::Parser; +use log::{error, info, warn}; +use thiserror::Error as ThisError; +use vhost::vhost_user::{self, Listener}; +use vhost_user_backend::VhostUserDaemon; +use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; + +use crate::scsi::emulation::{ + block_device::{BlockDevice, FileBackend, MediumRotationRate}, + target::EmulatedTarget, +}; +use crate::vhu_scsi::VhostUserScsiBackend; + +#[derive(Debug, ThisError)] +enum Error { + #[error("More than 256 LUNs aren't currently supported")] + TooManyLUNs, + #[error("Failed creating listener: {0}")] + FailedCreatingListener(vhost_user::Error), +} + +type Result = std::result::Result; + +#[derive(Parser)] +struct ScsiArgs { + /// Make the images read-only. + /// + /// Currently, we don't actually support writes, but sometimes we want to + /// pretend the disk is writable to work around issues with some tools that + /// use the Linux SCSI generic API. + #[arg(long = "read-only", short = 'r')] + read_only: bool, + /// Tell the guest this disk is non-rotational. + /// + /// Affects some heuristics in Linux around, for example, scheduling. + #[arg(long = "solid-state")] + solid_state: bool, + /// Location of vhost-user socket. + #[clap(short, long)] + socket_path: PathBuf, + /// Images against which the SCSI actions are emulated. + images: Vec, +} + +fn create_backend(args: &ScsiArgs) -> Result { + let mut backend = VhostUserScsiBackend::new(); + let mut target = EmulatedTarget::new(); + + if args.images.len() > 256 { + // This is fairly simple to add; it's just a matter of supporting the right LUN + // encoding formats. + error!("Currently only up to 256 targets are supported"); + return Err(Error::TooManyLUNs); + } + + if !args.read_only { + warn!("Currently, only read-only images are supported. Unless you know what you're doing, you want to pass -r"); + } + + for image in &args.images { + let mut dev = BlockDevice::new(FileBackend::new(File::open(image).expect("Opening image"))); + dev.set_write_protected(args.read_only); + dev.set_solid_state(if args.solid_state { + MediumRotationRate::NonRotating + } else { + MediumRotationRate::Unreported + }); + target.add_lun(Box::new(dev)); + } + + backend.add_target(Box::new(target)); + Ok(backend) +} + +fn start_backend(backend: VhostUserScsiBackend, args: ScsiArgs) -> Result<()> { + let backend = Arc::new(RwLock::new(backend)); + let mut daemon = VhostUserDaemon::new( + "vhost-user-scsi".into(), + Arc::clone(&backend), + GuestMemoryAtomic::new(GuestMemoryMmap::new()), + ) + .expect("Creating daemon"); + + daemon + .start(Listener::new(args.socket_path, true).map_err(Error::FailedCreatingListener)?) + .expect("Starting daemon"); + + match daemon.wait() { + Ok(()) => { + info!("Stopping cleanly."); + } + Err(vhost_user_backend::Error::HandleRequest(vhost_user::Error::PartialMessage)) => { + info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug."); + } + Err(e) => { + warn!("Error running daemon: {:?}", e); + } + } + + // No matter the result, we need to shut down the worker thread. + // unwrap will only panic if we already panicked somewhere else + backend + .read() + .unwrap() + .exit_event + .write(1) + .expect("Shutting down worker thread"); + Ok(()) +} + +fn main() -> Result<()> { + env_logger::init(); + let args = ScsiArgs::parse(); + let backend = create_backend(&args)?; + start_backend(backend, args) } diff --git a/crates/scsi/src/vhu_scsi.rs b/crates/scsi/src/vhu_scsi.rs new file mode 100644 index 0000000..2cb12e4 --- /dev/null +++ b/crates/scsi/src/vhu_scsi.rs @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use std::convert::TryFrom; +use std::io::{self, ErrorKind}; + +use log::{debug, error, info, warn}; +use vhost::vhost_user::{VhostUserProtocolFeatures, VhostUserVirtioFeatures}; +use vhost_user_backend::{VhostUserBackendMut, VringRwLock, VringT}; +use virtio_bindings::{ + virtio_config::VIRTIO_F_VERSION_1, + virtio_ring::{VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC}, + virtio_scsi::VIRTIO_SCSI_F_HOTPLUG, +}; +use virtio_queue::QueueOwnedT; +use vm_memory::{GuestAddressSpace, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap}; +use vmm_sys_util::{ + epoll::EventSet, + eventfd::{EventFd, EFD_NONBLOCK}, +}; + +use crate::scsi::Target; +use crate::{ + scsi::{self, CmdError, TaskAttr}, + virtio::{self, Request, RequestParseError, Response, ResponseCode, VirtioScsiLun, SENSE_SIZE}, +}; + +const REQUEST_QUEUE: u16 = 2; + +type DescriptorChainWriter = virtio::DescriptorChainWriter>; +type DescriptorChainReader = virtio::DescriptorChainReader>; + +pub(crate) struct VhostUserScsiBackend { + event_idx: bool, + mem: Option>, + targets: Vec>, + pub(crate) exit_event: EventFd, +} + +impl VhostUserScsiBackend { + pub(crate) fn new() -> Self { + Self { + event_idx: false, + mem: None, + targets: Vec::new(), + exit_event: EventFd::new(EFD_NONBLOCK).expect("Creating exit eventfd"), + } + } + + fn parse_target(&mut self, lun: VirtioScsiLun) -> Option<(&mut Box, u16)> { + match lun { + VirtioScsiLun::TargetLun(target, lun) => self + .targets + .get_mut(usize::from(target)) + .map(|tgt| (tgt, lun)), + VirtioScsiLun::ReportLuns => { + // TODO: do we need to handle the REPORT LUNS well-known LUN? + // In practice, everyone seems to just use LUN 0 + warn!("Guest is trying to use the REPORT LUNS well-known LUN, which we don't support."); + None + } + } + } + + fn process_requests( + &mut self, + reader: &mut DescriptorChainReader, + writer: &mut DescriptorChainWriter, + ) { + let mut body_writer = writer.clone(); + const RESPONSE_HEADER_SIZE: u32 = 12; + body_writer.skip( + RESPONSE_HEADER_SIZE + u32::try_from(SENSE_SIZE).expect("SENSE_SIZE should fit 32bit"), + ); + + let response = match Request::parse(reader) { + Ok(r) => { + if let Some((target, lun)) = self.parse_target(r.lun) { + let output = target.execute_command( + lun, + reader, + &mut body_writer, + scsi::Request { + id: r.id, + cdb: &r.cdb, + task_attr: match r.task_attr { + 0 => TaskAttr::Simple, + 1 => TaskAttr::Ordered, + 2 => TaskAttr::HeadOfQueue, + 3 => TaskAttr::Aca, + _ => { + // virtio-scsi spec allows us to map any task attr to simple, presumably + // including future ones + warn!("Unknown task attr: {}", r.task_attr); + TaskAttr::Simple + } + }, + crn: r.crn, + prio: r.prio, + }, + ); + + match output { + Ok(output) => { + assert!(output.sense.len() < SENSE_SIZE); + + Response { + response: ResponseCode::Ok, + status: output.status, + status_qualifier: output.status_qualifier, + sense: output.sense, + // TODO: handle residual for data in + residual: body_writer.residual(), + } + } + Err(CmdError::CdbTooShort) => { + // the CDB buffer is, by default, sized larger than any CDB we support; we don't + // handle writes to config space (because QEMU doesn't let us), so there's no + // way the guest can set it too small + unreachable!(); + } + Err(CmdError::DataIn(e)) => { + if e.kind() == ErrorKind::WriteZero { + Response::error(ResponseCode::Overrun, 0) + } else { + error!("Error writing response to guest memory: {}", e); + + // There's some chance the header and data in are on different descriptors, + // and only the data in descriptor is bad, so let's at least try to write an + // error to the header + Response::error(ResponseCode::Failure, body_writer.residual()) + } + } + } + } else { + debug!("Rejecting command to LUN with bad target {:?}", r.lun); + Response::error(ResponseCode::BadTarget, body_writer.residual()) + } + } + Err(RequestParseError::CouldNotReadGuestMemory(e)) => { + // See comment later about errors while writing to guest mem; maybe we at least + // got functional write desciptors, so we can report an error + error!("Error reading request from guest memory: {:?}", e); + Response::error(ResponseCode::Failure, body_writer.residual()) + } + Err(RequestParseError::FailedParsingLun(lun)) => { + error!("Unable to parse LUN: {:?}", lun); + Response::error(ResponseCode::Failure, body_writer.residual()) + } + }; + + if let Err(e) = response.write(writer) { + // Alright, so something went wrong writing our response header to guest memory. + // The only reason this should ever happen, I think, is if the guest gave us a + // virtio descriptor with an invalid address. + + // There's not a great way to recover from this - we just discovered that + // our only way of communicating with the guest doesn't work - so we either + // silently fail or crash. There isn't too much sense in crashing, IMO, as + // the guest could still recover by, say, installing a fixed kernel and + // rebooting. So let's just log an error and do nothing. + error!("Error writing response to guest memory: {:?}", e); + } + } + + fn process_request_queue(&mut self, vring: &VringRwLock) -> Result<(), io::Error> { + let chains: Vec<_> = vring + .get_mut() + .get_queue_mut() + .iter(self.mem.as_ref().unwrap().memory()) + .map_err(|e| io::Error::new(ErrorKind::Other, e))? + .collect(); + for dc in chains { + let mut writer = DescriptorChainWriter::new(dc.clone()); + let mut reader = DescriptorChainReader::new(dc.clone()); + + self.process_requests(&mut reader, &mut writer); + + vring + .add_used(dc.head_index(), writer.max_written()) + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + } + + vring + .signal_used_queue() + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + Ok(()) + } + + pub(crate) fn add_target(&mut self, target: Box) { + self.targets.push(target); + } +} + +impl VhostUserBackendMut for VhostUserScsiBackend { + fn num_queues(&self) -> usize { + // control + event + request queues + let num_request_queues = 1; + 2 + num_request_queues + } + + fn max_queue_size(&self) -> usize { + 128 // qemu assumes this by default + } + + fn features(&self) -> u64 { + 1 << VIRTIO_F_VERSION_1 + | 1 << VIRTIO_SCSI_F_HOTPLUG + | 1 << VIRTIO_RING_F_INDIRECT_DESC + | 1 << VIRTIO_RING_F_EVENT_IDX + | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits() + } + + fn protocol_features(&self) -> VhostUserProtocolFeatures { + VhostUserProtocolFeatures::MQ + } + + fn set_event_idx(&mut self, enabled: bool) { + self.event_idx = enabled; + } + + fn update_memory( + &mut self, + atomic_mem: GuestMemoryAtomic, + ) -> std::result::Result<(), std::io::Error> { + info!("Memory updated - guest probably booting"); + self.mem = Some(atomic_mem); + Ok(()) + } + + fn handle_event( + &mut self, + device_event: u16, + evset: EventSet, + vrings: &[VringRwLock], + thread_id: usize, + ) -> io::Result { + assert!(evset == EventSet::IN); + assert!(vrings.len() == 3); + assert!((device_event as usize) < vrings.len()); + assert!(thread_id == 0); + + let vring = &vrings[device_event as usize]; + match device_event { + REQUEST_QUEUE => { + if self.event_idx { + // vm-virtio's Queue implementation only checks avail_index + // once, so to properly support EVENT_IDX we need to keep + // calling process_request_queue() until it stops finding + // new requests on the queue. + loop { + vring.disable_notification().unwrap(); + self.process_request_queue(vring)?; + if !vring.enable_notification().unwrap() { + break; + } + } + } else { + // Without EVENT_IDX, a single call is enough. + self.process_request_queue(vring)?; + } + } + _ => { + error!("Ignoring descriptor on queue {}", device_event); + } + } + + Ok(false) + } + + fn get_config(&self, _offset: u32, _size: u32) -> Vec { + // QEMU handles config space itself + panic!("Access to configuration space is not supported."); + } + + fn set_config(&mut self, _offset: u32, _buf: &[u8]) -> std::result::Result<(), std::io::Error> { + // QEMU handles config space itself + panic!("Access to configuration space is not supported."); + } + + fn exit_event(&self, _thread_index: usize) -> Option { + Some(self.exit_event.try_clone().expect("Cloning exit eventfd")) + } +} diff --git a/crates/scsi/src/virtio.rs b/crates/scsi/src/virtio.rs new file mode 100644 index 0000000..2f2ecb3 --- /dev/null +++ b/crates/scsi/src/virtio.rs @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +//! Helpers for virtio and virtio-scsi. +use std::{ + cell::Cell, + cmp::{max, min}, + convert::TryInto, + io, + io::{ErrorKind, Read, Write}, + mem, + ops::Deref, + rc::Rc, +}; + +use log::error; +use virtio_bindings::virtio_scsi::virtio_scsi_cmd_req; +use virtio_queue::{Descriptor, DescriptorChain, DescriptorChainRwIter}; +use vm_memory::{Bytes, GuestAddress, GuestMemory}; + +/// virtio-scsi has its own format for LUNs, documented in 5.6.6.1 of virtio +/// v1.1. This represents a LUN parsed from that format. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub(crate) enum VirtioScsiLun { + ReportLuns, + TargetLun(u8, u16), +} + +pub(crate) const REPORT_LUNS: [u8; 8] = [0xc1, 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]; + +impl VirtioScsiLun { + pub(crate) const FLAT_SPACE_ADDRESSING_METHOD: u8 = 0b0100_0000; + pub(crate) const ADDRESS_METHOD_PATTERN: u8 = 0b1100_0000; + + pub(crate) fn parse(bytes: [u8; 8]) -> Option { + if bytes == REPORT_LUNS { + Some(Self::ReportLuns) + } else if bytes[0] == 0x1 { + let target = bytes[1]; + // bytes[2..3] is a normal SCSI single-level lun + if (bytes[2] & Self::ADDRESS_METHOD_PATTERN) != Self::FLAT_SPACE_ADDRESSING_METHOD { + error!( + "Got LUN in unsupported format: {:#2x} {:#2x}. \ + Only flat space addressing is supported!", + bytes[2], bytes[3] + ); + return None; + } + + let lun = u16::from_be_bytes([bytes[2] & !Self::ADDRESS_METHOD_PATTERN, bytes[3]]); + Some(Self::TargetLun(target, lun)) + } else { + None + } + } +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ResponseCode { + Ok = 0, + Overrun = 1, + BadTarget = 3, + Failure = 9, +} + +// These are the defaults given in the virtio spec; QEMU doesn't let the driver +// write to config space, so these will always be the correct values. +pub(crate) const SENSE_SIZE: usize = 96; +pub(crate) const CDB_SIZE: usize = 32; + +pub(crate) struct Request { + pub id: u64, + pub lun: VirtioScsiLun, + pub prio: u8, + pub crn: u8, + pub cdb: [u8; CDB_SIZE], + pub task_attr: u8, +} + +#[derive(Debug)] +pub(crate) enum RequestParseError { + CouldNotReadGuestMemory(io::Error), + FailedParsingLun([u8; 8]), +} + +impl Request { + pub fn parse(reader: &mut impl Read) -> Result { + let mut request = [0; mem::size_of::()]; + + reader + .read_exact(&mut request) + .map_err(RequestParseError::CouldNotReadGuestMemory)?; + + let lun = VirtioScsiLun::parse(request[0..8].try_into().expect("slice is of length 8")) + .ok_or(RequestParseError::FailedParsingLun( + request[0..8].try_into().expect("slice to be of length 8"), + ))?; + + Ok(Self { + id: u64::from_le_bytes(request[8..16].try_into().expect("slice is of length 8")), + lun, + task_attr: request[16], + prio: request[17], + crn: request[18], + cdb: request[19..].try_into().expect("should fit into cdb"), + }) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct Response { + pub response: ResponseCode, + pub status: u8, + pub status_qualifier: u16, + pub sense: Vec, + pub residual: u32, +} + +impl Response { + pub fn write(&self, writer: &mut impl Write) -> Result<(), io::Error> { + writer.write_all(&(self.sense.len() as u32).to_le_bytes())?; // sense_len + writer.write_all(&self.residual.to_le_bytes())?; // residual + writer.write_all(&self.status_qualifier.to_le_bytes())?; // status qual + writer.write_all(&[self.status])?; // status + writer.write_all(&[self.response as u8])?; // response + + writer.write_all(&self.sense[..])?; + + Ok(()) + } + + /// Shortcut to create a response for an error condition, where most fields + /// don't matter. + pub fn error(code: ResponseCode, residual: u32) -> Self { + assert!(code != ResponseCode::Ok); + Self { + response: code, + status: 0, + status_qualifier: 0, + sense: Vec::new(), + residual, + } + } +} + +// TODO: Drop this if https://github.com/rust-vmm/vm-virtio/pull/33 found an agreement +/// A `Write` implementation that writes to the memory indicated by a virtio +/// descriptor chain. +#[derive(Clone)] +pub struct DescriptorChainWriter +where + M::Target: GuestMemory, +{ + chain: DescriptorChain, + iter: DescriptorChainRwIter, + current: Option, + offset: u32, + written: u32, + max_written: Rc>, +} + +impl DescriptorChainWriter +where + M::Target: GuestMemory, +{ + pub fn new(chain: DescriptorChain) -> Self { + let mut iter = chain.clone().writable(); + let current = iter.next(); + Self { + chain, + iter, + current, + offset: 0, + written: 0, + max_written: Rc::new(Cell::new(0)), + } + } + + pub fn skip(&mut self, bytes: u32) { + self.offset += bytes; + self.add_written(bytes); + while self + .current + .map_or(false, |current| self.offset >= current.len()) + { + let current = self.current.expect("loop condition ensures existance"); + self.offset -= current.len(); + self.current = self.iter.next(); + } + } + + pub fn residual(&mut self) -> u32 { + let mut ret = 0; + while let Some(current) = self.current { + ret += current.len() - self.offset; + self.offset = 0; + self.current = self.iter.next(); + } + ret + } + + fn add_written(&mut self, written: u32) { + self.written += written; + self.max_written + .set(max(self.max_written.get(), self.written)); + } + + pub fn max_written(&self) -> u32 { + self.max_written.get() + } +} + +impl Write for DescriptorChainWriter +where + M::Target: GuestMemory, +{ + fn write(&mut self, buf: &[u8]) -> std::io::Result { + if let Some(current) = self.current { + let left_in_descriptor = current.len() - self.offset; + let to_write: u32 = min(left_in_descriptor as usize, buf.len()) as u32; + + let written = self + .chain + .memory() + .write( + &buf[..(to_write as usize)], + GuestAddress(current.addr().0.checked_add(u64::from(self.offset)).ok_or( + io::Error::new(ErrorKind::Other, vm_memory::Error::InvalidGuestRegion), + )?), + ) + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + + self.offset += written as u32; + + if self.offset == current.len() { + self.current = self.iter.next(); + self.offset = 0; + } + + self.add_written(written as u32); + + Ok(written) + } else { + Ok(0) + } + } + + fn flush(&mut self) -> std::io::Result<()> { + // no-op: we're writing directly to guest memory + Ok(()) + } +} + +/// A `Read` implementation that reads from the memory indicated by a virtio +/// descriptor chain. +pub struct DescriptorChainReader +where + M::Target: GuestMemory, +{ + chain: DescriptorChain, + iter: DescriptorChainRwIter, + current: Option, + offset: u32, +} + +impl DescriptorChainReader +where + M::Target: GuestMemory, +{ + pub fn new(chain: DescriptorChain) -> Self { + let mut iter = chain.clone().readable(); + let current = iter.next(); + + Self { + chain, + iter, + current, + offset: 0, + } + } +} + +impl Read for DescriptorChainReader +where + M::Target: GuestMemory, +{ + fn read(&mut self, buf: &mut [u8]) -> io::Result { + if let Some(current) = self.current { + let left_in_descriptor = current.len() - self.offset; + let to_read = min(left_in_descriptor, buf.len() as u32); + + let read = self + .chain + .memory() + .read( + &mut buf[..(to_read as usize)], + GuestAddress(current.addr().0 + u64::from(self.offset)), + ) + .map_err(|e| io::Error::new(ErrorKind::Other, e))?; + + self.offset += read as u32; + + if self.offset == current.len() { + self.current = self.iter.next(); + self.offset = 0; + } + + Ok(read) + } else { + Ok(0) + } + } +} From c07b42b6626c2ba2acbdc4d22e66cbaefe164689 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Fri, 12 May 2023 09:41:44 +0200 Subject: [PATCH 046/189] scsi: Add tests for daemon and virtio code Signed-off-by: Erik Schilling --- crates/scsi/src/main.rs | 34 +++++ crates/scsi/src/vhu_scsi.rs | 285 ++++++++++++++++++++++++++++++++++++ crates/scsi/src/virtio.rs | 57 ++++++++ 3 files changed, 376 insertions(+) diff --git a/crates/scsi/src/main.rs b/crates/scsi/src/main.rs index bfb8ec2..e48959f 100644 --- a/crates/scsi/src/main.rs +++ b/crates/scsi/src/main.rs @@ -126,3 +126,37 @@ fn main() -> Result<()> { let backend = create_backend(&args)?; start_backend(backend, args) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_create_backend() { + let sock = tempfile::NamedTempFile::new().unwrap(); + let args = ScsiArgs { + images: vec!["/dev/null".into()], + read_only: true, + socket_path: sock.path().into(), + solid_state: false, + }; + create_backend(&args).unwrap(); + } + + #[test] + fn test_fail_listener() { + let socket_name = "~/path/not/present/scsi"; + let args = ScsiArgs { + images: vec!["/dev/null".into()], + read_only: true, + socket_path: socket_name.into(), + solid_state: false, + }; + let backend = create_backend(&args).unwrap(); + let err = start_backend(backend, args).unwrap_err(); + if let Error::FailedCreatingListener(_) = err { + } else { + panic!("expected failure when creating listener"); + } + } +} diff --git a/crates/scsi/src/vhu_scsi.rs b/crates/scsi/src/vhu_scsi.rs index 2cb12e4..915419e 100644 --- a/crates/scsi/src/vhu_scsi.rs +++ b/crates/scsi/src/vhu_scsi.rs @@ -281,3 +281,288 @@ impl VhostUserBackendMut for VhostUserScsiBackend { Some(self.exit_event.try_clone().expect("Cloning exit eventfd")) } } + +#[cfg(test)] +mod tests { + use std::{ + convert::TryInto, + io::{self, Read, Write}, + sync::{Arc, Mutex}, + }; + + use vhost_user_backend::{VhostUserBackendMut, VringRwLock, VringT}; + use virtio_bindings::{ + virtio_ring::VRING_DESC_F_WRITE, + virtio_scsi::{ + virtio_scsi_cmd_req, VIRTIO_SCSI_S_BAD_TARGET, VIRTIO_SCSI_S_FAILURE, VIRTIO_SCSI_S_OK, + }, + }; + use virtio_queue::{mock::MockSplitQueue, Descriptor}; + use vm_memory::{ + Address, ByteValued, Bytes, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, + GuestMemoryMmap, + }; + + use super::VhostUserScsiBackend; + use crate::{ + scsi::{CmdOutput, Target, TaskAttr}, + virtio::{ + tests::{VirtioScsiCmdReq, VirtioScsiCmdResp}, + VirtioScsiLun, CDB_SIZE, + }, + }; + + #[allow(dead_code)] + struct RecordedCommand { + lun: u16, + id: u64, + cdb: [u8; CDB_SIZE], + task_attr: TaskAttr, + crn: u8, + prio: u8, + } + + struct FakeTargetCommandCollector { + received_commands: Vec, + } + + impl FakeTargetCommandCollector { + fn new() -> Arc> { + Arc::new(Mutex::new(Self { + received_commands: vec![], + })) + } + } + + type FakeResponse = Result; + + struct FakeTarget { + collector: Arc>, + callback: Cb, + } + + impl FakeTarget { + fn new(collector: Arc>, callback: Cb) -> Self + where + Cb: FnMut(u16, crate::scsi::Request) -> FakeResponse + Sync + Send, + { + Self { + collector, + callback, + } + } + } + + impl Target for FakeTarget + where + Cb: FnMut(u16, crate::scsi::Request) -> FakeResponse + Sync + Send, + { + fn execute_command( + &mut self, + lun: u16, + _data_out: &mut dyn Read, + _data_in: &mut dyn Write, + req: crate::scsi::Request, + ) -> Result { + let mut collector = self.collector.lock().unwrap(); + collector.received_commands.push(RecordedCommand { + lun, + id: req.id, + cdb: req.cdb.try_into().unwrap(), + task_attr: req.task_attr, + crn: req.crn, + prio: req.prio, + }); + (self.callback)(lun, req) + } + } + + fn setup( + req: impl ByteValued, + ) -> ( + VhostUserScsiBackend, + VringRwLock, + GuestMemoryAtomic, + ) { + let mem = GuestMemoryAtomic::new( + GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000_0000)]).unwrap(), + ); + // The `build_desc_chain` function will populate the `NEXT` related flags and field. + let v = vec![ + Descriptor::new(0x10_0000, 0x100, 0, 0), // request + Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), // response + ]; + + mem.memory() + .write_obj(req, GuestAddress(0x10_0000)) + .expect("writing to succeed"); + + let mem_handle = mem.memory(); + + let queue = MockSplitQueue::new(&*mem_handle, 16); + // queue.set_avail_idx(1); + + queue.build_desc_chain(&v).unwrap(); + + // Put the descriptor index 0 in the first available ring position. + mem.memory() + .write_obj(0u16, queue.avail_addr().unchecked_add(4)) + .unwrap(); + + // Set `avail_idx` to 1. + mem.memory() + .write_obj(1u16, queue.avail_addr().unchecked_add(2)) + .unwrap(); + + let vring = VringRwLock::new(mem.clone(), 16).unwrap(); + + // vring.set_queue_info(0x10_0000, 0x10_0000, 0x300).unwrap(); + vring.set_queue_size(16); + vring + .set_queue_info( + queue.desc_table_addr().0, + queue.avail_addr().0, + queue.used_addr().0, + ) + .unwrap(); + vring.set_queue_ready(true); + + let mut backend = VhostUserScsiBackend::new(); + backend.update_memory(mem.clone()).unwrap(); + + (backend, vring, mem) + } + + fn get_response(mem: &GuestMemoryAtomic) -> VirtioScsiCmdResp { + mem.memory() + .read_obj::(GuestAddress(0x20_0000)) + .expect("Unable to read response from memory") + } + + fn create_lun_specifier(target: u8, lun: u16) -> [u8; 8] { + let lun = lun.to_le_bytes(); + + [ + 0x1, + target, + lun[0] | VirtioScsiLun::FLAT_SPACE_ADDRESSING_METHOD, + lun[1], + 0x0, + 0x0, + 0x0, + 0x0, + ] + } + + #[test] + fn backend_test() { + let collector = FakeTargetCommandCollector::new(); + let fake_target = Box::new(FakeTarget::new(collector.clone(), |_, _| { + Ok(CmdOutput::ok()) + })); + + let req = VirtioScsiCmdReq(virtio_scsi_cmd_req { + lun: create_lun_specifier(0, 0), + tag: 0, + task_attr: 0, + prio: 0, + crn: 0, + cdb: [0; CDB_SIZE], + }); + + let (mut backend, vring, mem) = setup(req); + backend.add_target(fake_target); + backend.process_request_queue(&vring).unwrap(); + + let res = get_response(&mem); + assert_eq!(res.0.response, VIRTIO_SCSI_S_OK as u8); + + let collector = collector.lock().unwrap(); + assert_eq!( + collector.received_commands.len(), + 1, + "expect one command to be passed to Target" + ); + } + + #[test] + fn backend_error_reporting_test() { + let collector = FakeTargetCommandCollector::new(); + let fake_target = Box::new(FakeTarget::new(collector.clone(), |_, _| { + Err(crate::scsi::CmdError::DataIn(io::Error::new( + io::ErrorKind::Other, + "internal error", + ))) + })); + + let req = VirtioScsiCmdReq(virtio_scsi_cmd_req { + lun: create_lun_specifier(0, 0), + tag: 0, + task_attr: 0, + prio: 0, + crn: 0, + cdb: [0; CDB_SIZE], + }); + + let (mut backend, vring, mem) = setup(req); + backend.add_target(fake_target); + backend.process_request_queue(&vring).unwrap(); + + let res = get_response(&mem); + assert_eq!(res.0.response, VIRTIO_SCSI_S_FAILURE as u8); + + let collector = collector.lock().unwrap(); + assert_eq!( + collector.received_commands.len(), + 1, + "expect one command to be passed to Target" + ); + } + + #[test] + fn test_command_to_unknown_lun() { + let collector = FakeTargetCommandCollector::new(); + + let req = VirtioScsiCmdReq(virtio_scsi_cmd_req { + lun: create_lun_specifier(0, 0), + tag: 0, + task_attr: 0, + prio: 0, + crn: 0, + cdb: [0; CDB_SIZE], + }); + + let (mut backend, vring, mem) = setup(req); + backend.process_request_queue(&vring).unwrap(); + + let res = get_response(&mem); + assert_eq!(res.0.response, VIRTIO_SCSI_S_BAD_TARGET as u8); + + let collector = collector.lock().unwrap(); + assert_eq!( + collector.received_commands.len(), + 0, + "expect no command to make it to the target" + ); + } + + #[test] + fn test_broken_read_descriptor() { + let collector = FakeTargetCommandCollector::new(); + + let broken_req = [0u8; 1]; // single byte request + + let (mut backend, vring, mem) = setup(broken_req); + backend.process_request_queue(&vring).unwrap(); + + let res = get_response(&mem); + assert_eq!(res.0.response, VIRTIO_SCSI_S_FAILURE as u8); + + let collector = collector.lock().unwrap(); + assert_eq!( + collector.received_commands.len(), + 0, + "expect no command to make it to the target" + ); + } +} diff --git a/crates/scsi/src/virtio.rs b/crates/scsi/src/virtio.rs index 2f2ecb3..423c0ab 100644 --- a/crates/scsi/src/virtio.rs +++ b/crates/scsi/src/virtio.rs @@ -311,3 +311,60 @@ where } } } + +#[cfg(test)] +pub(crate) mod tests { + use virtio_bindings::virtio_scsi::{virtio_scsi_cmd_req, virtio_scsi_cmd_resp}; + use virtio_queue::{mock::MockSplitQueue, Descriptor}; + use vm_memory::{ByteValued, GuestAddress, GuestMemoryMmap}; + + use super::*; + + #[derive(Debug, Default, Clone, Copy)] + #[repr(transparent)] + pub(crate) struct VirtioScsiCmdReq(pub virtio_scsi_cmd_req); + /// SAFETY: struct is a transparent wrapper around the request + /// which can be read from a byte array + unsafe impl ByteValued for VirtioScsiCmdReq {} + + #[derive(Debug, Default, Clone, Copy)] + #[repr(transparent)] + pub(crate) struct VirtioScsiCmdResp(pub virtio_scsi_cmd_resp); + /// SAFETY: struct is a transparent wrapper around the response + /// which can be read from a byte array + unsafe impl ByteValued for VirtioScsiCmdResp {} + + pub(crate) fn report_luns_command() -> VirtioScsiCmdReq { + VirtioScsiCmdReq(virtio_scsi_cmd_req { + lun: REPORT_LUNS, + tag: 0, + task_attr: 0, + prio: 0, + crn: 0, + cdb: [0; CDB_SIZE], + }) + } + + #[test] + fn test_parse_request() { + let mem: GuestMemoryMmap = + GuestMemoryMmap::from_ranges(&[(GuestAddress(0), 0x1000_0000)]).unwrap(); + + // The `build_desc_chain` function will populate the `NEXT` related flags and field. + let v = vec![ + // A device-writable request header descriptor. + Descriptor::new(0x10_0000, 0x100, 0, 0), + ]; + + let req = report_luns_command(); + mem.write_obj(req, GuestAddress(0x10_0000)) + .expect("writing to succeed"); + + let queue = MockSplitQueue::new(&mem, 16); + let chain = queue.build_desc_chain(&v).unwrap(); + + let mut chain = DescriptorChainReader::new(chain.clone()); + let req = Request::parse(&mut chain).expect("request failed to parse"); + assert_eq!(req.lun, VirtioScsiLun::ReportLuns); + } +} From ad82d4592852ea665414ab4f27bb7149b914771b Mon Sep 17 00:00:00 2001 From: Gaelan Steele Date: Tue, 20 Jul 2021 01:39:22 -0700 Subject: [PATCH 047/189] scsi: Add documentation Co-developed-by: Erik Schilling Signed-off-by: Erik Schilling Signed-off-by: Gaelan Steele --- crates/scsi/ARCHITECTURE.md | 39 ++++++++++++++++++++++++++++++ crates/scsi/README.md | 48 +++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 crates/scsi/ARCHITECTURE.md create mode 100644 crates/scsi/README.md diff --git a/crates/scsi/ARCHITECTURE.md b/crates/scsi/ARCHITECTURE.md new file mode 100644 index 0000000..2a2f7fa --- /dev/null +++ b/crates/scsi/ARCHITECTURE.md @@ -0,0 +1,39 @@ +# vhost-user-scsi architecture + +Rough outline of the different pieces and how they fit together: + +## `scsi/mod.rs` + +This defines the `Target` trait, which represents a SCSI target. The code in +this file is independent from: + +- A particular SCSI implementation: Currently, we have one implementation of + `Target`, which emulates the SCSI commands itself; but future implementations + could provide pass-through to an iSCSI target or SCSI devices attached to the + host. +- A particular SCSI transport: Nothing in `src/scsi/*` knows anything about + virtio; this is helpful for maintainability, and also allows our SCSI + emulation code to be reusable as, for example, an iSCSI target. To this end, + the `Target` trait is generic over a `Read` and `Write` that it uses for SCSI + data transfer. This makes testing easy: we can just provide a `Vec` to + write into. + +## `scsi/emulation/*.rs` + +This is the SCSI emulation code, which forms the bulk of the crate. It provides +`EmulatedTarget`, an implementation of `Target`. `EmulatedTarget`, in turn, +looks at the LUN and delegates commands to an implementation of `LogicalUnit`. +In most cases, this will be `BlockDevice`; there's also `MissingLun`, which is +used for responding to commands to invalid LUNs. + +Currently, there is no separation between commands defined in the SPC standard +(commands shared by all device types) and the SBC standard (block-device +specific commands). If we ever implemented another device type (CD/DVD seems +most likely), we'd want to separate those out. + +As noted above, the emulation code knows nothing about virtio. + +## `src/{main,virtio}.rs` + +This code handles vhost-user, virtio, and virtio-scsi; it's the only part of +the crate that knows about these protocols. diff --git a/crates/scsi/README.md b/crates/scsi/README.md new file mode 100644 index 0000000..46779df --- /dev/null +++ b/crates/scsi/README.md @@ -0,0 +1,48 @@ +# vhost-user-scsi + +This is a Rust implementation of a vhost-user-scsi daemon. + +## Usage + +Run the vhost-user-scsi daemon: + +``` +vhost-user-scsi -r --socket-path /tmp/vhost-user-scsi.sock /path/to/image.raw /path/to/second-image.raw ... +``` + +Run QEMU: + +``` +qemu-system-x86_64 ... \ + -device vhost-user-scsi-pci,num_queues=1,param_change=off,chardev=vus \ + -chardev socket,id=vus,path=/tmp/vhost-user-scsi.sock \ + # must match total guest meory + -object memory-backend-memfd,id=mem,size=384M,share=on \ + -numa node,memdev=mem +``` + +## Limitations + +We are currently only supporting a single request queue and do not support +dynamic reconfiguration of LUN parameters (VIRTIO_SCSI_F_CHANGE). + +## Features + +This crate is a work-in-progress. Currently, it's possible to mount and read +up to 256 read-only raw disk images. Some features we might like to add +at some point, roughly ordered from sooner to later: + +- Write support. This should just be a matter of implementing the WRITE + command, but there's a bit of complexity around writeback caching we + need to make sure we get right. +- Support more LUNs. virtio-scsi supports up to 16384 LUNs per target. + After 256, the LUN encoding format is different; it's nothing too + complicated, but I haven't gotten around to implementing it. +- Concurrency. Currently, we process SCSI commands one at a time. Eventually, + it'd be a good idea to use threads or some fancy async/io_uring stuff to + concurrently handle multiple commands. virtio-scsi also allows for multiple + request queues, allowing the guest to submit requests from multiple cores + in parallel; we should support that. +- iSCSI passthrough. This shouldn't be too bad, but it might be a good idea + to decide on a concurrency model (threads or async) before we spend too much + time here. From 4c8a2bc3ac3bfc453455815527d775a521e80f37 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Tue, 18 Apr 2023 10:09:14 +0200 Subject: [PATCH 048/189] scsi: Advertise support for CONFIG The config that we send is based on the current QEMU defaults (as of 60ca584b8af0de525656f959991a440f8c191f12). This allows testing using Alex Bennee's vhost-user-generic series and will be required for hypervisors that do not come with the stubs that QEMU has (eg: Xen). Signed-off-by: Erik Schilling Link: https://lore.kernel.org/all/20230414160433.2096866-1-alex.bennee@linaro.org/ --- crates/scsi/src/vhu_scsi.rs | 65 +++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/crates/scsi/src/vhu_scsi.rs b/crates/scsi/src/vhu_scsi.rs index 915419e..8567654 100644 --- a/crates/scsi/src/vhu_scsi.rs +++ b/crates/scsi/src/vhu_scsi.rs @@ -1,11 +1,14 @@ // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause -use std::convert::TryFrom; +use core::slice; +use std::convert::{TryFrom, TryInto}; use std::io::{self, ErrorKind}; +use std::mem; use log::{debug, error, info, warn}; use vhost::vhost_user::{VhostUserProtocolFeatures, VhostUserVirtioFeatures}; use vhost_user_backend::{VhostUserBackendMut, VringRwLock, VringT}; +use virtio_bindings::virtio_scsi::{virtio_scsi_config, virtio_scsi_event}; use virtio_bindings::{ virtio_config::VIRTIO_F_VERSION_1, virtio_ring::{VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC}, @@ -19,6 +22,7 @@ use vmm_sys_util::{ }; use crate::scsi::Target; +use crate::virtio::CDB_SIZE; use crate::{ scsi::{self, CmdError, TaskAttr}, virtio::{self, Request, RequestParseError, Response, ResponseCode, VirtioScsiLun, SENSE_SIZE}, @@ -211,7 +215,7 @@ impl VhostUserBackendMut for VhostUserScsiBackend { } fn protocol_features(&self) -> VhostUserProtocolFeatures { - VhostUserProtocolFeatures::MQ + VhostUserProtocolFeatures::MQ | VhostUserProtocolFeatures::CONFIG } fn set_event_idx(&mut self, enabled: bool) { @@ -267,9 +271,38 @@ impl VhostUserBackendMut for VhostUserScsiBackend { Ok(false) } - fn get_config(&self, _offset: u32, _size: u32) -> Vec { - // QEMU handles config space itself - panic!("Access to configuration space is not supported."); + fn get_config(&self, offset: u32, size: u32) -> Vec { + let config = virtio_scsi_config { + num_queues: 1, + seg_max: 128 - 2, + max_sectors: 0xFFFF, + cmd_per_lun: 128, + event_info_size: mem::size_of::() + .try_into() + .expect("event info size should fit 32bit"), + sense_size: SENSE_SIZE.try_into().expect("SENSE_SIZE should fit 32bit"), + cdb_size: CDB_SIZE.try_into().expect("CDB_SIZE should fit 32bit"), + max_channel: 0, + max_target: 255, + max_lun: u32::from(!u16::from(VirtioScsiLun::ADDRESS_METHOD_PATTERN) << 8 | 0xff), + }; + + // SAFETY: + // Pointer is aligned (points to start of struct), valid and we only + // access up to the size of the struct. + let config_slice = unsafe { + slice::from_raw_parts( + &config as *const virtio_scsi_config as *const u8, + mem::size_of::(), + ) + }; + + config_slice + .iter() + .skip(offset as usize) + .take(size as usize) + .cloned() + .collect() } fn set_config(&mut self, _offset: u32, _buf: &[u8]) -> std::result::Result<(), std::io::Error> { @@ -294,7 +327,8 @@ mod tests { use virtio_bindings::{ virtio_ring::VRING_DESC_F_WRITE, virtio_scsi::{ - virtio_scsi_cmd_req, VIRTIO_SCSI_S_BAD_TARGET, VIRTIO_SCSI_S_FAILURE, VIRTIO_SCSI_S_OK, + virtio_scsi_cmd_req, virtio_scsi_config, VIRTIO_SCSI_S_BAD_TARGET, + VIRTIO_SCSI_S_FAILURE, VIRTIO_SCSI_S_OK, }, }; use virtio_queue::{mock::MockSplitQueue, Descriptor}; @@ -565,4 +599,23 @@ mod tests { "expect no command to make it to the target" ); } + + #[test] + fn test_reading_config() { + let backend = VhostUserScsiBackend::new(); + + // 0 len slice + assert_eq!(vec![0_u8; 0], backend.get_config(0, 0)); + // overly long slice + assert_eq!( + std::mem::size_of::(), + backend.get_config(0, 2000).len() + ); + // subslice + assert_eq!(1, backend.get_config(4, 1).len()); + // overly long subslice + assert_eq!(28, backend.get_config(8, 10000).len()); + // offset after end + assert_eq!(0, backend.get_config(100000, 10).len()); + } } From 3adff11c946eecc5f10505c8477ef9e7ac11efac Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Fri, 5 May 2023 15:13:26 +0200 Subject: [PATCH 049/189] scsi: add support for WRITE(10) This adds write support. Being very similar to READ(10) in structure, much of the code is very similar. Signed-off-by: Erik Schilling --- crates/scsi/src/main.rs | 8 +- .../scsi/src/scsi/emulation/block_device.rs | 72 +++++++++++++- crates/scsi/src/scsi/emulation/command.rs | 41 ++++++++ crates/scsi/src/scsi/emulation/tests/mod.rs | 94 ++++++++++++++++++- 4 files changed, 211 insertions(+), 4 deletions(-) diff --git a/crates/scsi/src/main.rs b/crates/scsi/src/main.rs index e48959f..9e7813f 100644 --- a/crates/scsi/src/main.rs +++ b/crates/scsi/src/main.rs @@ -70,7 +70,13 @@ fn create_backend(args: &ScsiArgs) -> Result { } for image in &args.images { - let mut dev = BlockDevice::new(FileBackend::new(File::open(image).expect("Opening image"))); + let mut dev = BlockDevice::new(FileBackend::new( + File::options() + .read(true) + .write(true) + .open(image) + .expect("Opening image"), + )); dev.set_write_protected(args.read_only); dev.set_solid_state(if args.solid_state { MediumRotationRate::NonRotating diff --git a/crates/scsi/src/scsi/emulation/block_device.rs b/crates/scsi/src/scsi/emulation/block_device.rs index 66777e7..b21c833 100644 --- a/crates/scsi/src/scsi/emulation/block_device.rs +++ b/crates/scsi/src/scsi/emulation/block_device.rs @@ -98,6 +98,7 @@ impl Mul for BlockOffset { pub(crate) trait BlockDeviceBackend: Send + Sync { fn read_exact_at(&mut self, buf: &mut [u8], offset: ByteOffset) -> io::Result<()>; + fn write_exact_at(&mut self, buf: &[u8], offset: ByteOffset) -> io::Result<()>; fn size_in_blocks(&mut self) -> io::Result; fn block_size(&self) -> BlockSize; fn sync(&mut self) -> io::Result<()>; @@ -122,6 +123,10 @@ impl BlockDeviceBackend for FileBackend { self.file.read_exact_at(buf, u64::from(offset)) } + fn write_exact_at(&mut self, buf: &[u8], offset: ByteOffset) -> io::Result<()> { + self.file.write_all_at(buf, u64::from(offset)) + } + fn size_in_blocks(&mut self) -> io::Result { let len = ByteOffset::from(self.file.metadata()?.len()); assert!(u64::from(len) % NonZeroU64::from(self.block_size.0) == 0); @@ -168,6 +173,25 @@ impl BlockDevice { Ok(ret) } + fn write_blocks( + &mut self, + lba: BlockOffset, + blocks: BlockOffset, + reader: &mut dyn Read, + ) -> io::Result<()> { + // TODO: Avoid the copies here. + let mut buf = vec![ + 0; + usize::try_from(u64::from(blocks * self.backend.block_size())) + .expect("block length in bytes should fit usize") + ]; + reader.read_exact(&mut buf)?; + self.backend + .write_exact_at(&buf, lba * self.backend.block_size())?; + + Ok(()) + } + pub fn set_write_protected(&mut self, wp: bool) { self.write_protected = wp; } @@ -181,7 +205,7 @@ impl LogicalUnit for BlockDevice { fn execute_command( &mut self, data_in: &mut SilentlyTruncate<&mut dyn Write>, - _data_out: &mut dyn Read, + data_out: &mut dyn Read, req: LunRequest, command: LunSpecificCommand, ) -> Result { @@ -415,6 +439,52 @@ impl LogicalUnit for BlockDevice { } } } + LunSpecificCommand::Write10 { + dpo, + fua, + lba, + transfer_length, + } => { + if dpo { + // DPO is just a hint that the guest probably won't access + // this any time soon, so we can ignore it + debug!("Silently ignoring DPO flag"); + } + + let size = match self.backend.size_in_blocks() { + Ok(size) => size, + Err(e) => { + error!("Error getting image size for read: {}", e); + return Ok(CmdOutput::check_condition(sense::TARGET_FAILURE)); + } + }; + + let lba = BlockOffset(lba.into()); + let transfer_length = BlockOffset(transfer_length.into()); + + if lba + transfer_length > size { + return Ok(CmdOutput::check_condition( + sense::LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, + )); + } + + let write_result = self.write_blocks(lba, transfer_length, data_out); + + if fua { + if let Err(e) = self.backend.sync() { + error!("Error syncing file: {}", e); + return Ok(CmdOutput::check_condition(sense::TARGET_FAILURE)); + } + } + + match write_result { + Ok(()) => Ok(CmdOutput::ok()), + Err(e) => { + error!("Error writing to block device: {}", e); + Ok(CmdOutput::check_condition(sense::TARGET_FAILURE)) + } + } + } LunSpecificCommand::Inquiry(page_code) => { // top 3 bits 0: peripheral device code = exists and ready // bottom 5 bits 0: device type = block device diff --git a/crates/scsi/src/scsi/emulation/command.rs b/crates/scsi/src/scsi/emulation/command.rs index 5572174..bdfed33 100644 --- a/crates/scsi/src/scsi/emulation/command.rs +++ b/crates/scsi/src/scsi/emulation/command.rs @@ -174,6 +174,15 @@ pub(crate) enum LunSpecificCommand { lba: u32, transfer_length: u16, }, + Write10 { + /// Disable page out (i.e. hint that this page won't be accessed again + /// soon, so we shouldn't bother caching it) + dpo: bool, + /// Force unit access (i.e. bypass cache) + fua: bool, + lba: u32, + transfer_length: u16, + }, ReadCapacity10, ReadCapacity16, ReportSupportedOperationCodes { @@ -202,6 +211,7 @@ pub(crate) enum CommandType { ReportSupportedOperationCodes, RequestSense, TestUnitReady, + Write10, } pub(crate) const OPCODES: &[(CommandType, (u8, Option))] = &[ @@ -211,6 +221,7 @@ pub(crate) const OPCODES: &[(CommandType, (u8, Option))] = &[ (CommandType::ModeSense6, (0x1a, None)), (CommandType::ReadCapacity10, (0x25, None)), (CommandType::Read10, (0x28, None)), + (CommandType::Write10, (0x2a, None)), (CommandType::ReadCapacity16, (0x9e, Some(0x10))), (CommandType::ReportLuns, (0xa0, None)), ( @@ -374,6 +385,18 @@ impl CommandType { 0b1111_1111, 0b0000_0100, ], + Self::Write10 => &[ + 0x2A, + 0b1111_1100, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b0011_1111, + 0b1111_1111, + 0b1111_1111, + 0b0000_0100, + ], Self::Inquiry => &[ 0x12, 0b0000_0001, @@ -514,6 +537,24 @@ impl Cdb { naca: (cdb[9] & 0b0000_0100) != 0, }) } + CommandType::Write10 => { + if cdb[1] & 0b1110_0000 != 0 { + // Feature (protection) that we don't + // support; the standard says to respond with INVALID + // FIELD IN CDB for these if unsupported + return Err(ParseError::InvalidField); + } + Ok(Self { + command: Command::LunSpecificCommand(LunSpecificCommand::Write10 { + dpo: cdb[1] & 0b0001_0000 != 0, + fua: cdb[1] & 0b0000_1000 != 0, + lba: u32::from_be_bytes(cdb[2..6].try_into().unwrap()), + transfer_length: u16::from_be_bytes(cdb[7..9].try_into().unwrap()), + }), + allocation_length: None, + naca: (cdb[9] & 0b0000_0100) != 0, + }) + } CommandType::ReadCapacity10 => Ok(Self { command: Command::LunSpecificCommand(LunSpecificCommand::ReadCapacity10), allocation_length: None, diff --git a/crates/scsi/src/scsi/emulation/tests/mod.rs b/crates/scsi/src/scsi/emulation/tests/mod.rs index b112ba0..f23e01a 100644 --- a/crates/scsi/src/scsi/emulation/tests/mod.rs +++ b/crates/scsi/src/scsi/emulation/tests/mod.rs @@ -6,12 +6,18 @@ mod bad_lun; mod generic; mod report_supported_operation_codes; -use std::{fs::File, io::Write}; +use std::{ + fs::File, + io::Write, + sync::{Arc, Mutex}, +}; use tempfile::tempfile; use super::{ - block_device::{BlockDevice, FileBackend}, + block_device::{ + BlockDevice, BlockDeviceBackend, BlockOffset, BlockSize, ByteOffset, FileBackend, + }, target::EmulatedTarget, }; use crate::scsi::{ @@ -19,6 +25,51 @@ use crate::scsi::{ CmdOutput, Request, Target, TaskAttr, }; +#[derive(Clone)] +struct TestBackend { + data: Arc>, +} + +impl TestBackend { + fn new() -> Self { + TestBackend { + data: Arc::new(Mutex::new([0; 512 * 16])), + } + } +} + +impl BlockDeviceBackend for TestBackend { + fn read_exact_at(&mut self, buf: &mut [u8], offset: ByteOffset) -> std::io::Result<()> { + let data = self.data.lock().unwrap(); + + let offset = usize::try_from(u64::from(offset)).expect("offset should fit usize"); + buf.copy_from_slice(&data[offset..(offset + buf.len())]); + Ok(()) + } + + fn write_exact_at(&mut self, buf: &[u8], offset: ByteOffset) -> std::io::Result<()> { + let mut data = self.data.lock().unwrap(); + + let offset = usize::try_from(u64::from(offset)).expect("offset should fit usize"); + data[offset..(offset + buf.len())].copy_from_slice(buf); + Ok(()) + } + + fn size_in_blocks(&mut self) -> std::io::Result { + Ok(ByteOffset::from( + u64::try_from(self.data.lock().unwrap().len()).expect("size_in_blocks should fit u64"), + ) / self.block_size()) + } + + fn block_size(&self) -> BlockSize { + BlockSize::try_from(512).expect("512 should be a valid BlockSize") + } + + fn sync(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + fn null_image() -> FileBackend { FileBackend::new(File::open("/dev/null").unwrap()) } @@ -103,6 +154,10 @@ fn do_command_fail(target: &mut EmulatedTarget, cdb: &[u8], expected_error: Sens do_command_fail_lun(target, 0, cdb, expected_error); } +fn block_size_512() -> BlockSize { + BlockSize::try_from(512).expect("512 should be a valid block_size") +} + #[test] fn test_test_unit_ready() { let mut target = EmulatedTarget::new(); @@ -237,6 +292,41 @@ fn test_read_10_cross_out() { ); } +#[test] +fn test_write_10() { + let mut target = EmulatedTarget::new(); + let mut backend = TestBackend::new(); + let dev = BlockDevice::new(backend.clone()); + target.add_lun(Box::new(dev)); + + // TODO: this test relies on the default logical block size of 512. We should + // make that explicit. + + { + let data_out = [b'w'; 512]; + + do_command_in( + &mut target, + &[ + 0x2a, // WRITE (10) + 0, // flags + 0, 0, 0, 5, // LBA: 5 + 0, // reserved, group # + 0, 1, // transfer length: 1 + 0, // control + ], + &data_out, + &[], + ); + + let mut buf = [0_u8; 512]; + backend + .read_exact_at(&mut buf, BlockOffset::from(5) * block_size_512()) + .expect("Reading should work"); + assert_eq!(data_out, buf); + } +} + #[test] fn test_read_capacity_10() { let mut target = EmulatedTarget::new(); From 07b103b4d6cb6e6b5fcc82abaa2af27c276bf082 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Fri, 5 May 2023 15:16:22 +0200 Subject: [PATCH 050/189] scsi: add support for WRITE SAME(16) WRITE SAME allows writing a block for a repeated number of times. Mostly, it can also be used to deallocate parts of the block device (the fstrim functionality uses this). We do not support that aspect yet. Instead, we will just stupidly repeat the pattern as many times as we are told. A future, smarter implementation could just punch a hole into the backend instead of filling it with zeros. Signed-off-by: Erik Schilling --- .../scsi/src/scsi/emulation/block_device.rs | 66 +++++++++++++++++++ crates/scsi/src/scsi/emulation/command.rs | 43 ++++++++++++ crates/scsi/src/scsi/emulation/tests/mod.rs | 46 +++++++++++++ 3 files changed, 155 insertions(+) diff --git a/crates/scsi/src/scsi/emulation/block_device.rs b/crates/scsi/src/scsi/emulation/block_device.rs index b21c833..117c66c 100644 --- a/crates/scsi/src/scsi/emulation/block_device.rs +++ b/crates/scsi/src/scsi/emulation/block_device.rs @@ -192,6 +192,20 @@ impl BlockDevice { Ok(()) } + fn write_same_block( + &mut self, + lba_start: BlockOffset, + block_count: BlockOffset, + buf: &[u8], + ) -> io::Result<()> { + let block_size = self.backend.block_size(); + for lba in u64::from(lba_start)..u64::from(lba_start + block_count) { + let lba = BlockOffset(lba); + self.backend.write_exact_at(buf, lba * block_size)?; + } + Ok(()) + } + pub fn set_write_protected(&mut self, wp: bool) { self.write_protected = wp; } @@ -485,6 +499,58 @@ impl LogicalUnit for BlockDevice { } } } + LunSpecificCommand::WriteSame16 { + lba, + number_of_logical_blocks, + anchor, + } => { + // We do not support block provisioning + if anchor { + return Ok(CmdOutput::check_condition(sense::INVALID_FIELD_IN_CDB)); + } + + // This command can be used to unmap/discard a region of blocks... + // TODO: Do something smarter and punch holes into the backend, + // for now we will just write A LOT of zeros in a very inefficient way. + + let size = match self.backend.size_in_blocks() { + Ok(size) => size, + Err(e) => { + error!("Error getting image size for read: {}", e); + return Ok(CmdOutput::check_condition(sense::UNRECOVERED_READ_ERROR)); + } + }; + + let lba = BlockOffset(lba); + let number_of_logical_blocks = BlockOffset(number_of_logical_blocks.into()); + + if lba + number_of_logical_blocks > size { + return Ok(CmdOutput::check_condition( + sense::LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE, + )); + } + + let mut buf = vec![ + 0; + usize::try_from(u32::from(self.backend.block_size())) + .expect("block_size should fit usize") + ]; + let read_result = data_out.read_exact(&mut buf); + if let Err(e) = read_result { + error!("Error reading from data_out: {}", e); + return Ok(CmdOutput::check_condition(sense::TARGET_FAILURE)); + } + + let write_result = self.write_same_block(lba, number_of_logical_blocks, &buf); + + match write_result { + Ok(()) => Ok(CmdOutput::ok()), + Err(e) => { + error!("Error writing to block device: {}", e); + Ok(CmdOutput::check_condition(sense::TARGET_FAILURE)) + } + } + } LunSpecificCommand::Inquiry(page_code) => { // top 3 bits 0: peripheral device code = exists and ready // bottom 5 bits 0: device type = block device diff --git a/crates/scsi/src/scsi/emulation/command.rs b/crates/scsi/src/scsi/emulation/command.rs index bdfed33..39fed99 100644 --- a/crates/scsi/src/scsi/emulation/command.rs +++ b/crates/scsi/src/scsi/emulation/command.rs @@ -183,6 +183,11 @@ pub(crate) enum LunSpecificCommand { lba: u32, transfer_length: u16, }, + WriteSame16 { + lba: u64, + number_of_logical_blocks: u32, + anchor: bool, + }, ReadCapacity10, ReadCapacity16, ReportSupportedOperationCodes { @@ -212,6 +217,7 @@ pub(crate) enum CommandType { RequestSense, TestUnitReady, Write10, + WriteSame16, } pub(crate) const OPCODES: &[(CommandType, (u8, Option))] = &[ @@ -222,6 +228,7 @@ pub(crate) const OPCODES: &[(CommandType, (u8, Option))] = &[ (CommandType::ReadCapacity10, (0x25, None)), (CommandType::Read10, (0x28, None)), (CommandType::Write10, (0x2a, None)), + (CommandType::WriteSame16, (0x93, None)), (CommandType::ReadCapacity16, (0x9e, Some(0x10))), (CommandType::ReportLuns, (0xa0, None)), ( @@ -397,6 +404,24 @@ impl CommandType { 0b1111_1111, 0b0000_0100, ], + Self::WriteSame16 => &[ + 0x93, + 0b1111_1001, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b0011_1111, + 0b0000_0100, + ], Self::Inquiry => &[ 0x12, 0b0000_0001, @@ -555,6 +580,24 @@ impl Cdb { naca: (cdb[9] & 0b0000_0100) != 0, }) } + CommandType::WriteSame16 => { + if cdb[1] & 0b1110_0001 != 0 { + warn!("Unsupported field in WriteSame16"); + // We neither support protections nor logical block provisioning + return Err(ParseError::InvalidField); + } + Ok(Self { + command: Command::LunSpecificCommand(LunSpecificCommand::WriteSame16 { + lba: u64::from_be_bytes(cdb[2..10].try_into().expect("lba should fit u64")), + number_of_logical_blocks: u32::from_be_bytes( + cdb[10..14].try_into().expect("block count should fit u32"), + ), + anchor: (cdb[1] & 0b0001_0000) != 0, + }), + allocation_length: None, + naca: (cdb[15] & 0b0000_0100) != 0, + }) + } CommandType::ReadCapacity10 => Ok(Self { command: Command::LunSpecificCommand(LunSpecificCommand::ReadCapacity10), allocation_length: None, diff --git a/crates/scsi/src/scsi/emulation/tests/mod.rs b/crates/scsi/src/scsi/emulation/tests/mod.rs index f23e01a..8e8ffc1 100644 --- a/crates/scsi/src/scsi/emulation/tests/mod.rs +++ b/crates/scsi/src/scsi/emulation/tests/mod.rs @@ -327,6 +327,52 @@ fn test_write_10() { } } +#[test] +fn test_write_same_16() { + let mut target = EmulatedTarget::new(); + let mut backend = TestBackend::new(); + let dev = BlockDevice::new(backend.clone()); + target.add_lun(Box::new(dev)); + + // TODO: this test relies on the default logical block size of 512. We should + // make that explicit. + + backend + .write_exact_at(&[0xff; 512 * 6], BlockOffset::from(5) * block_size_512()) + .expect("Write should succeed"); + + let data_out = [0_u8; 512]; + + do_command_in( + &mut target, + &[ + 0x93, // WRITE SAME (16) + 0, // flags + 0, 0, 0, 0, 0, 0, 0, 5, // LBA: 5 + 0, 0, 0, 5, // tnumber of blocks: 5 + 0, // reserved, group # + 0, // control + ], + &data_out, + &[], + ); + + let mut buf = [0_u8; 512 * 5]; + backend + .read_exact_at(&mut buf, BlockOffset::from(5) * block_size_512()) + .expect("Reading should work"); + assert_eq!([0_u8; 512 * 5], buf, "5 sectors should have been zero'd"); + + let mut buf = [0_u8; 512]; + backend + .read_exact_at(&mut buf, BlockOffset::from(10) * block_size_512()) + .expect("Reading should work"); + assert_eq!( + [0xff_u8; 512], buf, + "sector after write should be left untouched" + ); +} + #[test] fn test_read_capacity_10() { let mut target = EmulatedTarget::new(); From a6382dd937dc7a09819dc96a7cb6d8cdd9281891 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Fri, 5 May 2023 15:23:23 +0200 Subject: [PATCH 051/189] scsi: add support for SYNCHRONIZE CACHE(10) While the command also allows syncing individual regions of a LUN, we do not support that here and simply sync the entire file. Signed-off-by: Erik Schilling --- .../scsi/src/scsi/emulation/block_device.rs | 10 ++++++++++ crates/scsi/src/scsi/emulation/command.rs | 20 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/crates/scsi/src/scsi/emulation/block_device.rs b/crates/scsi/src/scsi/emulation/block_device.rs index 117c66c..7ac4884 100644 --- a/crates/scsi/src/scsi/emulation/block_device.rs +++ b/crates/scsi/src/scsi/emulation/block_device.rs @@ -763,6 +763,16 @@ impl LogicalUnit for BlockDevice { } } } + LunSpecificCommand::SynchronizeCache10 => { + // While SCSI allows just syncing a range, we just sync the entire file + match self.backend.sync() { + Ok(()) => Ok(CmdOutput::ok()), + Err(e) => { + error!("Error syncing block device: {}", e); + Ok(CmdOutput::check_condition(sense::TARGET_FAILURE)) + } + } + } } } } diff --git a/crates/scsi/src/scsi/emulation/command.rs b/crates/scsi/src/scsi/emulation/command.rs index 39fed99..43cb0b4 100644 --- a/crates/scsi/src/scsi/emulation/command.rs +++ b/crates/scsi/src/scsi/emulation/command.rs @@ -197,6 +197,7 @@ pub(crate) enum LunSpecificCommand { }, RequestSense(SenseFormat), TestUnitReady, + SynchronizeCache10, } #[derive(Debug)] @@ -218,6 +219,7 @@ pub(crate) enum CommandType { TestUnitReady, Write10, WriteSame16, + SynchronizeCache10, } pub(crate) const OPCODES: &[(CommandType, (u8, Option))] = &[ @@ -228,6 +230,7 @@ pub(crate) const OPCODES: &[(CommandType, (u8, Option))] = &[ (CommandType::ReadCapacity10, (0x25, None)), (CommandType::Read10, (0x28, None)), (CommandType::Write10, (0x2a, None)), + (CommandType::SynchronizeCache10, (0x35, None)), (CommandType::WriteSame16, (0x93, None)), (CommandType::ReadCapacity16, (0x9e, Some(0x10))), (CommandType::ReportLuns, (0xa0, None)), @@ -444,6 +447,18 @@ impl CommandType { 0b0000_0000, 0b0000_0100, ], + Self::SynchronizeCache10 => &[ + 0x53, + 0b0000_0010, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + 0b0011_1111, + 0b1111_1111, + 0b1111_1111, + 0b0000_0100, + ], } } } @@ -598,6 +613,11 @@ impl Cdb { naca: (cdb[15] & 0b0000_0100) != 0, }) } + CommandType::SynchronizeCache10 => Ok(Self { + command: Command::LunSpecificCommand(LunSpecificCommand::SynchronizeCache10), + allocation_length: None, + naca: (cdb[9] & 0b0000_0100) != 0, + }), CommandType::ReadCapacity10 => Ok(Self { command: Command::LunSpecificCommand(LunSpecificCommand::ReadCapacity10), allocation_length: None, From 8bb862d41545bcd659aaaa2f7dad5cddf9e834ac Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Thu, 11 May 2023 10:38:41 +0200 Subject: [PATCH 052/189] scsi: add some helper scripts for testing This provides some tooling for running blktests. The README.md contains documentation about the architecture. I am seeing some race-conditions that sometimes lead to boot freezes [1], so this is not really ready for automatic evaluation during a CI pipeline. [1] https://linaro.atlassian.net/browse/ORKO-37 Signed-off-by: Erik Schilling Link: https://linaro.atlassian.net/browse/ORKO-17 --- crates/scsi/test/.containerignore | 2 ++ crates/scsi/test/.gitignore | 2 ++ crates/scsi/test/Containerfile | 11 ++++++ crates/scsi/test/README.md | 28 +++++++++++++++ crates/scsi/test/invoke-test.sh | 20 +++++++++++ crates/scsi/test/start-test.sh | 60 +++++++++++++++++++++++++++++++ crates/scsi/test/test-script.sh | 10 ++++++ 7 files changed, 133 insertions(+) create mode 100644 crates/scsi/test/.containerignore create mode 100644 crates/scsi/test/.gitignore create mode 100644 crates/scsi/test/Containerfile create mode 100644 crates/scsi/test/README.md create mode 100755 crates/scsi/test/invoke-test.sh create mode 100755 crates/scsi/test/start-test.sh create mode 100755 crates/scsi/test/test-script.sh diff --git a/crates/scsi/test/.containerignore b/crates/scsi/test/.containerignore new file mode 100644 index 0000000..d26621d --- /dev/null +++ b/crates/scsi/test/.containerignore @@ -0,0 +1,2 @@ +results/ +test-data/ \ No newline at end of file diff --git a/crates/scsi/test/.gitignore b/crates/scsi/test/.gitignore new file mode 100644 index 0000000..d26621d --- /dev/null +++ b/crates/scsi/test/.gitignore @@ -0,0 +1,2 @@ +results/ +test-data/ \ No newline at end of file diff --git a/crates/scsi/test/Containerfile b/crates/scsi/test/Containerfile new file mode 100644 index 0000000..6abc276 --- /dev/null +++ b/crates/scsi/test/Containerfile @@ -0,0 +1,11 @@ +FROM fedora:39 +RUN dnf install --quiet --assumeyes \ + /usr/bin/qemu-system-x86_64 \ + /usr/bin/qemu-img \ + /usr/bin/virt-sysprep \ + /usr/bin/ssh-keygen \ + /usr/bin/ssh \ + /usr/sbin/libvirtd \ + wget \ + && dnf clean all +VOLUME /tests/ diff --git a/crates/scsi/test/README.md b/crates/scsi/test/README.md new file mode 100644 index 0000000..254d4e2 --- /dev/null +++ b/crates/scsi/test/README.md @@ -0,0 +1,28 @@ +# Testing tools + +This folder contains some tooling for tests + +## Prerequisites + +For running these tests, you need a KVM enabled x86_64 machine and `podman`. + +vhost-user-scsi must have been built already. + +## Performed tests + +Right now, the test harness will only run +[blktests](https://github.com/osandov/blktests) against the target device +(these tests are probably testing the guest kernel more than the actual +device). + +## Test execution + +Triggering the build of the necessary container images and invoking the tests +is done by calling `./invoke-test.sh`. + +That will build the `Containerfile`, launch a container and invoke +`./start-test.sh` inside of the container. That will download a Fedora cloud +image, launch the daemon, launch QEMU, waits until it is up and triggers the +test execution. + +Results will be downloaded into a timestamped folder under `results/`. diff --git a/crates/scsi/test/invoke-test.sh b/crates/scsi/test/invoke-test.sh new file mode 100755 index 0000000..34e48ea --- /dev/null +++ b/crates/scsi/test/invoke-test.sh @@ -0,0 +1,20 @@ +#!/bin/bash -xe + +cd $(dirname "$0") + +DAEMON_BINARY="$PWD/../../../target/debug/vhost-device-scsi" + +if [[ ! -e "$DAEMON_BINARY" ]] +then + echo "Unable to find \"$DAEMON_BINARY\". Did you run cargo build?" + exit 1 +fi + +TAG_NAME=vhost-device-scsi-test-env +podman build -t "$TAG_NAME" . +podman run \ + -v /dev/kvm:/dev/kvm \ + --security-opt label=disable \ + -v "$DAEMON_BINARY":/usr/local/bin/vhost-device-scsi:ro \ + -v $PWD:/test "$TAG_NAME" \ + /test/start-test.sh diff --git a/crates/scsi/test/start-test.sh b/crates/scsi/test/start-test.sh new file mode 100755 index 0000000..6ebf8c6 --- /dev/null +++ b/crates/scsi/test/start-test.sh @@ -0,0 +1,60 @@ +#!/bin/bash -xe + +cd $(dirname "$0") + +libvirtd --daemon +virtlogd --daemon +export LIBGUESTFS_BACKEND=direct + +mkdir -p test-data/ +pushd test-data + IMAGE=Fedora-Cloud-Base-38-1.6.x86_64.qcow2 + test -e "$IMAGE" || wget --quiet "https://download.fedoraproject.org/pub/fedora/linux/releases/38/Cloud/x86_64/images/$IMAGE" -O "$IMAGE" + qemu-img create -f qcow2 -F qcow2 -b "$PWD/$IMAGE" fedora-overlay.qcow2 + + test -e test-key-id_rsa || ssh-keygen -N "" -f test-key-id_rsa + + virt-sysprep -a fedora-overlay.qcow2 \ + --ssh-inject root:file:test-key-id_rsa.pub + + fallocate -l 5GiB big-image.img +popd + +SSH_OPTS="-i test-data/test-key-id_rsa -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o User=root -o Port=2222" + +vhost-device-scsi --socket-path /tmp/vhost-user-scsi.sock test-data/big-image.img & + +sleep 1 + +qemu-system-x86_64 \ + -enable-kvm -cpu host \ + -device virtio-net-pci,netdev=net0,mac=52:54:00:12:35:02\ + -netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23 \ + -object rng-random,filename=/dev/urandom,id=rng0 -device virtio-rng-pci,rng=rng0 \ + -hda test-data/fedora-overlay.qcow2 \ + -object memory-backend-memfd,id=mem,size=8192M,share=on \ + -numa node,memdev=mem \ + -device vhost-user-scsi-pci,num_queues=1,param_change=off,chardev=vus \ + -chardev socket,id=vus,path=/tmp/vhost-user-scsi.sock \ + -smp 4 -m 8192 \ + -serial mon:stdio \ + -display none & + + +while ! ssh $SSH_OPTS localhost echo waiting for guest to come online +do + sleep 1 +done + + +scp $SSH_OPTS test-script.sh localhost:~/ +ssh $SSH_OPTS localhost /root/test-script.sh || echo "tests failed" + +export RESULT_DIR="$PWD/results/$(date --rfc-3339=s)" +mkdir -p "$RESULT_DIR" + +scp $SSH_OPTS -r localhost:/root/blktests/results/ "$RESULT_DIR/" +ssh $SSH_OPTS localhost poweroff + +wait # wait for qemu to terminate + diff --git a/crates/scsi/test/test-script.sh b/crates/scsi/test/test-script.sh new file mode 100755 index 0000000..927aec5 --- /dev/null +++ b/crates/scsi/test/test-script.sh @@ -0,0 +1,10 @@ +#!/bin/bash -xe + +dnf install -y git make g++ fio liburing-devel blktrace + +git clone https://github.com/osandov/blktests.git +pushd blktests + echo "TEST_DEVS=(/dev/sdb)" > config + make -j $(nproc) + ./check scsi block +popd \ No newline at end of file From f50e7147dfc3a6958c6dce7601a375e30c3abd15 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Thu, 11 May 2023 10:47:34 +0200 Subject: [PATCH 053/189] scsi: mention out-of-tree fuzzing infrastructure Polishing this up for inclusion is currently not high on the priority list, but we can at least link it. Signed-off-by: Erik Schilling --- crates/scsi/test/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/scsi/test/README.md b/crates/scsi/test/README.md index 254d4e2..9322054 100644 --- a/crates/scsi/test/README.md +++ b/crates/scsi/test/README.md @@ -26,3 +26,8 @@ image, launch the daemon, launch QEMU, waits until it is up and triggers the test execution. Results will be downloaded into a timestamped folder under `results/`. + +# Other test tools + +Some quick and dirty fuzzing code is available at +https://github.com/Ablu/vhost-device/tree/scsi-fuzzing. From a4aabb15e18099b9df4b49bb55a6607b2b638f3d Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Sun, 4 Jun 2023 15:45:30 +0530 Subject: [PATCH 054/189] vsock: Add support for multiple guests Adds support for instantiating multiple `VhostUserVsockBackend`s parallely to handle multiple guests. Extends the CLI interface to accept the config for multiple VMs in addition to the yaml config file with the `--vm` argument as follows: vhost-user-vsock \ --vm guest_cid=3,socket=/tmp/vhost3.socket,uds_path=/tmp/vm3.vsock \ --vm guest_cid=4,socket=/tmp/vhost4.socket,uds_path=/tmp/vm4.vsock Signed-off-by: Priyansh Rathi --- crates/vsock/README.md | 30 ++++- crates/vsock/src/main.rs | 218 +++++++++++++++++++++++++++++----- crates/vsock/src/vhu_vsock.rs | 2 - 3 files changed, 212 insertions(+), 38 deletions(-) diff --git a/crates/vsock/README.md b/crates/vsock/README.md index 14d084b..8e594f0 100644 --- a/crates/vsock/README.md +++ b/crates/vsock/README.md @@ -41,20 +41,38 @@ the crate are split into various files as described below: Run the vhost-user-vsock device: ``` vhost-user-vsock --guest-cid= \ - --socket= - --uds-path= + --socket= \ + --uds-path= \ [--tx-buffer-size=host packets)>] - --config= +``` +or +``` +vhost-user-vsock --vm guest_cid=,socket=,uds-path=[,tx-buffer-size=host packets)>] ``` -Configuration Example +Specify the `--vm` argument multiple times to specify multiple devices like this: +``` +vhost-user-vsock \ +--vm guest-cid=3,socket=/tmp/vhost3.socket,uds-path=/tmp/vm3.vsock \ +--vm guest-cid=4,socket=/tmp/vhost4.socket,uds-path=/tmp/vm4.vsock,tx-buffer-size=32768 +``` +Or use a configuration file: +``` +vhost-user-vsock --config= +``` + +Configuration file example: ```yaml vms: - guest_cid: 3 socket: /tmp/vhost3.socket uds_path: /tmp/vm3.sock tx_buffer_size: 65536 + - guest_cid: 4 + socket: /tmp/vhost4.socket + uds_path: /tmp/vm4.sock + tx_buffer_size: 32768 ``` Run VMM (e.g. QEMU): @@ -71,11 +89,11 @@ qemu-system-x86_64 \ ## Working example ```sh -shell1$ vhost-user-vsock --guest-cid=4 --uds-path=/tmp/vm4.vsock --socket=/tmp/vhost4.socket +shell1$ vhost-user-vsock --vm guest-cid=4,uds-path=/tmp/vm4.vsock,socket=/tmp/vhost4.socket ``` or if you want to configure the TX buffer size ```sh -shell1$ vhost-user-vsock --guest-cid=4 --uds-path=/tmp/vm4.vsock --socket=/tmp/vhost4.socket --tx-buffer-size=65536 +shell1$ vhost-user-vsock --vm guest-cid=4,uds-path=/tmp/vm4.vsock,socket=/tmp/vhost4.socket,tx-buffer-size=65536 ``` ```sh diff --git a/crates/vsock/src/main.rs b/crates/vsock/src/main.rs index 3d365d1..babb12a 100644 --- a/crates/vsock/src/main.rs +++ b/crates/vsock/src/main.rs @@ -8,32 +8,61 @@ mod vhu_vsock; mod vhu_vsock_thread; mod vsock_conn; -use std::{convert::TryFrom, sync::Arc}; +use std::{convert::TryFrom, sync::Arc, thread}; -use crate::vhu_vsock::{Error, Result, VhostUserVsockBackend, VsockConfig}; +use crate::vhu_vsock::{VhostUserVsockBackend, VsockConfig}; use clap::{Args, Parser}; use log::{info, warn}; use serde::Deserialize; +use thiserror::Error as ThisError; use vhost::{vhost_user, vhost_user::Listener}; use vhost_user_backend::VhostUserDaemon; use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; -#[derive(Args, Debug, Deserialize)] +const DEFAULT_GUEST_CID: u64 = 3; +const DEFAULT_TX_BUFFER_SIZE: u32 = 64 * 1024; + +#[derive(Debug, ThisError)] +enum CliError { + #[error("No arguments provided")] + NoArgsProvided, + #[error("Failed to parse configuration file")] + ConfigParse, +} + +#[derive(Debug, ThisError)] +enum VmArgsParseError { + #[error("Bad argument")] + BadArgument, + #[error("Invalid key `{0}`")] + InvalidKey(String), + #[error("Unable to convert string to integer: {0}")] + ParseInteger(std::num::ParseIntError), + #[error("Required key `{0}` not found")] + RequiredKeyNotFound(String), +} + +#[derive(Args, Clone, Debug, Deserialize)] struct VsockParam { /// Context identifier of the guest which uniquely identifies the device for its lifetime. - #[arg(long, default_value_t = 3, conflicts_with = "config")] + #[arg( + long, + default_value_t = DEFAULT_GUEST_CID, + conflicts_with = "config", + conflicts_with = "vm" + )] guest_cid: u64, /// Unix socket to which a hypervisor connects to and sets up the control path with the device. - #[arg(long, conflicts_with = "config")] + #[arg(long, conflicts_with = "config", conflicts_with = "vm")] socket: String, /// Unix socket to which a host-side application connects to. - #[arg(long, conflicts_with = "config")] + #[arg(long, conflicts_with = "config", conflicts_with = "vm")] uds_path: String, /// The size of the buffer used for the TX virtqueue - #[clap(long, default_value_t = 64 * 1024, conflicts_with = "config")] + #[clap(long, default_value_t = DEFAULT_TX_BUFFER_SIZE, conflicts_with = "config", conflicts_with = "vm")] tx_buffer_size: u32, } @@ -43,45 +72,100 @@ struct VsockArgs { #[command(flatten)] param: Option, + /// Device parameters corresponding to a VM in the form of comma separated key=value pairs. + /// The allowed keys are: guest_cid, socket, uds_path and tx_buffer_size + /// Example: --vm guest-cid=3,socket=/tmp/vhost3.socket,uds-path=/tmp/vm3.vsock,tx-buffer-size=65536 + /// Multiple instances of this argument can be provided to configure devices for multiple guests. + #[arg(long, conflicts_with = "config", verbatim_doc_comment, value_parser = parse_vm_params)] + vm: Option>, + /// Load from a given configuration file #[arg(long)] config: Option, } +fn parse_vm_params(s: &str) -> Result { + let mut guest_cid = None; + let mut socket = None; + let mut uds_path = None; + let mut tx_buffer_size = None; + + for arg in s.trim().split(',') { + let mut parts = arg.split('='); + let key = parts.next().ok_or(VmArgsParseError::BadArgument)?; + let val = parts.next().ok_or(VmArgsParseError::BadArgument)?; + + match key { + "guest_cid" | "guest-cid" => { + guest_cid = Some(val.parse().map_err(VmArgsParseError::ParseInteger)?) + } + "socket" => socket = Some(val.to_string()), + "uds_path" | "uds-path" => uds_path = Some(val.to_string()), + "tx_buffer_size" | "tx-buffer-size" => { + tx_buffer_size = Some(val.parse().map_err(VmArgsParseError::ParseInteger)?) + } + _ => return Err(VmArgsParseError::InvalidKey(key.to_string())), + } + } + + Ok(VsockConfig::new( + guest_cid.unwrap_or(DEFAULT_GUEST_CID), + socket.ok_or_else(|| VmArgsParseError::RequiredKeyNotFound("socket".to_string()))?, + uds_path.ok_or_else(|| VmArgsParseError::RequiredKeyNotFound("uds-path".to_string()))?, + tx_buffer_size.unwrap_or(DEFAULT_TX_BUFFER_SIZE), + )) +} + impl VsockArgs { - pub fn parse_config(&self) -> Option { + pub fn parse_config(&self) -> Option, CliError>> { if let Some(c) = &self.config { let b = config::Config::builder() .add_source(config::File::new(c.as_str(), config::FileFormat::Yaml)) .build(); if let Ok(s) = b { let mut v = s.get::>("vms").unwrap(); - if v.len() == 1 { - return v.pop().map(|vm| { - VsockConfig::new(vm.guest_cid, vm.socket, vm.uds_path, vm.tx_buffer_size) - }); + if !v.is_empty() { + let parsed: Vec = v + .drain(..) + .map(|p| { + VsockConfig::new( + p.guest_cid, + p.socket.trim().to_string(), + p.uds_path.trim().to_string(), + p.tx_buffer_size, + ) + }) + .collect(); + return Some(Ok(parsed)); + } else { + return Some(Err(CliError::ConfigParse)); } + } else { + return Some(Err(CliError::ConfigParse)); } } None } } -impl TryFrom for VsockConfig { - type Error = Error; +impl TryFrom for Vec { + type Error = CliError; - fn try_from(cmd_args: VsockArgs) -> Result { + fn try_from(cmd_args: VsockArgs) -> Result { // we try to use the configuration first, if failed, then fall back to the manual settings. match cmd_args.parse_config() { - Some(c) => Ok(c), - _ => cmd_args.param.map_or(Err(Error::ConfigParse), |p| { - Ok(Self::new( - p.guest_cid, - p.socket.trim().to_string(), - p.uds_path.trim().to_string(), - p.tx_buffer_size, - )) - }), + Some(c) => c, + _ => match cmd_args.vm { + Some(v) => Ok(v), + _ => cmd_args.param.map_or(Err(CliError::NoArgsProvided), |p| { + Ok(vec![VsockConfig::new( + p.guest_cid, + p.socket.trim().to_string(), + p.uds_path.trim().to_string(), + p.tx_buffer_size, + )]) + }), + }, } } } @@ -129,11 +213,39 @@ pub(crate) fn start_backend_server(config: VsockConfig) { } } +pub(crate) fn start_backend_servers(configs: &[VsockConfig]) { + let mut handles = Vec::new(); + + for c in configs.iter() { + let config = c.clone(); + let handle = thread::Builder::new() + .name(format!("vhu-vsock-cid-{}", c.get_guest_cid())) + .spawn(move || start_backend_server(config)) + .unwrap(); + handles.push(handle); + } + + for handle in handles { + handle.join().unwrap(); + } +} + fn main() { env_logger::init(); - let config = VsockConfig::try_from(VsockArgs::parse()).unwrap(); - start_backend_server(config); + let mut configs = match Vec::::try_from(VsockArgs::parse()) { + Ok(c) => c, + Err(e) => { + println!("Error parsing arguments: {}", e); + return; + } + }; + + if configs.len() == 1 { + start_backend_server(configs.pop().unwrap()); + } else { + start_backend_servers(&configs); + } } #[cfg(test)] @@ -152,12 +264,14 @@ mod tests { uds_path: uds_path.to_string(), tx_buffer_size, }), + vm: None, config: None, } } fn from_file(config: &str) -> Self { VsockArgs { param: None, + vm: None, config: Some(config.to_string()), } } @@ -168,16 +282,56 @@ mod tests { fn test_vsock_config_setup() { let args = VsockArgs::from_args(3, "/tmp/vhost4.socket", "/tmp/vm4.vsock", 64 * 1024); - let config = VsockConfig::try_from(args); - assert!(config.is_ok()); + let configs = Vec::::try_from(args); + assert!(configs.is_ok()); - let config = config.unwrap(); + let configs = configs.unwrap(); + assert_eq!(configs.len(), 1); + + let config = &configs[0]; assert_eq!(config.get_guest_cid(), 3); assert_eq!(config.get_socket_path(), "/tmp/vhost4.socket"); assert_eq!(config.get_uds_path(), "/tmp/vm4.vsock"); assert_eq!(config.get_tx_buffer_size(), 64 * 1024); } + #[test] + #[serial] + fn test_vsock_config_setup_from_vm_args() { + let params = "--vm socket=/tmp/vhost3.socket,uds_path=/tmp/vm3.vsock \ + --vm socket=/tmp/vhost4.socket,uds-path=/tmp/vm4.vsock,guest-cid=4,tx_buffer_size=65536 \ + --vm guest-cid=5,socket=/tmp/vhost5.socket,uds_path=/tmp/vm5.vsock,tx-buffer-size=32768"; + + let mut params = params.split_whitespace().collect::>(); + params.insert(0, ""); // to make the test binary name agnostic + + let args = VsockArgs::parse_from(params); + + let configs = Vec::::try_from(args); + assert!(configs.is_ok()); + + let configs = configs.unwrap(); + assert_eq!(configs.len(), 3); + + let config = configs.get(0).unwrap(); + assert_eq!(config.get_guest_cid(), 3); + assert_eq!(config.get_socket_path(), "/tmp/vhost3.socket"); + assert_eq!(config.get_uds_path(), "/tmp/vm3.vsock"); + assert_eq!(config.get_tx_buffer_size(), 65536); + + let config = configs.get(1).unwrap(); + assert_eq!(config.get_guest_cid(), 4); + assert_eq!(config.get_socket_path(), "/tmp/vhost4.socket"); + assert_eq!(config.get_uds_path(), "/tmp/vm4.vsock"); + assert_eq!(config.get_tx_buffer_size(), 65536); + + let config = configs.get(2).unwrap(); + assert_eq!(config.get_guest_cid(), 5); + assert_eq!(config.get_socket_path(), "/tmp/vhost5.socket"); + assert_eq!(config.get_uds_path(), "/tmp/vm5.vsock"); + assert_eq!(config.get_tx_buffer_size(), 32768); + } + #[test] #[serial] fn test_vsock_config_setup_from_file() { @@ -191,7 +345,11 @@ mod tests { ) .unwrap(); let args = VsockArgs::from_file("./config.yaml"); - let config = VsockConfig::try_from(args).unwrap(); + + let configs = Vec::::try_from(args).unwrap(); + assert_eq!(configs.len(), 1); + + let config = &configs[0]; assert_eq!(config.get_guest_cid(), 4); assert_eq!(config.get_socket_path(), "/tmp/vhost4.socket"); assert_eq!(config.get_uds_path(), "/tmp/vm4.vsock"); diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index 8e0863e..3f52d95 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -123,8 +123,6 @@ pub(crate) enum Error { EmptyBackendRxQ, #[error("Failed to create an EventFd")] EventFdCreate(std::io::Error), - #[error("Failed to parse a configuration file")] - ConfigParse, } impl std::convert::From for std::io::Error { From 10133d65a29bf45e64e029c1bd61e6b367a6d8ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 04:58:33 +0000 Subject: [PATCH 055/189] build(deps): bump parking_lot_core from 0.9.7 to 0.9.8 Bumps [parking_lot_core](https://github.com/Amanieu/parking_lot) from 0.9.7 to 0.9.8. - [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md) - [Commits](https://github.com/Amanieu/parking_lot/compare/core-0.9.7...core-0.9.8) --- updated-dependencies: - dependency-name: parking_lot_core dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2c95bb0..19a1808 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -736,15 +736,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "smallvec", - "windows-sys 0.45.0", + "windows-targets 0.48.0", ] [[package]] @@ -879,15 +879,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - [[package]] name = "redox_syscall" version = "0.3.5" @@ -1104,7 +1095,7 @@ checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall", "rustix", "windows-sys 0.45.0", ] From 7ad018653c03b0a0657463e84444627a4b65ca53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 04:58:03 +0000 Subject: [PATCH 056/189] build(deps): bump lock_api from 0.4.9 to 0.4.10 Bumps [lock_api](https://github.com/Amanieu/parking_lot) from 0.4.9 to 0.4.10. - [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md) - [Commits](https://github.com/Amanieu/parking_lot/compare/lock_api-0.4.9...lock_api-0.4.10) --- updated-dependencies: - dependency-name: lock_api dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19a1808..6fedb44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -641,9 +641,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", From 393b26892173a6dc12e4c8ca323a7b5d3fe8ffea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 04:58:01 +0000 Subject: [PATCH 057/189] build(deps): bump rust-vmm-ci from `8627b37` to `285971e` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `8627b37` to `285971e`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/8627b3766b2bedde4657c7e9ddfc6f95a20e6942...285971e8c716512d6e35ac47a009a49fc3c75660) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 8627b37..285971e 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 8627b3766b2bedde4657c7e9ddfc6f95a20e6942 +Subproject commit 285971e8c716512d6e35ac47a009a49fc3c75660 From 93bdb73af24a9cf97f50db0095eb290446f1950c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 04:57:45 +0000 Subject: [PATCH 058/189] build(deps): bump serial_test from 1.0.0 to 2.0.0 Bumps [serial_test](https://github.com/palfrey/serial_test) from 1.0.0 to 2.0.0. - [Release notes](https://github.com/palfrey/serial_test/releases) - [Commits](https://github.com/palfrey/serial_test/compare/v1.0.0...v2.0.0) --- updated-dependencies: - dependency-name: serial_test dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- Cargo.lock | 10 +++++----- crates/vsock/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6fedb44..c775fc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1004,9 +1004,9 @@ dependencies = [ [[package]] name = "serial_test" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "538c30747ae860d6fb88330addbbd3e0ddbe46d662d032855596d8a8ca260611" +checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" dependencies = [ "dashmap", "futures", @@ -1018,13 +1018,13 @@ dependencies = [ [[package]] name = "serial_test_derive" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "079a83df15f85d89a68d64ae1238f142f172b1fa915d0d76b26a7cba1b659a69" +checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.16", ] [[package]] diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index 50c3699..841c7d9 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -30,4 +30,4 @@ serde_yaml = "0.9" [dev-dependencies] virtio-queue = { version = "0.8", features = ["test-utils"] } -serial_test = "1.0" +serial_test = "2.0" From 8ac1ce45c18b48d26f565aa762e718bcbc516d05 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Tue, 13 Jun 2023 13:33:07 +0200 Subject: [PATCH 059/189] gpio: use libgpiod from crates.io This simplifies the packaging and allows using existing tooling to manage further updates. The lock file was updated with: cargo update -p libgpiod --aggressive Signed-off-by: Erik Schilling --- Cargo.lock | 118 +++++++++++++++++++++++++++++++---------- crates/gpio/Cargo.toml | 2 +- 2 files changed, 91 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c775fc9..fa1db9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] @@ -85,7 +85,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -205,10 +205,10 @@ version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -417,7 +417,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -486,6 +486,15 @@ dependencies = [ "ahash", ] +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.4.1" @@ -592,14 +601,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "libgpiod" version = "0.1.0" -source = "git+https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/?rev=d8d3a84b2ddf#d8d3a84b2ddfc29670430fc73ff8483a44b8f61e" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e9fdf4b437063f5697151f9ead12bafa223958e243f2f736107ec68c2b88231" dependencies = [ "errno 0.2.8", "intmap", @@ -611,10 +621,11 @@ dependencies = [ [[package]] name = "libgpiod-sys" version = "0.1.0" -source = "git+https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/?rev=d8d3a84b2ddf#d8d3a84b2ddfc29670430fc73ff8483a44b8f61e" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa282e1da652deaeed776f6ef36d443689aeda19e5c0a3a2335c50b4611ce489" dependencies = [ "bindgen", - "cc", + "system-deps", ] [[package]] @@ -651,9 +662,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" @@ -789,7 +800,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -815,6 +826,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -833,18 +850,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.58" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -890,9 +907,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.3" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "aho-corasick", "memchr", @@ -960,22 +977,22 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -1065,6 +1082,24 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c" + +[[package]] +name = "strum_macros" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "syn" version = "1.0.109" @@ -1078,15 +1113,30 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.16" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "system-deps" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b59b8aafd652f3c1469f16e6c223121e8a8dbe40c71475209c1401cff3a67ef" +dependencies = [ + "heck 0.3.3", + "pkg-config", + "strum", + "strum_macros", + "thiserror", + "toml", + "version-compare", +] + [[package]] name = "tempfile" version = "3.5.0" @@ -1126,7 +1176,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -1173,6 +1223,12 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unsafe-libyaml" version = "0.2.8" @@ -1185,6 +1241,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "version-compare" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" + [[package]] name = "version_check" version = "0.9.4" diff --git a/crates/gpio/Cargo.toml b/crates/gpio/Cargo.toml index aba760d..8dc7b3b 100644 --- a/crates/gpio/Cargo.toml +++ b/crates/gpio/Cargo.toml @@ -25,7 +25,7 @@ vm-memory = "0.11" vmm-sys-util = "0.11" [target.'cfg(target_env = "gnu")'.dependencies] -libgpiod = { git = "https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/", rev = "d8d3a84b2ddf" } +libgpiod = { version = "0.1" } [dev-dependencies] virtio-queue = { version = "0.8", features = ["test-utils"] } From 1ca98aadea24b5634941a67928ed23453c41acca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 04:57:15 +0000 Subject: [PATCH 060/189] build(deps): bump rust-vmm-ci from `285971e` to `7e9af57` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `285971e` to `7e9af57`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/285971e8c716512d6e35ac47a009a49fc3c75660...7e9af57588b2529a351e7f292152a6240129158b) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 285971e..7e9af57 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 285971e8c716512d6e35ac47a009a49fc3c75660 +Subproject commit 7e9af57588b2529a351e7f292152a6240129158b From 13b9e2ceaa4da74f21f0da91901d21b230b92336 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 04:57:43 +0000 Subject: [PATCH 061/189] build(deps): bump winnow from 0.4.6 to 0.4.7 Bumps [winnow](https://github.com/winnow-rs/winnow) from 0.4.6 to 0.4.7. - [Changelog](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md) - [Commits](https://github.com/winnow-rs/winnow/compare/v0.4.6...v0.4.7) --- updated-dependencies: - dependency-name: winnow dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fa1db9a..74d852b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1041,7 +1041,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.16", + "syn 2.0.18", ] [[package]] @@ -1615,9 +1615,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" dependencies = [ "memchr", ] From fa4fbc4d1e7d4340c2285a0497157be8e8d8ae29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 04:58:00 +0000 Subject: [PATCH 062/189] build(deps): bump sha2 from 0.10.6 to 0.10.7 Bumps [sha2](https://github.com/RustCrypto/hashes) from 0.10.6 to 0.10.7. - [Commits](https://github.com/RustCrypto/hashes/compare/sha2-v0.10.6...sha2-v0.10.7) --- updated-dependencies: - dependency-name: sha2 dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 74d852b..e6aaf4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1046,9 +1046,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", From f260959cdf06db762212da8727b6610cb6417f1b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 04:57:30 +0000 Subject: [PATCH 063/189] build(deps): bump getrandom from 0.2.9 to 0.2.10 Bumps [getrandom](https://github.com/rust-random/getrandom) from 0.2.9 to 0.2.10. - [Changelog](https://github.com/rust-random/getrandom/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-random/getrandom/compare/v0.2.9...v0.2.10) --- updated-dependencies: - dependency-name: getrandom dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6aaf4a..8c3e428 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -462,9 +462,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", From 83bc481e765c4e23acecac9678d0e6f187efe3ad Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Fri, 23 Jun 2023 16:01:41 +0530 Subject: [PATCH 064/189] Run cargo update Manually update all cargo dependencies. Signed-off-by: Priyansh Rathi --- Cargo.lock | 151 +++++++++++++++-------------------------------------- 1 file changed, 43 insertions(+), 108 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c3e428..1fe04f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,15 +39,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ "utf8parse", ] @@ -58,7 +58,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -68,7 +68,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -177,9 +177,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.0" +version = "4.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc" +checksum = "2686c4115cb0810d9a984776e197823d08ec94f176549a89a9efded477c456dc" dependencies = [ "clap_builder", "clap_derive", @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.0" +version = "4.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990" +checksum = "2e53afce1efce6ed1f633cf0e57612fe51db54a1ee4fd8f8503d078fe02d69ae" dependencies = [ "anstream", "anstyle", @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.0" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -244,9 +244,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" dependencies = [ "libc", ] @@ -338,7 +338,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -555,7 +555,7 @@ checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -567,7 +567,7 @@ dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -755,7 +755,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.0", + "windows-targets", ] [[package]] @@ -772,9 +772,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pest" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" +checksum = "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9" dependencies = [ "thiserror", "ucd-trie", @@ -782,9 +782,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" +checksum = "aef623c9bbfa0eedf5a0efba11a5ee83209c326653ca31ff019bec3a95bfff2b" dependencies = [ "pest", "pest_generator", @@ -792,9 +792,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" +checksum = "b3e8cba4ec22bada7fc55ffe51e2deb6a0e0db2d0b7ab0b103acc80d2510c190" dependencies = [ "pest", "pest_meta", @@ -805,9 +805,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" +checksum = "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0" dependencies = [ "once_cell", "pest", @@ -951,16 +951,16 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" dependencies = [ "bitflags", "errno 0.3.1", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -997,9 +997,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" dependencies = [ "itoa", "ryu", @@ -1139,15 +1139,16 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.5.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ + "autocfg", "cfg-if", "fastrand", "redox_syscall", "rustix", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -1481,37 +1482,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[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.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] @@ -1520,93 +1497,51 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" From f0be54bcfe2b1e7a73f8d0912408fc8b674bd0f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 04:58:23 +0000 Subject: [PATCH 065/189] build(deps): bump toml_edit from 0.19.10 to 0.19.11 Bumps [toml_edit](https://github.com/toml-rs/toml) from 0.19.10 to 0.19.11. - [Commits](https://github.com/toml-rs/toml/compare/v0.19.10...v0.19.11) --- updated-dependencies: - dependency-name: toml_edit dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1fe04f4..16dba7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -268,7 +268,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" dependencies = [ "cfg-if", - "hashbrown", + "hashbrown 0.12.3", "lock_api", "once_cell", "parking_lot_core", @@ -319,6 +319,12 @@ dependencies = [ "libc", ] +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + [[package]] name = "errno" version = "0.2.8" @@ -486,6 +492,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.3.3" @@ -529,7 +541,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", ] [[package]] @@ -732,7 +754,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" dependencies = [ "dlv-list", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -1012,7 +1034,7 @@ version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" dependencies = [ - "indexmap", + "indexmap 1.9.3", "itoa", "ryu", "serde", @@ -1191,17 +1213,17 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.19.10" +version = "0.19.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" dependencies = [ - "indexmap", + "indexmap 2.0.0", "toml_datetime", "winnow", ] From a893caa0d61dc84af13055669513efc3189237aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 04:57:32 +0000 Subject: [PATCH 066/189] build(deps): bump clap from 4.3.5 to 4.3.8 Bumps [clap](https://github.com/clap-rs/clap) from 4.3.5 to 4.3.8. - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/v4.3.5...v4.3.8) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- crates/gpio/Cargo.toml | 2 +- crates/i2c/Cargo.toml | 2 +- crates/rng/Cargo.toml | 2 +- crates/scsi/Cargo.toml | 2 +- crates/vsock/Cargo.toml | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 16dba7b..b4e7ef0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,9 +177,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.5" +version = "4.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2686c4115cb0810d9a984776e197823d08ec94f176549a89a9efded477c456dc" +checksum = "d9394150f5b4273a1763355bd1c2ec54cc5a2593f790587bcd6b2c947cfa9211" dependencies = [ "clap_builder", "clap_derive", @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.5" +version = "4.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e53afce1efce6ed1f633cf0e57612fe51db54a1ee4fd8f8503d078fe02d69ae" +checksum = "9a78fbdd3cc2914ddf37ba444114bc7765bbdcb55ec9cbe6fa054f0137400717" dependencies = [ "anstream", "anstyle", diff --git a/crates/gpio/Cargo.toml b/crates/gpio/Cargo.toml index 8dc7b3b..edd3049 100644 --- a/crates/gpio/Cargo.toml +++ b/crates/gpio/Cargo.toml @@ -12,7 +12,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.2", features = ["derive"] } +clap = { version = "4.3", features = ["derive"] } env_logger = "0.10" libc = "0.2" log = "0.4" diff --git a/crates/i2c/Cargo.toml b/crates/i2c/Cargo.toml index 9608761..0505d88 100644 --- a/crates/i2c/Cargo.toml +++ b/crates/i2c/Cargo.toml @@ -12,7 +12,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.2", features = ["derive"] } +clap = { version = "4.3", features = ["derive"] } env_logger = "0.10" libc = "0.2" log = "0.4" diff --git a/crates/rng/Cargo.toml b/crates/rng/Cargo.toml index fa9a99c..b39d8f9 100644 --- a/crates/rng/Cargo.toml +++ b/crates/rng/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0 OR BSD-3-Clause" edition = "2021" [dependencies] -clap = { version = "4.2", features = ["derive"] } +clap = { version = "4.3", features = ["derive"] } env_logger = "0.10" epoll = "4.3" libc = "0.2" diff --git a/crates/scsi/Cargo.toml b/crates/scsi/Cargo.toml index 682c9ca..b1b9d9d 100644 --- a/crates/scsi/Cargo.toml +++ b/crates/scsi/Cargo.toml @@ -12,7 +12,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.2", features = ["derive"] } +clap = { version = "4.3", features = ["derive"] } env_logger = "0.10" epoll = "4.3" log = "0.4" diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index 841c7d9..bcbf0b8 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -11,7 +11,7 @@ edition = "2018" [dependencies] byteorder = "1" -clap = { version = "4.2", features = ["derive"] } +clap = { version = "4.3", features = ["derive"] } env_logger = "0.10" epoll = "4.3.1" futures = { version = "0.3", features = ["thread-pool"] } From 45e6c6d5aad09c5345e9a09999da580afd289dea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jun 2023 05:53:16 +0000 Subject: [PATCH 067/189] build(deps): bump serde_yaml from 0.9.21 to 0.9.22 Bumps [serde_yaml](https://github.com/dtolnay/serde-yaml) from 0.9.21 to 0.9.22. - [Release notes](https://github.com/dtolnay/serde-yaml/releases) - [Commits](https://github.com/dtolnay/serde-yaml/compare/0.9.21...0.9.22) --- updated-dependencies: - dependency-name: serde_yaml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b4e7ef0..b1ca9b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -534,16 +534,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.0.0" @@ -1030,11 +1020,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.21" +version = "0.9.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" +checksum = "452e67b9c20c37fa79df53201dc03839651086ed9bbe92b3ca585ca9fdaa7d85" dependencies = [ - "indexmap 1.9.3", + "indexmap", "itoa", "ryu", "serde", @@ -1223,7 +1213,7 @@ version = "0.19.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" dependencies = [ - "indexmap 2.0.0", + "indexmap", "toml_datetime", "winnow", ] From 1e97f7c0dd34c2cfeb95a456be2df92238f0722a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jul 2023 04:43:18 +0000 Subject: [PATCH 068/189] build(deps): bump rust-vmm-ci from `7e9af57` to `9dfe5b2` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `7e9af57` to `9dfe5b2`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/7e9af57588b2529a351e7f292152a6240129158b...9dfe5b267c4009150f0cdf49597b683f15848d1a) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 7e9af57..9dfe5b2 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 7e9af57588b2529a351e7f292152a6240129158b +Subproject commit 9dfe5b267c4009150f0cdf49597b683f15848d1a From 8ed0c0e58a6019f8a4aebce2f41526ca9c9eaa02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jul 2023 04:48:32 +0000 Subject: [PATCH 069/189] build(deps): bump num_cpus from 1.15.0 to 1.16.0 Bumps [num_cpus](https://github.com/seanmonstar/num_cpus) from 1.15.0 to 1.16.0. - [Release notes](https://github.com/seanmonstar/num_cpus/releases) - [Changelog](https://github.com/seanmonstar/num_cpus/blob/master/CHANGELOG.md) - [Commits](https://github.com/seanmonstar/num_cpus/compare/v1.15.0...v1.16.0) --- updated-dependencies: - dependency-name: num_cpus dependency-type: indirect update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1ca9b9..f3f12f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -513,15 +513,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.1" @@ -565,7 +556,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "libc", "windows-sys", ] @@ -576,7 +567,7 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi", "io-lifetimes", "rustix", "windows-sys", @@ -702,11 +693,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] From 6d548cd7c828c673751c60b80b0b30dfdf8f25c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jul 2023 04:48:59 +0000 Subject: [PATCH 070/189] build(deps): bump itoa from 1.0.6 to 1.0.7 Bumps [itoa](https://github.com/dtolnay/itoa) from 1.0.6 to 1.0.7. - [Release notes](https://github.com/dtolnay/itoa/releases) - [Commits](https://github.com/dtolnay/itoa/compare/1.0.6...1.0.7) --- updated-dependencies: - dependency-name: itoa dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3f12f0..f1138a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -575,9 +575,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "c0aa48fab2893d8a49caa94082ae8488f4e1050d73b367881dcd2198f4199fd8" [[package]] name = "json5" From f0b0eee465cdc1d82406cecbf6132abb5f52efd5 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Thu, 29 Jun 2023 13:50:50 +0200 Subject: [PATCH 071/189] gpio: update instructions for setting up libgpiod Signed-off-by: Erik Schilling --- README.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 06673f7..018cc19 100644 --- a/README.md +++ b/README.md @@ -45,14 +45,20 @@ logic to service the virtio requests directly in the application. ## Build dependency -The GPIO crate needs a local installation of libgpiod library to be available, -which can be done like: +The GPIO crate needs a local installation of libgpiod library to be available. +If your distro ships libgpiod >= v2.0, then you should be fine. -$ git clone --depth 1 --branch v2.0-rc1 https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/ -$ cd libgpiod -$ ./autogen.sh && make +Otherwise, you will need to build libgpiod yourself: -Either you can do a 'make install' now on your system, or provide path to the -locally build library like this while building vhost-device crates: + git clone --depth 1 --branch v2.0.x https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/ + cd libgpiod + ./autogen.sh --prefix="$PWD/install/" + make install -$ RUSTFLAGS='-L /home//libgpiod/lib/.libs/' cargo build --release +In order to inform tools about the build location, you can now set: + + export PKG_CONFIG_PATH="/install/lib/pkgconfig/" + +To prevent setting this in every terminal session, you can also configure +cargo to +[set it automatically](https://doc.rust-lang.org/cargo/reference/config.html#env). \ No newline at end of file From 015ccff9fce475a01e7848ef7beae5a1ab9205b9 Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Fri, 30 Jun 2023 00:06:57 +0530 Subject: [PATCH 072/189] vsock: Handle the single VM case same as multiple VMs In future, we could add the ability to change the configuration at runtime and allow new guests to be added even without having to restart the daemon. So it is reasonable to not differentiate between the single and multiple VM cases, even with only one guest. Signed-off-by: Priyansh Rathi --- crates/vsock/src/main.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/vsock/src/main.rs b/crates/vsock/src/main.rs index babb12a..86b2ad4 100644 --- a/crates/vsock/src/main.rs +++ b/crates/vsock/src/main.rs @@ -233,7 +233,7 @@ pub(crate) fn start_backend_servers(configs: &[VsockConfig]) { fn main() { env_logger::init(); - let mut configs = match Vec::::try_from(VsockArgs::parse()) { + let configs = match Vec::::try_from(VsockArgs::parse()) { Ok(c) => c, Err(e) => { println!("Error parsing arguments: {}", e); @@ -241,11 +241,7 @@ fn main() { } }; - if configs.len() == 1 { - start_backend_server(configs.pop().unwrap()); - } else { - start_backend_servers(&configs); - } + start_backend_servers(&configs); } #[cfg(test)] From d6b953e95861d3b4b9002c91ab1867b206afc39e Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Fri, 30 Jun 2023 00:18:32 +0530 Subject: [PATCH 073/189] vsock: Implement sibling VM communication Adds support for communication between sibling VMs that use the vhost-user-vsock devices from the same vhost-user-vsock application. Tested with nc-vsock patched to set `.svm_flags = VMADDR_FLAG_TO_HOST`: host$ vhost-user-vsock \ --vm guest-cid=3,uds-path=/tmp/vm3.vsock,socket=/tmp/vhost3.socket \ --vm guest-cid=4,uds-path=/tmp/vm4.vsock,socket=/tmp/vhost4.socket vm_cid3$ nc-vsock -l 1234 vm_cid4$ nc-vsock 3 1234 Signed-off-by: Priyansh Rathi --- crates/vsock/README.md | 28 ++++ crates/vsock/src/main.rs | 28 +++- crates/vsock/src/thread_backend.rs | 201 ++++++++++++++++++++++++--- crates/vsock/src/vhu_vsock.rs | 30 +++- crates/vsock/src/vhu_vsock_thread.rs | 112 +++++++++++++-- 5 files changed, 356 insertions(+), 43 deletions(-) diff --git a/crates/vsock/README.md b/crates/vsock/README.md index 8e594f0..605b0a5 100644 --- a/crates/vsock/README.md +++ b/crates/vsock/README.md @@ -142,6 +142,34 @@ host$ nc -l -U /tmp/vm4.vsock_1234 guest$ nc --vsock 2 1234 ``` +### Sibling VM communication + +If you add multiple VMs, they can communicate with each other. For example, if you have two VMs with +CID 3 and 4, you can run the following commands to make them communicate: + +```sh +shell1$ vhost-user-vsock --vm guest-cid=3,uds-path=/tmp/vm3.vsock,socket=/tmp/vhost3.socket \ + --vm guest-cid=4,uds-path=/tmp/vm4.vsock,socket=/tmp/vhost4.socket +shell2$ qemu-system-x86_64 \ + -drive file=vm1.qcow2,format=qcow2,if=virtio -smp 2 -m 512M -mem-prealloc \ + -object memory-backend-file,share=on,id=mem0,size=512M,mem-path="/dev/hugepages" \ + -machine q35,accel=kvm,memory-backend=mem0 \ + -chardev socket,id=char0,reconnect=0,path=/tmp/vhost3.socket \ + -device vhost-user-vsock-pci,chardev=char0 +shell3$ qemu-system-x86_64 \ + -drive file=vm2.qcow2,format=qcow2,if=virtio -smp 2 -m 512M -mem-prealloc \ + -object memory-backend-file,share=on,id=mem0,size=512M,mem-path="/dev/hugepages2" \ + -machine q35,accel=kvm,memory-backend=mem0 \ + -chardev socket,id=char0,reconnect=0,path=/tmp/vhost4.socket \ + -device vhost-user-vsock-pci,chardev=char0 +``` + +```sh +# nc-vsock patched to set `.svm_flags = VMADDR_FLAG_TO_HOST` +guest_cid3$ nc-vsock -l 1234 +guest_cid4$ nc-vsock 3 1234 +``` + ## License This project is licensed under either of diff --git a/crates/vsock/src/main.rs b/crates/vsock/src/main.rs index 86b2ad4..eb2d7e3 100644 --- a/crates/vsock/src/main.rs +++ b/crates/vsock/src/main.rs @@ -8,9 +8,14 @@ mod vhu_vsock; mod vhu_vsock_thread; mod vsock_conn; -use std::{convert::TryFrom, sync::Arc, thread}; +use std::{ + collections::HashMap, + convert::TryFrom, + sync::{Arc, RwLock}, + thread, +}; -use crate::vhu_vsock::{VhostUserVsockBackend, VsockConfig}; +use crate::vhu_vsock::{CidMap, VhostUserVsockBackend, VsockConfig}; use clap::{Args, Parser}; use log::{info, warn}; use serde::Deserialize; @@ -172,9 +177,14 @@ impl TryFrom for Vec { /// This is the public API through which an external program starts the /// vhost-user-vsock backend server. -pub(crate) fn start_backend_server(config: VsockConfig) { +pub(crate) fn start_backend_server(config: VsockConfig, cid_map: Arc>) { loop { - let backend = Arc::new(VhostUserVsockBackend::new(config.clone()).unwrap()); + let backend = + Arc::new(VhostUserVsockBackend::new(config.clone(), cid_map.clone()).unwrap()); + cid_map + .write() + .unwrap() + .insert(config.get_guest_cid(), backend.clone()); let listener = Listener::new(config.get_socket_path(), true).unwrap(); @@ -210,17 +220,20 @@ pub(crate) fn start_backend_server(config: VsockConfig) { // No matter the result, we need to shut down the worker thread. backend.exit_event.write(1).unwrap(); + cid_map.write().unwrap().remove(&config.get_guest_cid()); } } pub(crate) fn start_backend_servers(configs: &[VsockConfig]) { + let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); let mut handles = Vec::new(); for c in configs.iter() { let config = c.clone(); + let cid_map = cid_map.clone(); let handle = thread::Builder::new() .name(format!("vhu-vsock-cid-{}", c.get_guest_cid())) - .spawn(move || start_backend_server(config)) + .spawn(move || start_backend_server(config, cid_map)) .unwrap(); handles.push(handle); } @@ -367,7 +380,10 @@ mod tests { CONN_TX_BUF_SIZE, ); - let backend = Arc::new(VhostUserVsockBackend::new(config).unwrap()); + let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); + + let backend = Arc::new(VhostUserVsockBackend::new(config, cid_map.clone()).unwrap()); + cid_map.write().unwrap().insert(CID, backend.clone()); let daemon = VhostUserDaemon::new( String::from("vhost-user-vsock"), diff --git a/crates/vsock/src/thread_backend.rs b/crates/vsock/src/thread_backend.rs index d101047..31e2f76 100644 --- a/crates/vsock/src/thread_backend.rs +++ b/crates/vsock/src/thread_backend.rs @@ -6,22 +6,46 @@ use std::{ net::UnixStream, prelude::{AsRawFd, FromRawFd, RawFd}, }, + sync::{Arc, RwLock}, }; use log::{info, warn}; -use virtio_vsock::packet::VsockPacket; +use virtio_vsock::packet::{VsockPacket, PKT_HEADER_SIZE}; use vm_memory::bitmap::BitmapSlice; use crate::{ rxops::*, vhu_vsock::{ - ConnMapKey, Error, Result, VSOCK_HOST_CID, VSOCK_OP_REQUEST, VSOCK_OP_RST, + CidMap, ConnMapKey, Error, Result, VSOCK_HOST_CID, VSOCK_OP_REQUEST, VSOCK_OP_RST, VSOCK_TYPE_STREAM, }, vhu_vsock_thread::VhostUserVsockThread, vsock_conn::*, }; +pub(crate) struct RawVsockPacket { + pub header: [u8; PKT_HEADER_SIZE], + pub data: Vec, +} + +impl RawVsockPacket { + fn from_vsock_packet(pkt: &VsockPacket) -> Result { + let mut raw_pkt = Self { + header: [0; PKT_HEADER_SIZE], + data: vec![0; pkt.len() as usize], + }; + + pkt.header_slice().copy_to(&mut raw_pkt.header); + if !pkt.is_empty() { + pkt.data_slice() + .ok_or(Error::PktBufMissing)? + .copy_to(raw_pkt.data.as_mut()); + } + + Ok(raw_pkt) + } +} + pub(crate) struct VsockThreadBackend { /// Map of ConnMapKey objects indexed by raw file descriptors. pub listener_map: HashMap, @@ -38,11 +62,20 @@ pub(crate) struct VsockThreadBackend { /// Set of allocated local ports. pub local_port_set: HashSet, tx_buffer_size: u32, + /// Maps the guest CID to the corresponding backend. Used for sibling VM communication. + cid_map: Arc>, + /// Queue of raw vsock packets recieved from sibling VMs to be sent to the guest. + raw_pkts_queue: VecDeque, } impl VsockThreadBackend { /// New instance of VsockThreadBackend. - pub fn new(host_socket_path: String, epoll_fd: i32, tx_buffer_size: u32) -> Self { + pub fn new( + host_socket_path: String, + epoll_fd: i32, + tx_buffer_size: u32, + cid_map: Arc>, + ) -> Self { Self { listener_map: HashMap::new(), conn_map: HashMap::new(), @@ -54,6 +87,8 @@ impl VsockThreadBackend { epoll_fd, local_port_set: HashSet::new(), tx_buffer_size, + cid_map, + raw_pkts_queue: VecDeque::new(), } } @@ -62,6 +97,11 @@ impl VsockThreadBackend { !self.backend_rxq.is_empty() } + /// Checks if there are pending raw vsock packets to be sent to the guest. + pub fn pending_raw_pkts(&self) -> bool { + !self.raw_pkts_queue.is_empty() + } + /// Deliver a vsock packet to the guest vsock driver. /// /// Returns: @@ -122,7 +162,24 @@ impl VsockThreadBackend { /// Returns: /// - always `Ok(())` if packet has been consumed correctly pub fn send_pkt(&mut self, pkt: &VsockPacket) -> Result<()> { - let key = ConnMapKey::new(pkt.dst_port(), pkt.src_port()); + let dst_cid = pkt.dst_cid(); + if dst_cid != VSOCK_HOST_CID { + let cid_map = self.cid_map.read().unwrap(); + if cid_map.contains_key(&dst_cid) { + let sibling_backend = cid_map.get(&dst_cid).unwrap(); + let mut sibling_backend_thread = sibling_backend.threads[0].lock().unwrap(); + + sibling_backend_thread + .thread_backend + .raw_pkts_queue + .push_back(RawVsockPacket::from_vsock_packet(pkt)?); + let _ = sibling_backend_thread.sibling_event_fd.write(1); + } else { + warn!("vsock: dropping packet for unknown cid: {:?}", dst_cid); + } + + return Ok(()); + } // TODO: Rst if packet has unsupported type if pkt.type_() != VSOCK_TYPE_STREAM { @@ -130,15 +187,7 @@ impl VsockThreadBackend { return Ok(()); } - // TODO: Handle packets to other CIDs as well - if pkt.dst_cid() != VSOCK_HOST_CID { - info!( - "vsock: dropping packet for cid other than host: {:?}", - pkt.dst_cid() - ); - - return Ok(()); - } + let key = ConnMapKey::new(pkt.dst_port(), pkt.src_port()); // TODO: Handle cases where connection does not exist and packet op // is not VSOCK_OP_REQUEST @@ -185,6 +234,26 @@ impl VsockThreadBackend { Ok(()) } + /// Deliver a raw vsock packet sent from a sibling VM to the guest vsock driver. + /// + /// Returns: + /// - `Ok(())` if packet was successfully filled in + /// - `Err(Error::EmptyRawPktsQueue)` if there was no available data + pub fn recv_raw_pkt(&mut self, pkt: &mut VsockPacket) -> Result<()> { + let raw_vsock_pkt = self + .raw_pkts_queue + .pop_front() + .ok_or(Error::EmptyRawPktsQueue)?; + + pkt.set_header_from_raw(&raw_vsock_pkt.header).unwrap(); + if !raw_vsock_pkt.data.is_empty() { + let buf = pkt.data_slice().ok_or(Error::PktBufMissing)?; + buf.copy_from(&raw_vsock_pkt.data); + } + + Ok(()) + } + /// Handle a new guest initiated connection, i.e from the peer, the guest driver. /// /// Attempts to connect to a host side unix socket listening on a path @@ -251,7 +320,7 @@ impl VsockThreadBackend { #[cfg(test)] mod tests { use super::*; - use crate::vhu_vsock::VSOCK_OP_RW; + use crate::vhu_vsock::{VhostUserVsockBackend, VsockConfig, VSOCK_OP_RW}; use serial_test::serial; use std::os::unix::net::UnixListener; use virtio_vsock::packet::{VsockPacket, PKT_HEADER_SIZE}; @@ -270,8 +339,15 @@ mod tests { let _listener = UnixListener::bind(VSOCK_PEER_PATH).unwrap(); let epoll_fd = epoll::create(false).unwrap(); - let mut vtp = - VsockThreadBackend::new(VSOCK_SOCKET_PATH.to_string(), epoll_fd, CONN_TX_BUF_SIZE); + + let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); + + let mut vtp = VsockThreadBackend::new( + VSOCK_SOCKET_PATH.to_string(), + epoll_fd, + CONN_TX_BUF_SIZE, + cid_map, + ); assert!(!vtp.pending_rx()); @@ -309,4 +385,97 @@ mod tests { // cleanup let _ = std::fs::remove_file(VSOCK_PEER_PATH); } + + #[test] + #[serial] + fn test_vsock_thread_backend_sibling_vms() { + const CID: u64 = 3; + const VSOCK_SOCKET_PATH: &str = "test_vsock_thread_backend.vsock"; + + const SIBLING_CID: u64 = 4; + const SIBLING_VHOST_SOCKET_PATH: &str = "test_vsock_thread_backend_sibling.socket"; + const SIBLING_VSOCK_SOCKET_PATH: &str = "test_vsock_thread_backend_sibling.vsock"; + const SIBLING_LISTENING_PORT: u32 = 1234; + + let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); + + let sibling_config = VsockConfig::new( + SIBLING_CID, + SIBLING_VHOST_SOCKET_PATH.to_string(), + SIBLING_VSOCK_SOCKET_PATH.to_string(), + CONN_TX_BUF_SIZE, + ); + + let sibling_backend = + Arc::new(VhostUserVsockBackend::new(sibling_config, cid_map.clone()).unwrap()); + cid_map + .write() + .unwrap() + .insert(SIBLING_CID, sibling_backend.clone()); + + let epoll_fd = epoll::create(false).unwrap(); + let mut vtp = VsockThreadBackend::new( + VSOCK_SOCKET_PATH.to_string(), + epoll_fd, + CONN_TX_BUF_SIZE, + cid_map, + ); + + assert!(!vtp.pending_raw_pkts()); + + let mut pkt_raw = [0u8; PKT_HEADER_SIZE + DATA_LEN]; + let (hdr_raw, data_raw) = pkt_raw.split_at_mut(PKT_HEADER_SIZE); + + // SAFETY: Safe as hdr_raw and data_raw are guaranteed to be valid. + let mut packet = unsafe { VsockPacket::new(hdr_raw, Some(data_raw)).unwrap() }; + + assert_eq!( + vtp.recv_raw_pkt(&mut packet).unwrap_err().to_string(), + Error::EmptyRawPktsQueue.to_string() + ); + + packet.set_type(VSOCK_TYPE_STREAM); + packet.set_src_cid(CID); + packet.set_dst_cid(SIBLING_CID); + packet.set_dst_port(SIBLING_LISTENING_PORT); + packet.set_op(VSOCK_OP_RW); + packet.set_len(DATA_LEN as u32); + packet + .data_slice() + .unwrap() + .copy_from(&[0xCAu8, 0xFEu8, 0xBAu8, 0xBEu8]); + + assert!(vtp.send_pkt(&packet).is_ok()); + assert!(sibling_backend.threads[0] + .lock() + .unwrap() + .thread_backend + .pending_raw_pkts()); + + let mut recvd_pkt_raw = [0u8; PKT_HEADER_SIZE + DATA_LEN]; + let (recvd_hdr_raw, recvd_data_raw) = recvd_pkt_raw.split_at_mut(PKT_HEADER_SIZE); + + let mut recvd_packet = + // SAFETY: Safe as recvd_hdr_raw and recvd_data_raw are guaranteed to be valid. + unsafe { VsockPacket::new(recvd_hdr_raw, Some(recvd_data_raw)).unwrap() }; + + assert!(sibling_backend.threads[0] + .lock() + .unwrap() + .thread_backend + .recv_raw_pkt(&mut recvd_packet) + .is_ok()); + + assert_eq!(recvd_packet.type_(), VSOCK_TYPE_STREAM); + assert_eq!(recvd_packet.src_cid(), CID); + assert_eq!(recvd_packet.dst_cid(), SIBLING_CID); + assert_eq!(recvd_packet.dst_port(), SIBLING_LISTENING_PORT); + assert_eq!(recvd_packet.op(), VSOCK_OP_RW); + assert_eq!(recvd_packet.len(), DATA_LEN as u32); + + assert_eq!(recvd_data_raw[0], 0xCAu8); + assert_eq!(recvd_data_raw[1], 0xFEu8); + assert_eq!(recvd_data_raw[2], 0xBAu8); + assert_eq!(recvd_data_raw[3], 0xBEu8); + } } diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index 3f52d95..1dfc0aa 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -1,8 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause use std::{ + collections::HashMap, io::{self, Result as IoResult}, - sync::Mutex, + sync::{Arc, Mutex, RwLock}, u16, u32, u64, u8, }; @@ -22,6 +23,8 @@ use vmm_sys_util::{ use crate::vhu_vsock_thread::*; +pub(crate) type CidMap = HashMap>; + const NUM_QUEUES: usize = 2; const QUEUE_SIZE: usize = 256; @@ -35,6 +38,9 @@ const EVT_QUEUE_EVENT: u16 = 2; /// Notification coming from the backend. pub(crate) const BACKEND_EVENT: u16 = 3; +/// Notification coming from the sibling VM. +pub(crate) const SIBLING_VM_EVENT: u16 = 4; + /// CID of the host pub(crate) const VSOCK_HOST_CID: u64 = 2; @@ -123,6 +129,8 @@ pub(crate) enum Error { EmptyBackendRxQ, #[error("Failed to create an EventFd")] EventFdCreate(std::io::Error), + #[error("Raw vsock packets queue is empty")] + EmptyRawPktsQueue, } impl std::convert::From for std::io::Error { @@ -211,11 +219,12 @@ pub(crate) struct VhostUserVsockBackend { } impl VhostUserVsockBackend { - pub fn new(config: VsockConfig) -> Result { + pub fn new(config: VsockConfig, cid_map: Arc>) -> Result { let thread = Mutex::new(VhostUserVsockThread::new( config.get_uds_path(), config.get_guest_cid(), config.get_tx_buffer_size(), + cid_map, )?); let queues_per_thread = vec![QUEUE_MASK]; @@ -297,6 +306,11 @@ impl VhostUserBackend for VhostUserVsockBackend { } } } + SIBLING_VM_EVENT => { + let _ = thread.sibling_event_fd.read(); + thread.process_raw_pkts(vring_rx, evt_idx)?; + return Ok(false); + } _ => { return Err(Error::HandleUnknownEvent.into()); } @@ -355,7 +369,9 @@ mod tests { CONN_TX_BUF_SIZE, ); - let backend = VhostUserVsockBackend::new(config); + let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); + + let backend = VhostUserVsockBackend::new(config, cid_map); assert!(backend.is_ok()); let backend = backend.unwrap(); @@ -428,7 +444,9 @@ mod tests { CONN_TX_BUF_SIZE, ); - let backend = VhostUserVsockBackend::new(config); + let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); + + let backend = VhostUserVsockBackend::new(config, cid_map.clone()); assert!(backend.is_err()); let config = VsockConfig::new( @@ -438,7 +456,7 @@ mod tests { CONN_TX_BUF_SIZE, ); - let backend = VhostUserVsockBackend::new(config).unwrap(); + let backend = VhostUserVsockBackend::new(config, cid_map).unwrap(); let mem = GuestMemoryAtomic::new( GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(), ); @@ -462,7 +480,7 @@ mod tests { ); assert_eq!( backend - .handle_event(BACKEND_EVENT + 1, EventSet::IN, &vrings, 0) + .handle_event(SIBLING_VM_EVENT + 1, EventSet::IN, &vrings, 0) .unwrap_err() .to_string(), Error::HandleUnknownEvent.to_string() diff --git a/crates/vsock/src/vhu_vsock_thread.rs b/crates/vsock/src/vhu_vsock_thread.rs index 6d13e02..d49718f 100644 --- a/crates/vsock/src/vhu_vsock_thread.rs +++ b/crates/vsock/src/vhu_vsock_thread.rs @@ -10,7 +10,7 @@ use std::{ net::{UnixListener, UnixStream}, prelude::{AsRawFd, FromRawFd, RawFd}, }, - sync::Arc, + sync::{Arc, RwLock}, }; use futures::executor::{ThreadPool, ThreadPoolBuilder}; @@ -19,17 +19,27 @@ use vhost_user_backend::{VringEpollHandler, VringRwLock, VringT}; use virtio_queue::QueueOwnedT; use virtio_vsock::packet::{VsockPacket, PKT_HEADER_SIZE}; use vm_memory::{GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap}; -use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::{ + epoll::EventSet, + eventfd::{EventFd, EFD_NONBLOCK}, +}; use crate::{ rxops::*, thread_backend::*, - vhu_vsock::{ConnMapKey, Error, Result, VhostUserVsockBackend, BACKEND_EVENT, VSOCK_HOST_CID}, + vhu_vsock::{ + CidMap, ConnMapKey, Error, Result, VhostUserVsockBackend, BACKEND_EVENT, SIBLING_VM_EVENT, + VSOCK_HOST_CID, + }, vsock_conn::*, }; type ArcVhostBknd = Arc; +enum RxQueueType { + Standard, + RawPkts, +} pub(crate) struct VhostUserVsockThread { /// Guest memory map. pub mem: Option>, @@ -55,11 +65,19 @@ pub(crate) struct VhostUserVsockThread { local_port: Wrapping, /// The tx buffer size tx_buffer_size: u32, + /// EventFd to notify this thread for custom events. Currently used to notify + /// this thread to process raw vsock packets sent from a sibling VM. + pub sibling_event_fd: EventFd, } impl VhostUserVsockThread { /// Create a new instance of VhostUserVsockThread. - pub fn new(uds_path: String, guest_cid: u64, tx_buffer_size: u32) -> Result { + pub fn new( + uds_path: String, + guest_cid: u64, + tx_buffer_size: u32, + cid_map: Arc>, + ) -> Result { // TODO: better error handling, maybe add a param to force the unlink let _ = std::fs::remove_file(uds_path.clone()); let host_sock = UnixListener::bind(&uds_path) @@ -72,6 +90,8 @@ impl VhostUserVsockThread { let host_raw_fd = host_sock.as_raw_fd(); + let sibling_event_fd = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?; + let thread = VhostUserVsockThread { mem: None, event_idx: false, @@ -80,7 +100,7 @@ impl VhostUserVsockThread { host_listener: host_sock, vring_worker: None, epoll_file, - thread_backend: VsockThreadBackend::new(uds_path, epoll_fd, tx_buffer_size), + thread_backend: VsockThreadBackend::new(uds_path, epoll_fd, tx_buffer_size, cid_map), guest_cid, pool: ThreadPoolBuilder::new() .pool_size(1) @@ -88,6 +108,7 @@ impl VhostUserVsockThread { .map_err(Error::CreateThreadPool)?, local_port: Wrapping(0), tx_buffer_size, + sibling_event_fd, }; VhostUserVsockThread::epoll_register(epoll_fd, host_raw_fd, epoll::Events::EPOLLIN)?; @@ -150,6 +171,15 @@ impl VhostUserVsockThread { .unwrap() .register_listener(self.get_epoll_fd(), EventSet::IN, u64::from(BACKEND_EVENT)) .unwrap(); + self.vring_worker + .as_ref() + .unwrap() + .register_listener( + self.sibling_event_fd.as_raw_fd(), + EventSet::IN, + u64::from(SIBLING_VM_EVENT), + ) + .unwrap(); } /// Process a BACKEND_EVENT received by VhostUserVsockBackend. @@ -388,7 +418,11 @@ impl VhostUserVsockThread { } /// Iterate over the rx queue and process rx requests. - fn process_rx_queue(&mut self, vring: &VringRwLock) -> Result { + fn process_rx_queue( + &mut self, + vring: &VringRwLock, + rx_queue_type: RxQueueType, + ) -> Result { let mut used_any = false; let atomic_mem = match &self.mem { Some(m) => m, @@ -414,7 +448,12 @@ impl VhostUserVsockThread { self.tx_buffer_size, ) { Ok(mut pkt) => { - if self.thread_backend.recv_pkt(&mut pkt).is_ok() { + let recv_result = match rx_queue_type { + RxQueueType::Standard => self.thread_backend.recv_pkt(&mut pkt), + RxQueueType::RawPkts => self.thread_backend.recv_raw_pkt(&mut pkt), + }; + + if recv_result.is_ok() { PKT_HEADER_SIZE + pkt.len() as usize } else { queue.iter(mem).unwrap().go_to_previous_position(); @@ -455,8 +494,17 @@ impl VhostUserVsockThread { } }); - if !self.thread_backend.pending_rx() { - break; + match rx_queue_type { + RxQueueType::Standard => { + if !self.thread_backend.pending_rx() { + break; + } + } + RxQueueType::RawPkts => { + if !self.thread_backend.pending_raw_pkts() { + break; + } + } } } Ok(used_any) @@ -475,13 +523,13 @@ impl VhostUserVsockThread { } vring.disable_notification().unwrap(); - self.process_rx_queue(vring)?; + self.process_rx_queue(vring, RxQueueType::Standard)?; if !vring.enable_notification().unwrap() { break; } } } else { - self.process_rx_queue(vring)?; + self.process_rx_queue(vring, RxQueueType::Standard)?; } Ok(false) } @@ -581,6 +629,26 @@ impl VhostUserVsockThread { } Ok(false) } + + /// Wrapper to process raw vsock packets queue based on whether event idx is enabled or not. + pub fn process_raw_pkts(&mut self, vring: &VringRwLock, event_idx: bool) -> Result { + if event_idx { + loop { + if !self.thread_backend.pending_raw_pkts() { + break; + } + vring.disable_notification().unwrap(); + + self.process_rx_queue(vring, RxQueueType::RawPkts)?; + if !vring.enable_notification().unwrap() { + break; + } + } + } else { + self.process_rx_queue(vring, RxQueueType::RawPkts)?; + } + Ok(false) + } } impl Drop for VhostUserVsockThread { @@ -592,6 +660,7 @@ impl Drop for VhostUserVsockThread { mod tests { use super::*; use serial_test::serial; + use std::collections::HashMap; use vm_memory::GuestAddress; use vmm_sys_util::eventfd::EventFd; @@ -606,8 +675,14 @@ mod tests { #[test] #[serial] fn test_vsock_thread() { - let t = - VhostUserVsockThread::new("test_vsock_thread.vsock".to_string(), 3, CONN_TX_BUF_SIZE); + let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); + + let t = VhostUserVsockThread::new( + "test_vsock_thread.vsock".to_string(), + 3, + CONN_TX_BUF_SIZE, + cid_map, + ); assert!(t.is_ok()); let mut t = t.unwrap(); @@ -662,14 +737,21 @@ mod tests { #[test] #[serial] fn test_vsock_thread_failures() { - let t = - VhostUserVsockThread::new("/sys/not_allowed.vsock".to_string(), 3, CONN_TX_BUF_SIZE); + let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); + + let t = VhostUserVsockThread::new( + "/sys/not_allowed.vsock".to_string(), + 3, + CONN_TX_BUF_SIZE, + cid_map.clone(), + ); assert!(t.is_err()); let mut t = VhostUserVsockThread::new( "test_vsock_thread_failures.vsock".to_string(), 3, CONN_TX_BUF_SIZE, + cid_map, ) .unwrap(); assert!(VhostUserVsockThread::epoll_register(-1, -1, epoll::Events::EPOLLIN).is_err()); From 4346438bff4399bdc8bc30e01595758a69ce0e0b Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Mon, 26 Jun 2023 15:54:16 +0530 Subject: [PATCH 074/189] vsock: Compare packet src_cid with the configured guest CID Cross-check the packet `src_cid` with the CID configured for the guest. This will forbid a VM from impersonating another. Signed-off-by: Priyansh Rathi --- crates/vsock/src/thread_backend.rs | 16 ++++++++++++++++ crates/vsock/src/vhu_vsock_thread.rs | 8 +++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/crates/vsock/src/thread_backend.rs b/crates/vsock/src/thread_backend.rs index 31e2f76..7defac9 100644 --- a/crates/vsock/src/thread_backend.rs +++ b/crates/vsock/src/thread_backend.rs @@ -59,6 +59,8 @@ pub(crate) struct VsockThreadBackend { host_socket_path: String, /// epoll for registering new host-side connections. epoll_fd: i32, + /// CID of the guest. + guest_cid: u64, /// Set of allocated local ports. pub local_port_set: HashSet, tx_buffer_size: u32, @@ -73,6 +75,7 @@ impl VsockThreadBackend { pub fn new( host_socket_path: String, epoll_fd: i32, + guest_cid: u64, tx_buffer_size: u32, cid_map: Arc>, ) -> Self { @@ -85,6 +88,7 @@ impl VsockThreadBackend { stream_map: HashMap::new(), host_socket_path, epoll_fd, + guest_cid, local_port_set: HashSet::new(), tx_buffer_size, cid_map, @@ -162,6 +166,14 @@ impl VsockThreadBackend { /// Returns: /// - always `Ok(())` if packet has been consumed correctly pub fn send_pkt(&mut self, pkt: &VsockPacket) -> Result<()> { + if pkt.src_cid() != self.guest_cid { + warn!( + "vsock: dropping packet with inconsistent src_cid: {:?} from guest configured with CID: {:?}", + pkt.src_cid(), self.guest_cid + ); + return Ok(()); + } + let dst_cid = pkt.dst_cid(); if dst_cid != VSOCK_HOST_CID { let cid_map = self.cid_map.read().unwrap(); @@ -331,6 +343,7 @@ mod tests { #[test] #[serial] fn test_vsock_thread_backend() { + const CID: u64 = 3; const VSOCK_SOCKET_PATH: &str = "test_vsock_thread_backend.vsock"; const VSOCK_PEER_PORT: u32 = 1234; const VSOCK_PEER_PATH: &str = "test_vsock_thread_backend.vsock_1234"; @@ -345,6 +358,7 @@ mod tests { let mut vtp = VsockThreadBackend::new( VSOCK_SOCKET_PATH.to_string(), epoll_fd, + CID, CONN_TX_BUF_SIZE, cid_map, ); @@ -367,6 +381,7 @@ mod tests { packet.set_type(VSOCK_TYPE_STREAM); assert!(vtp.send_pkt(&packet).is_ok()); + packet.set_src_cid(CID); packet.set_dst_cid(VSOCK_HOST_CID); packet.set_dst_port(VSOCK_PEER_PORT); assert!(vtp.send_pkt(&packet).is_ok()); @@ -417,6 +432,7 @@ mod tests { let mut vtp = VsockThreadBackend::new( VSOCK_SOCKET_PATH.to_string(), epoll_fd, + CID, CONN_TX_BUF_SIZE, cid_map, ); diff --git a/crates/vsock/src/vhu_vsock_thread.rs b/crates/vsock/src/vhu_vsock_thread.rs index d49718f..6fabdf4 100644 --- a/crates/vsock/src/vhu_vsock_thread.rs +++ b/crates/vsock/src/vhu_vsock_thread.rs @@ -100,7 +100,13 @@ impl VhostUserVsockThread { host_listener: host_sock, vring_worker: None, epoll_file, - thread_backend: VsockThreadBackend::new(uds_path, epoll_fd, tx_buffer_size, cid_map), + thread_backend: VsockThreadBackend::new( + uds_path, + epoll_fd, + guest_cid, + tx_buffer_size, + cid_map, + ), guest_cid, pool: ThreadPoolBuilder::new() .pool_size(1) From 41f4cb1ab2f1414d6ec166e3015921f2cebb2009 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jul 2023 05:00:22 +0000 Subject: [PATCH 075/189] build(deps): bump epoll from 4.3.1 to 4.3.2 Bumps [epoll](https://github.com/nathansizemore/epoll) from 4.3.1 to 4.3.2. - [Release notes](https://github.com/nathansizemore/epoll/releases) - [Commits](https://github.com/nathansizemore/epoll/commits) --- updated-dependencies: - dependency-name: epoll dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 26 ++++++++++++++++---------- crates/vsock/Cargo.toml | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f1138a0..0d3172a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,7 +106,7 @@ version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", @@ -128,6 +128,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "block-buffer" version = "0.10.4" @@ -194,7 +200,7 @@ checksum = "9a78fbdd3cc2914ddf37ba444114bc7765bbdcb55ec9cbe6fa054f0137400717" dependencies = [ "anstream", "anstyle", - "bitflags", + "bitflags 1.3.2", "clap_lex", "strsim", ] @@ -311,11 +317,11 @@ dependencies = [ [[package]] name = "epoll" -version = "4.3.1" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20df693c700404f7e19d4d6fae6b15215d2913c27955d2b9d6f2c0f537511cd0" +checksum = "e837bd01be2e964e6ffa5bf6110eab5d5162a28a7507cf59486cf31a9714cef5" dependencies = [ - "bitflags", + "bitflags 2.3.3", "libc", ] @@ -905,7 +911,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -932,7 +938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" dependencies = [ "base64", - "bitflags", + "bitflags 1.3.2", "serde", ] @@ -958,7 +964,7 @@ version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno 0.3.1", "io-lifetimes", "libc", @@ -1263,7 +1269,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84f81f436bca4541f4d33172e1202882c9d437db34ed17fc6d84c8ff2bde21f5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "vm-memory", "vmm-sys-util", @@ -1433,7 +1439,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", ] diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index bcbf0b8..6b149e6 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -13,7 +13,7 @@ edition = "2018" byteorder = "1" clap = { version = "4.3", features = ["derive"] } env_logger = "0.10" -epoll = "4.3.1" +epoll = "4.3.2" futures = { version = "0.3", features = ["thread-pool"] } log = "0.4" thiserror = "1.0" From ad40227827023207d9365bef17e5787cbd3a9e55 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 3 Jul 2023 11:12:18 +0530 Subject: [PATCH 076/189] vsock: Fix build error with epoll-4.3.2 With epoll-4.3.2, bitflags moves to 2.3.3 from 1.3.2 and breaks the build with following error: error[E0369]: binary operation `!=` cannot be applied to type `Events` Fix those by using the .bits() functions. Signed-off-by: Viresh Kumar --- crates/vsock/src/vhu_vsock_thread.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/vsock/src/vhu_vsock_thread.rs b/crates/vsock/src/vhu_vsock_thread.rs index 6fabdf4..a6c170d 100644 --- a/crates/vsock/src/vhu_vsock_thread.rs +++ b/crates/vsock/src/vhu_vsock_thread.rs @@ -243,7 +243,7 @@ impl VhostUserVsockThread { self.thread_backend.listener_map.entry(fd) { // New connection from the host - if evset != epoll::Events::EPOLLIN { + if evset.bits() != epoll::Events::EPOLLIN.bits() { // Has to be EPOLLIN as it was not connected previously return; } @@ -312,7 +312,7 @@ impl VhostUserVsockThread { let key = self.thread_backend.listener_map.get(&fd).unwrap(); let conn = self.thread_backend.conn_map.get_mut(key).unwrap(); - if evset == epoll::Events::EPOLLOUT { + if evset.bits() == epoll::Events::EPOLLOUT.bits() { // Flush any remaining data from the tx buffer match conn.tx_buf.flush_to(&mut conn.stream) { Ok(cnt) => { From 147c566ec03827c88a98a178689fd2630b47b74b Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Tue, 4 Jul 2023 07:14:30 +0200 Subject: [PATCH 077/189] build(deps): bump rustix to prevent build error Before bumping, the error was: error[E0554]: `#![feature]` may not be used on the stable release channel --> [...]/rustix-0.37.20/src/lib.rs:101:26 | 101 | #![cfg_attr(rustc_attrs, feature(rustc_attrs))] | ^^^^^^^^^^^^^^^^^^^^ error[E0554]: `#![feature]` may not be used on the stable release channel --> [...]/rustix-0.37.20/src/lib.rs:117:5 | 117 | feature(core_intrinsics) | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0554]: `#![feature]` may not be used on the stable release channel --> [...]/rustix-0.37.20/src/lib.rs:117:13 | 117 | feature(core_intrinsics) | ^^^^^^^^^^^^^^^ For more information about this error, try `rustc --explain E0554`. error: could not compile `rustix` (lib) due to 3 previous errors Bumped using: cargo update -p rustix --aggressive Signed-off-by: Erik Schilling --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d3172a..a95fec2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -610,9 +610,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.146" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "libgpiod" @@ -960,9 +960,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.37.20" +version = "0.37.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" +checksum = "8818fa822adcc98b18fedbb3632a6a33213c070556b5aa7c4c8cc21cff565c4c" dependencies = [ "bitflags 1.3.2", "errno 0.3.1", @@ -1502,9 +1502,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", From 2ebba0910cafc1db730cca3d470011302c9dbe5c Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Tue, 4 Jul 2023 07:17:34 +0200 Subject: [PATCH 078/189] build(deps): switch to released virtio-bindings This version brings the SCSI bindings and allows us to drop the git dependency (which was complicating the packaging situation). Thanks-to: Jiang Liu Signed-off-by: Erik Schilling --- Cargo.lock | 25 ++++++++++--------------- crates/gpio/Cargo.toml | 2 +- crates/i2c/Cargo.toml | 2 +- crates/rng/Cargo.toml | 2 +- crates/scsi/Cargo.toml | 3 +-- crates/vsock/Cargo.toml | 2 +- 6 files changed, 15 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a95fec2..8c240e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1287,7 +1287,7 @@ dependencies = [ "thiserror", "vhost", "vhost-user-backend", - "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "virtio-bindings", "virtio-queue", "vm-memory", "vmm-sys-util", @@ -1304,7 +1304,7 @@ dependencies = [ "thiserror", "vhost", "vhost-user-backend", - "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "virtio-bindings", "virtio-queue", "vm-memory", "vmm-sys-util", @@ -1324,7 +1324,7 @@ dependencies = [ "thiserror", "vhost", "vhost-user-backend", - "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "virtio-bindings", "virtio-queue", "vm-memory", "vmm-sys-util", @@ -1343,7 +1343,7 @@ dependencies = [ "thiserror", "vhost", "vhost-user-backend", - "virtio-bindings 0.2.0 (git+https://github.com/rust-vmm/vm-virtio?rev=467c8ec99375a5f4e08b85b18257cd7e0bac1dc0)", + "virtio-bindings", "virtio-queue", "vm-memory", "vmm-sys-util", @@ -1358,7 +1358,7 @@ dependencies = [ "libc", "log", "vhost", - "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "virtio-bindings", "virtio-queue", "vm-memory", "vmm-sys-util", @@ -1381,7 +1381,7 @@ dependencies = [ "thiserror", "vhost", "vhost-user-backend", - "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "virtio-bindings", "virtio-queue", "virtio-vsock", "vm-memory", @@ -1390,14 +1390,9 @@ dependencies = [ [[package]] name = "virtio-bindings" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9084faf91b9aa9676ae2cac8f1432df2839d9566e6f19f29dbc13a8b831dff" - -[[package]] -name = "virtio-bindings" -version = "0.2.0" -source = "git+https://github.com/rust-vmm/vm-virtio?rev=467c8ec99375a5f4e08b85b18257cd7e0bac1dc0#467c8ec99375a5f4e08b85b18257cd7e0bac1dc0" +checksum = "c18d7b74098a946470ea265b5bacbbf877abc3373021388454de0d47735a5b98" [[package]] name = "virtio-queue" @@ -1406,7 +1401,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91aebb1df33db33cbf04d4c2445e4f78d0b0c8e65acfd16a4ee95ef63ca252f8" dependencies = [ "log", - "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "virtio-bindings", "vm-memory", "vmm-sys-util", ] @@ -1417,7 +1412,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb198c4dd87bf0b4f6b5d8cb41284fca13763a5a1a7e5b8a7ccce45e46d4cf73" dependencies = [ - "virtio-bindings 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "virtio-bindings", "virtio-queue", "vm-memory", ] diff --git a/crates/gpio/Cargo.toml b/crates/gpio/Cargo.toml index edd3049..d3f85a2 100644 --- a/crates/gpio/Cargo.toml +++ b/crates/gpio/Cargo.toml @@ -19,7 +19,7 @@ log = "0.4" thiserror = "1.0" vhost = { version = "0.7", features = ["vhost-user-slave"] } vhost-user-backend = "0.9" -virtio-bindings = "0.2" +virtio-bindings = "0.2.1" virtio-queue = "0.8" vm-memory = "0.11" vmm-sys-util = "0.11" diff --git a/crates/i2c/Cargo.toml b/crates/i2c/Cargo.toml index 0505d88..a610d8a 100644 --- a/crates/i2c/Cargo.toml +++ b/crates/i2c/Cargo.toml @@ -19,7 +19,7 @@ log = "0.4" thiserror = "1.0" vhost = { version = "0.7", features = ["vhost-user-slave"] } vhost-user-backend = "0.9" -virtio-bindings = "0.2" +virtio-bindings = "0.2.1" virtio-queue = "0.8" vm-memory = "0.11" vmm-sys-util = "0.11" diff --git a/crates/rng/Cargo.toml b/crates/rng/Cargo.toml index b39d8f9..043a98b 100644 --- a/crates/rng/Cargo.toml +++ b/crates/rng/Cargo.toml @@ -20,7 +20,7 @@ tempfile = "3.5" thiserror = "1.0" vhost = { version = "0.7", features = ["vhost-user-slave"] } vhost-user-backend = "0.9" -virtio-bindings = "0.2" +virtio-bindings = "0.2.1" virtio-queue = "0.8" vm-memory = "0.11" vmm-sys-util = "0.11" diff --git a/crates/scsi/Cargo.toml b/crates/scsi/Cargo.toml index b1b9d9d..a2194fa 100644 --- a/crates/scsi/Cargo.toml +++ b/crates/scsi/Cargo.toml @@ -20,8 +20,7 @@ num_enum = "0.5" thiserror = "1.0" vhost = { version = "0.7", features = ["vhost-user-slave"] } vhost-user-backend = "0.9" -# until the scsi bindings hit a release, we have to use the commit that adds them as rev. -virtio-bindings = { git = "https://github.com/rust-vmm/vm-virtio", rev = "467c8ec99375a5f4e08b85b18257cd7e0bac1dc0" } +virtio-bindings = "0.2.1" virtio-queue = "0.8" vm-memory = "0.11" vmm-sys-util = "0.11" diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index 6b149e6..04240b1 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -19,7 +19,7 @@ log = "0.4" thiserror = "1.0" vhost = { version = "0.7", features = ["vhost-user-slave"] } vhost-user-backend = "0.9" -virtio-bindings = "0.2" +virtio-bindings = "0.2.1" virtio-queue = "0.8" virtio-vsock = "0.3.0" vm-memory = "0.11" From 827921a07f0eb4a27fb85af74128b2542a2bef99 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Tue, 4 Jul 2023 07:50:45 +0200 Subject: [PATCH 079/189] build(deps): updated epoll to 4.3.3 4.3.2 got yanked [1]. No reason was given, but it looks like a build fix for a bitflags updated [2]. Signed-off-by: Erik Schilling [1] https://crates.io/crates/epoll/4.3.2 [2] https://github.com/nathansizemore/epoll/commit/d3a2304d3822bd94f0fb169ca94b6429472f920e --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c240e2..d358e7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -317,9 +317,9 @@ dependencies = [ [[package]] name = "epoll" -version = "4.3.2" +version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e837bd01be2e964e6ffa5bf6110eab5d5162a28a7507cf59486cf31a9714cef5" +checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79" dependencies = [ "bitflags 2.3.3", "libc", From 0bb783601eed58d1ed82bbb9acb23f5b8de73057 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Tue, 4 Jul 2023 10:13:24 +0200 Subject: [PATCH 080/189] Symlink license files into crate folders Once we publish crates to crates.io, only the crate subfolder is uploaded. Symlink the license files in in order to include them during packaging. `cargo package` will replace the symlinks with the actual files during packaging, so crates.io will include the license file. Signed-off-by: Erik Schilling --- crates/gpio/LICENSE-APACHE | 1 + crates/gpio/LICENSE-BSD-3-Clause | 1 + crates/i2c/LICENSE-APACHE | 1 + crates/i2c/LICENSE-BSD-3-Clause | 1 + crates/rng/LICENSE-APACHE | 1 + crates/rng/LICENSE-BSD-3-Clause | 1 + crates/scsi/LICENSE-APACHE | 1 + crates/scsi/LICENSE-BSD-3-Clause | 1 + crates/vsock/LICENSE-APACHE | 1 + crates/vsock/LICENSE-BSD-3-Clause | 1 + 10 files changed, 10 insertions(+) create mode 120000 crates/gpio/LICENSE-APACHE create mode 120000 crates/gpio/LICENSE-BSD-3-Clause create mode 120000 crates/i2c/LICENSE-APACHE create mode 120000 crates/i2c/LICENSE-BSD-3-Clause create mode 120000 crates/rng/LICENSE-APACHE create mode 120000 crates/rng/LICENSE-BSD-3-Clause create mode 120000 crates/scsi/LICENSE-APACHE create mode 120000 crates/scsi/LICENSE-BSD-3-Clause create mode 120000 crates/vsock/LICENSE-APACHE create mode 120000 crates/vsock/LICENSE-BSD-3-Clause diff --git a/crates/gpio/LICENSE-APACHE b/crates/gpio/LICENSE-APACHE new file mode 120000 index 0000000..1cd601d --- /dev/null +++ b/crates/gpio/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/crates/gpio/LICENSE-BSD-3-Clause b/crates/gpio/LICENSE-BSD-3-Clause new file mode 120000 index 0000000..a60f1af --- /dev/null +++ b/crates/gpio/LICENSE-BSD-3-Clause @@ -0,0 +1 @@ +../../LICENSE-BSD-3-Clause \ No newline at end of file diff --git a/crates/i2c/LICENSE-APACHE b/crates/i2c/LICENSE-APACHE new file mode 120000 index 0000000..1cd601d --- /dev/null +++ b/crates/i2c/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/crates/i2c/LICENSE-BSD-3-Clause b/crates/i2c/LICENSE-BSD-3-Clause new file mode 120000 index 0000000..a60f1af --- /dev/null +++ b/crates/i2c/LICENSE-BSD-3-Clause @@ -0,0 +1 @@ +../../LICENSE-BSD-3-Clause \ No newline at end of file diff --git a/crates/rng/LICENSE-APACHE b/crates/rng/LICENSE-APACHE new file mode 120000 index 0000000..1cd601d --- /dev/null +++ b/crates/rng/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/crates/rng/LICENSE-BSD-3-Clause b/crates/rng/LICENSE-BSD-3-Clause new file mode 120000 index 0000000..a60f1af --- /dev/null +++ b/crates/rng/LICENSE-BSD-3-Clause @@ -0,0 +1 @@ +../../LICENSE-BSD-3-Clause \ No newline at end of file diff --git a/crates/scsi/LICENSE-APACHE b/crates/scsi/LICENSE-APACHE new file mode 120000 index 0000000..1cd601d --- /dev/null +++ b/crates/scsi/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/crates/scsi/LICENSE-BSD-3-Clause b/crates/scsi/LICENSE-BSD-3-Clause new file mode 120000 index 0000000..a60f1af --- /dev/null +++ b/crates/scsi/LICENSE-BSD-3-Clause @@ -0,0 +1 @@ +../../LICENSE-BSD-3-Clause \ No newline at end of file diff --git a/crates/vsock/LICENSE-APACHE b/crates/vsock/LICENSE-APACHE new file mode 120000 index 0000000..1cd601d --- /dev/null +++ b/crates/vsock/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/crates/vsock/LICENSE-BSD-3-Clause b/crates/vsock/LICENSE-BSD-3-Clause new file mode 120000 index 0000000..a60f1af --- /dev/null +++ b/crates/vsock/LICENSE-BSD-3-Clause @@ -0,0 +1 @@ +../../LICENSE-BSD-3-Clause \ No newline at end of file From 462a62148511e0fe7de69dee89966631f5f79791 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 23 Mar 2023 10:41:31 +0530 Subject: [PATCH 081/189] Add support for Xen builds Various rust-vmm dependencies now support Xen platforms under a new feature `xen`. Add the same here for all the crates. Signed-off-by: Viresh Kumar --- Cargo.lock | 23 +++++++++++++---------- README.md | 11 ++++++++++- crates/gpio/Cargo.toml | 15 +++++++++------ crates/i2c/Cargo.toml | 15 +++++++++------ crates/rng/Cargo.toml | 15 +++++++++------ crates/scsi/Cargo.toml | 11 +++++++---- crates/vsock/Cargo.toml | 15 +++++++++------ 7 files changed, 66 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d358e7e..a027ac2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1265,9 +1265,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vhost" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f81f436bca4541f4d33172e1202882c9d437db34ed17fc6d84c8ff2bde21f5" +checksum = "73832f4d8d636d63d9b145e8ef22a2c50b93f4d24eb7a99c9e6781b1b08549cf" dependencies = [ "bitflags 1.3.2", "libc", @@ -1351,9 +1351,9 @@ dependencies = [ [[package]] name = "vhost-user-backend" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d3b7affe04f61d19b03c5db823287855789b687218fec139699a0c7f7f2790" +checksum = "e3ea9d5e8ec847cde4df1c04e586698a479706fd6beca37323f9d425b24b4c2f" dependencies = [ "libc", "log", @@ -1396,9 +1396,9 @@ checksum = "c18d7b74098a946470ea265b5bacbbf877abc3373021388454de0d47735a5b98" [[package]] name = "virtio-queue" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91aebb1df33db33cbf04d4c2445e4f78d0b0c8e65acfd16a4ee95ef63ca252f8" +checksum = "35aca00da06841bd99162c381ec65893cace23ca0fb89254302cfe4bec4c300f" dependencies = [ "log", "virtio-bindings", @@ -1408,9 +1408,9 @@ dependencies = [ [[package]] name = "virtio-vsock" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb198c4dd87bf0b4f6b5d8cb41284fca13763a5a1a7e5b8a7ccce45e46d4cf73" +checksum = "c92d1d0c0db339e03dc275e86e5de2654ed94b351f02d405a3a0260dfc1b839f" dependencies = [ "virtio-bindings", "virtio-queue", @@ -1419,12 +1419,15 @@ dependencies = [ [[package]] name = "vm-memory" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6ea57fe00f9086c59eeeb68e102dd611686bc3c28520fa465996d4d4bdce07" +checksum = "a77c7a0891cbac53618f5f6eec650ed1dc4f7e506bbe14877aff49d94b8408b0" dependencies = [ "arc-swap", + "bitflags 1.3.2", "libc", + "thiserror", + "vmm-sys-util", "winapi", ] diff --git a/README.md b/README.md index 018cc19..9c6e42f 100644 --- a/README.md +++ b/README.md @@ -61,4 +61,13 @@ In order to inform tools about the build location, you can now set: To prevent setting this in every terminal session, you can also configure cargo to -[set it automatically](https://doc.rust-lang.org/cargo/reference/config.html#env). \ No newline at end of file +[set it automatically](https://doc.rust-lang.org/cargo/reference/config.html#env). + +## Xen support + +Supporting Xen requires special handling while mapping the guest memory. The +`vm-memory` crate implements xen memory mapping support via a separate feature +`xen`, and this crate uses the same feature name to enable Xen support. + +It was decided by the `rust-vmm` maintainers to keep the interface simple and +build the crate for either standard Unix memory mapping or Xen, and not both. diff --git a/crates/gpio/Cargo.toml b/crates/gpio/Cargo.toml index d3f85a2..8dd607b 100644 --- a/crates/gpio/Cargo.toml +++ b/crates/gpio/Cargo.toml @@ -11,22 +11,25 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] + [dependencies] clap = { version = "4.3", features = ["derive"] } env_logger = "0.10" libc = "0.2" log = "0.4" thiserror = "1.0" -vhost = { version = "0.7", features = ["vhost-user-slave"] } -vhost-user-backend = "0.9" +vhost = { version = "0.8", features = ["vhost-user-slave"] } +vhost-user-backend = "0.10" virtio-bindings = "0.2.1" -virtio-queue = "0.8" -vm-memory = "0.11" +virtio-queue = "0.9" +vm-memory = "0.12" vmm-sys-util = "0.11" [target.'cfg(target_env = "gnu")'.dependencies] libgpiod = { version = "0.1" } [dev-dependencies] -virtio-queue = { version = "0.8", features = ["test-utils"] } -vm-memory = { version = "0.11", features = ["backend-mmap", "backend-atomic"] } +virtio-queue = { version = "0.9", features = ["test-utils"] } +vm-memory = { version = "0.12", features = ["backend-mmap", "backend-atomic"] } diff --git a/crates/i2c/Cargo.toml b/crates/i2c/Cargo.toml index a610d8a..bbf6487 100644 --- a/crates/i2c/Cargo.toml +++ b/crates/i2c/Cargo.toml @@ -11,19 +11,22 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] + [dependencies] clap = { version = "4.3", features = ["derive"] } env_logger = "0.10" libc = "0.2" log = "0.4" thiserror = "1.0" -vhost = { version = "0.7", features = ["vhost-user-slave"] } -vhost-user-backend = "0.9" +vhost = { version = "0.8", features = ["vhost-user-slave"] } +vhost-user-backend = "0.10" virtio-bindings = "0.2.1" -virtio-queue = "0.8" -vm-memory = "0.11" +virtio-queue = "0.9" +vm-memory = "0.12" vmm-sys-util = "0.11" [dev-dependencies] -virtio-queue = { version = "0.8", features = ["test-utils"] } -vm-memory = { version = "0.11", features = ["backend-mmap", "backend-atomic"] } +virtio-queue = { version = "0.9", features = ["test-utils"] } +vm-memory = { version = "0.12", features = ["backend-mmap", "backend-atomic"] } diff --git a/crates/rng/Cargo.toml b/crates/rng/Cargo.toml index 043a98b..dd71b18 100644 --- a/crates/rng/Cargo.toml +++ b/crates/rng/Cargo.toml @@ -9,6 +9,9 @@ keywords = ["rng", "vhost", "virt", "backend"] license = "Apache-2.0 OR BSD-3-Clause" edition = "2021" +[features] +xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] + [dependencies] clap = { version = "4.3", features = ["derive"] } env_logger = "0.10" @@ -18,13 +21,13 @@ log = "0.4" rand = "0.8.5" tempfile = "3.5" thiserror = "1.0" -vhost = { version = "0.7", features = ["vhost-user-slave"] } -vhost-user-backend = "0.9" +vhost = { version = "0.8", features = ["vhost-user-slave"] } +vhost-user-backend = "0.10" virtio-bindings = "0.2.1" -virtio-queue = "0.8" -vm-memory = "0.11" +virtio-queue = "0.9" +vm-memory = "0.12" vmm-sys-util = "0.11" [dev-dependencies] -virtio-queue = { version = "0.8", features = ["test-utils"] } -vm-memory = { version = "0.11", features = ["backend-mmap", "backend-atomic"] } +virtio-queue = { version = "0.9", features = ["test-utils"] } +vm-memory = { version = "0.12", features = ["backend-mmap", "backend-atomic"] } diff --git a/crates/scsi/Cargo.toml b/crates/scsi/Cargo.toml index a2194fa..472935a 100644 --- a/crates/scsi/Cargo.toml +++ b/crates/scsi/Cargo.toml @@ -11,6 +11,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] + [dependencies] clap = { version = "4.3", features = ["derive"] } env_logger = "0.10" @@ -18,11 +21,11 @@ epoll = "4.3" log = "0.4" num_enum = "0.5" thiserror = "1.0" -vhost = { version = "0.7", features = ["vhost-user-slave"] } -vhost-user-backend = "0.9" +vhost = { version = "0.8", features = ["vhost-user-slave"] } +vhost-user-backend = "0.10" virtio-bindings = "0.2.1" -virtio-queue = "0.8" -vm-memory = "0.11" +virtio-queue = "0.9" +vm-memory = "0.12" vmm-sys-util = "0.11" [dev-dependencies] diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index 04240b1..624e252 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -9,6 +9,9 @@ keywords = ["vhost", "vsock"] license = "Apache-2.0 OR BSD-3-Clause" edition = "2018" +[features] +xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] + [dependencies] byteorder = "1" clap = { version = "4.3", features = ["derive"] } @@ -17,17 +20,17 @@ epoll = "4.3.2" futures = { version = "0.3", features = ["thread-pool"] } log = "0.4" thiserror = "1.0" -vhost = { version = "0.7", features = ["vhost-user-slave"] } -vhost-user-backend = "0.9" +vhost = { version = "0.8", features = ["vhost-user-slave"] } +vhost-user-backend = "0.10" virtio-bindings = "0.2.1" -virtio-queue = "0.8" -virtio-vsock = "0.3.0" -vm-memory = "0.11" +virtio-queue = "0.9" +virtio-vsock = "0.3.1" +vm-memory = "0.12" vmm-sys-util = "0.11" config = "0.13" serde = "1" serde_yaml = "0.9" [dev-dependencies] -virtio-queue = { version = "0.8", features = ["test-utils"] } +virtio-queue = { version = "0.9", features = ["test-utils"] } serial_test = "2.0" From 055f27f4ab4b261bbcb1f637659972e015960721 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 6 Jul 2023 09:42:40 +0530 Subject: [PATCH 082/189] Run Cargo update Cargo audit gives an error currently: Crate: hermit-abi Version: 0.3.1 Warning: yanked Fix it by updating the dependencies forcefully. Signed-off-by: Viresh Kumar --- Cargo.lock | 145 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 87 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a027ac2..fafaafe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,13 +79,13 @@ checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -183,9 +183,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.8" +version = "4.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9394150f5b4273a1763355bd1c2ec54cc5a2593f790587bcd6b2c947cfa9211" +checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" dependencies = [ "clap_builder", "clap_derive", @@ -194,13 +194,12 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.8" +version = "4.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a78fbdd3cc2914ddf37ba444114bc7765bbdcb55ec9cbe6fa054f0137400717" +checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" dependencies = [ "anstream", "anstyle", - "bitflags 1.3.2", "clap_lex", "strsim", ] @@ -214,7 +213,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -250,9 +249,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -429,7 +428,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -521,9 +520,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "humantime" @@ -569,21 +568,20 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb" dependencies = [ "hermit-abi", - "io-lifetimes", - "rustix", + "rustix 0.38.3", "windows-sys", ] [[package]] name = "itoa" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0aa48fab2893d8a49caa94082ae8488f4e1050d73b367881dcd2198f4199fd8" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" [[package]] name = "json5" @@ -659,6 +657,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + [[package]] name = "lock_api" version = "0.4.10" @@ -809,7 +813,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -825,9 +829,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -859,18 +863,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.28" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] @@ -916,9 +920,21 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.4" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +checksum = "89089e897c013b3deb627116ae56a6955a72b8bed395c9526af31c9fe528b484" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa250384981ea14565685dea16a9ccc4d1c541a13f82b9c168572264d1df8c56" dependencies = [ "aho-corasick", "memchr", @@ -927,9 +943,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" [[package]] name = "ron" @@ -960,23 +976,36 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.37.22" +version = "0.37.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8818fa822adcc98b18fedbb3632a6a33213c070556b5aa7c4c8cc21cff565c4c" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" dependencies = [ "bitflags 1.3.2", "errno 0.3.1", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.8", + "windows-sys", +] + +[[package]] +name = "rustix" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +dependencies = [ + "bitflags 2.3.3", + "errno 0.3.1", + "libc", + "linux-raw-sys 0.4.3", "windows-sys", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" [[package]] name = "scopeguard" @@ -986,29 +1015,29 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.164" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] name = "serde_json" -version = "1.0.97" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" +checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" dependencies = [ "itoa", "ryu", @@ -1050,7 +1079,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -1081,9 +1110,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "strsim" @@ -1122,9 +1151,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.18" +version = "2.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" dependencies = [ "proc-macro2", "quote", @@ -1156,7 +1185,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall", - "rustix", + "rustix 0.37.23", "windows-sys", ] @@ -1171,22 +1200,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -1206,9 +1235,9 @@ checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.19.11" +version = "0.19.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" +checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" dependencies = [ "indexmap", "toml_datetime", @@ -1229,9 +1258,9 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] name = "unicode-segmentation" From 7a371edc3e981b2f63ce29d8bc5074b16b8f3760 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Thu, 6 Jul 2023 18:05:44 +0200 Subject: [PATCH 083/189] Add CHANGELOG.md to all crates In order to prepare for an initial release of the crates, this adds a CHANGELOG.md for tracking changes in the future releases. The template was copied from the vhost crate [1]. Signed-off-by: Erik Schilling [1] https://github.com/rust-vmm/vhost/blob/900b9a5c4197a82b6c75a56faf728159d1a3dafc/crates/vhost/CHANGELOG.md --- crates/gpio/CHANGELOG.md | 15 +++++++++++++++ crates/i2c/CHANGELOG.md | 15 +++++++++++++++ crates/rng/CHANGELOG.md | 15 +++++++++++++++ crates/scsi/CHANGELOG.md | 16 ++++++++++++++-- crates/vsock/CHANGELOG.md | 15 +++++++++++++++ 5 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 crates/gpio/CHANGELOG.md create mode 100644 crates/i2c/CHANGELOG.md create mode 100644 crates/rng/CHANGELOG.md create mode 100644 crates/vsock/CHANGELOG.md diff --git a/crates/gpio/CHANGELOG.md b/crates/gpio/CHANGELOG.md new file mode 100644 index 0000000..51d3f04 --- /dev/null +++ b/crates/gpio/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog +## [Unreleased] + +### Added + +### Changed + +### Fixed + +### Deprecated + +## [0.1.0] + +First release + diff --git a/crates/i2c/CHANGELOG.md b/crates/i2c/CHANGELOG.md new file mode 100644 index 0000000..51d3f04 --- /dev/null +++ b/crates/i2c/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog +## [Unreleased] + +### Added + +### Changed + +### Fixed + +### Deprecated + +## [0.1.0] + +First release + diff --git a/crates/rng/CHANGELOG.md b/crates/rng/CHANGELOG.md new file mode 100644 index 0000000..51d3f04 --- /dev/null +++ b/crates/rng/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog +## [Unreleased] + +### Added + +### Changed + +### Fixed + +### Deprecated + +## [0.1.0] + +First release + diff --git a/crates/scsi/CHANGELOG.md b/crates/scsi/CHANGELOG.md index d471959..51d3f04 100644 --- a/crates/scsi/CHANGELOG.md +++ b/crates/scsi/CHANGELOG.md @@ -1,3 +1,15 @@ -# Upcoming Release +# Changelog +## [Unreleased] + +### Added + +### Changed + +### Fixed + +### Deprecated + +## [0.1.0] + +First release -- First initial daemon implementation. diff --git a/crates/vsock/CHANGELOG.md b/crates/vsock/CHANGELOG.md new file mode 100644 index 0000000..51d3f04 --- /dev/null +++ b/crates/vsock/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog +## [Unreleased] + +### Added + +### Changed + +### Fixed + +### Deprecated + +## [0.1.0] + +First release + From 83f9e61c0918bc02fccb994ab6a26c9f72df2fb5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 04:55:24 +0000 Subject: [PATCH 084/189] build(deps): bump ucd-trie from 0.1.5 to 0.1.6 Bumps [ucd-trie](https://github.com/BurntSushi/ucd-generate) from 0.1.5 to 0.1.6. - [Commits](https://github.com/BurntSushi/ucd-generate/compare/ucd-util-0.1.5...ucd-trie-0.1.6) --- updated-dependencies: - dependency-name: ucd-trie dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fafaafe..728baf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1252,9 +1252,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicode-ident" From 0d08c5a3345e98bfb00f0b58b57b23661f1b4395 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 04:53:55 +0000 Subject: [PATCH 085/189] build(deps): bump serde from 1.0.166 to 1.0.168 Bumps [serde](https://github.com/serde-rs/serde) from 1.0.166 to 1.0.168. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.166...v1.0.168) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 728baf9..69c5a35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1015,18 +1015,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.166" +version = "1.0.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8" +checksum = "d614f89548720367ded108b3c843be93f3a341e22d5674ca0dd5cd57f34926af" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.166" +version = "1.0.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6" +checksum = "d4fe589678c688e44177da4f27152ee2d190757271dc7f1d5b6b9f68d869d641" dependencies = [ "proc-macro2", "quote", From 0b7f5369985460caea9e21c094cf333b68b00257 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 04:54:53 +0000 Subject: [PATCH 086/189] build(deps): bump regex from 1.9.0 to 1.9.1 Bumps [regex](https://github.com/rust-lang/regex) from 1.9.0 to 1.9.1. - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/1.9.0...1.9.1) --- updated-dependencies: - dependency-name: regex dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69c5a35..5f369c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -920,9 +920,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89089e897c013b3deb627116ae56a6955a72b8bed395c9526af31c9fe528b484" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", @@ -932,9 +932,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa250384981ea14565685dea16a9ccc4d1c541a13f82b9c168572264d1df8c56" +checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" dependencies = [ "aho-corasick", "memchr", From e24d3e52fc64384f9eb1deb25c43689dda9d5044 Mon Sep 17 00:00:00 2001 From: Ramyak Mehra Date: Sat, 15 Apr 2023 01:44:39 +0530 Subject: [PATCH 087/189] vsock: reduce credit update messages sent to guest Earlier we were sending credit update messages to guest everytime data was read, now we send it only if the available space is less than 1/4th of the tx buffer size. benchmarks: host->guest[Gbps] before 1.45 after 1.51 guest->host[Gbps] before 5.68 after 6.07 Note: I did 3 runs of 10 secs and took average of all, they are relative results. Fixes #317 Signed-off-by: Ramyak Mehra [SG: fixed S-o-b and small things in the commit description] Signed-off-by: Stefano Garzarella --- crates/vsock/src/vsock_conn.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/crates/vsock/src/vsock_conn.rs b/crates/vsock/src/vsock_conn.rs index 25e3335..834ece8 100644 --- a/crates/vsock/src/vsock_conn.rs +++ b/crates/vsock/src/vsock_conn.rs @@ -52,6 +52,8 @@ pub(crate) struct VsockConnection { pub epoll_fd: RawFd, /// Local tx buffer. pub tx_buf: LocalTxBuf, + /// Local tx buffer size + tx_buffer_size: u32, } impl VsockConnection { @@ -81,6 +83,7 @@ impl VsockConnection { rx_cnt: Wrapping(0), epoll_fd, tx_buf: LocalTxBuf::new(tx_buffer_size), + tx_buffer_size, } } @@ -114,6 +117,7 @@ impl VsockConnection { rx_cnt: Wrapping(0), epoll_fd, tx_buf: LocalTxBuf::new(tx_buffer_size), + tx_buffer_size, } } @@ -308,8 +312,18 @@ impl VsockConnection { if written_count > 0 { // Increment forwarded count by number of bytes written to the stream self.fwd_cnt += Wrapping(written_count as u32); - // TODO: https://github.com/torvalds/linux/commit/c69e6eafff5f725bc29dcb8b52b6782dca8ea8a2 - self.rx_queue.enqueue(RxOps::CreditUpdate); + + // At what point in available credits should we send a credit update. + // This is set to 1/4th of the tx buffer size. If we keep it too low, + // we will end up sending too many credit updates. If we keep it too + // high, we will end up sending too few credit updates and cause stalls. + // Stalls are more bad than too many credit updates. + let free_space = self + .tx_buffer_size + .wrapping_sub((self.fwd_cnt - self.last_fwd_cnt).0); + if free_space < self.tx_buffer_size / 4 { + self.rx_queue.enqueue(RxOps::CreditUpdate); + } } if written_count != buf.len() { @@ -332,7 +346,7 @@ impl VsockConnection { .set_src_port(self.local_port) .set_dst_port(self.peer_port) .set_type(VSOCK_TYPE_STREAM) - .set_buf_alloc(self.tx_buf.get_buf_size()) + .set_buf_alloc(self.tx_buffer_size) .set_fwd_cnt(self.fwd_cnt.0) } From 7f809eeab8b50793a49c7c1c16205da531971152 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 10 Jul 2023 15:59:36 +0300 Subject: [PATCH 088/189] vsock: fix use-after-close RawFd unsafe {} use Using a raw file descriptor in `stream_map` caused two `UnixStream` instances have a shared single reference to the underlying stream socket. Use UnixStream::try_clone instead which duplicates the file descriptor while still referring to the same stream. Fixes #232 Signed-off-by: Manos Pitsidianakis --- crates/vsock/src/thread_backend.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/vsock/src/thread_backend.rs b/crates/vsock/src/thread_backend.rs index 7defac9..5a13ed1 100644 --- a/crates/vsock/src/thread_backend.rs +++ b/crates/vsock/src/thread_backend.rs @@ -4,7 +4,7 @@ use std::{ collections::{HashMap, HashSet, VecDeque}, os::unix::{ net::UnixStream, - prelude::{AsRawFd, FromRawFd, RawFd}, + prelude::{AsRawFd, RawFd}, }, sync::{Arc, RwLock}, }; @@ -292,7 +292,7 @@ impl VsockThreadBackend { .insert(stream_fd, ConnMapKey::new(pkt.dst_port(), pkt.src_port())); let conn = VsockConnection::new_peer_init( - stream, + stream.try_clone().map_err(Error::UnixConnect)?, pkt.dst_cid(), pkt.dst_port(), pkt.src_cid(), @@ -307,11 +307,7 @@ impl VsockThreadBackend { self.backend_rxq .push_back(ConnMapKey::new(pkt.dst_port(), pkt.src_port())); - self.stream_map.insert( - stream_fd, - // SAFETY: Safe as the file descriptor is guaranteed to be valid. - unsafe { UnixStream::from_raw_fd(stream_fd) }, - ); + self.stream_map.insert(stream_fd, stream); self.local_port_set.insert(pkt.dst_port()); VhostUserVsockThread::epoll_register( From 8a1deef49a5de85a0b436d839ae68dcfc4cbded8 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 10 Jul 2023 15:47:49 +0300 Subject: [PATCH 089/189] vsock: use tempfile in tests Tests were run in a series before the previous commit because of a testing failure (#232), and masked a bug. This is not necessary anymore since the bug was fixed in the previous commit. Instead of reverting to running the tests without the #[serial] attribute, make every test self-contained by using unique temp directories in each test run. Test files that refer to sockets need to be unique because they risk sharing filenames with other tests after refactoring. Since these tests create/use/free resources, they should take care not to litter /tmp/ and not share any file with other tests. This commit uses a unique temp dir as location of test run files instead of `/tmp/`. It adds a new dev-dependency, `tempfile`. Signed-off-by: Manos Pitsidianakis --- Cargo.lock | 85 +------------------ crates/vsock/Cargo.toml | 2 +- crates/vsock/src/main.rs | 120 +++++++++++++++++++-------- crates/vsock/src/thread_backend.rs | 58 ++++++++----- crates/vsock/src/vhu_vsock.rs | 54 ++++++++---- crates/vsock/src/vhu_vsock_thread.rs | 32 ++++--- crates/vsock/src/vsock_conn.rs | 6 -- 7 files changed, 185 insertions(+), 172 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5f369c5..720117f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,19 +266,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "dashmap" -version = "5.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" -dependencies = [ - "cfg-if", - "hashbrown 0.12.3", - "lock_api", - "once_cell", - "parking_lot_core", -] - [[package]] name = "digest" version = "0.10.7" @@ -663,16 +650,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.19" @@ -748,29 +725,6 @@ dependencies = [ "hashbrown 0.12.3", ] -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - [[package]] name = "pathdiff" version = "0.2.1" @@ -1007,12 +961,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - [[package]] name = "serde" version = "1.0.168" @@ -1057,31 +1005,6 @@ dependencies = [ "unsafe-libyaml", ] -[[package]] -name = "serial_test" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d" -dependencies = [ - "dashmap", - "futures", - "lazy_static", - "log", - "parking_lot", - "serial_test_derive", -] - -[[package]] -name = "serial_test_derive" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.23", -] - [[package]] name = "sha2" version = "0.10.7" @@ -1108,12 +1031,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - [[package]] name = "strsim" version = "0.10.0" @@ -1406,7 +1323,7 @@ dependencies = [ "log", "serde", "serde_yaml", - "serial_test", + "tempfile", "thiserror", "vhost", "vhost-user-backend", diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index 624e252..55e4da7 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -33,4 +33,4 @@ serde_yaml = "0.9" [dev-dependencies] virtio-queue = { version = "0.9", features = ["test-utils"] } -serial_test = "2.0" +tempfile = "3.6.0" diff --git a/crates/vsock/src/main.rs b/crates/vsock/src/main.rs index eb2d7e3..98d8751 100644 --- a/crates/vsock/src/main.rs +++ b/crates/vsock/src/main.rs @@ -260,9 +260,9 @@ fn main() { #[cfg(test)] mod tests { use super::*; - use serial_test::serial; use std::fs::File; use std::io::Write; + use tempfile::tempdir; impl VsockArgs { fn from_args(guest_cid: u64, socket: &str, uds_path: &str, tx_buffer_size: u32) -> Self { @@ -287,9 +287,12 @@ mod tests { } #[test] - #[serial] fn test_vsock_config_setup() { - let args = VsockArgs::from_args(3, "/tmp/vhost4.socket", "/tmp/vm4.vsock", 64 * 1024); + let test_dir = tempdir().expect("Could not create a temp test directory."); + + let socket_path = test_dir.path().join("vhost4.socket").display().to_string(); + let uds_path = test_dir.path().join("vm4.vsock").display().to_string(); + let args = VsockArgs::from_args(3, &socket_path, &uds_path, 64 * 1024); let configs = Vec::::try_from(args); assert!(configs.is_ok()); @@ -299,17 +302,38 @@ mod tests { let config = &configs[0]; assert_eq!(config.get_guest_cid(), 3); - assert_eq!(config.get_socket_path(), "/tmp/vhost4.socket"); - assert_eq!(config.get_uds_path(), "/tmp/vm4.vsock"); + assert_eq!(config.get_socket_path(), socket_path); + assert_eq!(config.get_uds_path(), uds_path); assert_eq!(config.get_tx_buffer_size(), 64 * 1024); + + test_dir.close().unwrap(); } #[test] - #[serial] fn test_vsock_config_setup_from_vm_args() { - let params = "--vm socket=/tmp/vhost3.socket,uds_path=/tmp/vm3.vsock \ - --vm socket=/tmp/vhost4.socket,uds-path=/tmp/vm4.vsock,guest-cid=4,tx_buffer_size=65536 \ - --vm guest-cid=5,socket=/tmp/vhost5.socket,uds_path=/tmp/vm5.vsock,tx-buffer-size=32768"; + let test_dir = tempdir().expect("Could not create a temp test directory."); + + let socket_paths = [ + test_dir.path().join("vhost3.socket"), + test_dir.path().join("vhost4.socket"), + test_dir.path().join("vhost5.socket"), + ]; + let uds_paths = [ + test_dir.path().join("vm3.vsock"), + test_dir.path().join("vm4.vsock"), + test_dir.path().join("vm5.vsock"), + ]; + let params = format!( + "--vm socket={vhost3_socket},uds_path={vm3_vsock} \ + --vm socket={vhost4_socket},uds-path={vm4_vsock},guest-cid=4,tx_buffer_size=65536 \ + --vm guest-cid=5,socket={vhost5_socket},uds_path={vm5_vsock},tx-buffer-size=32768", + vhost3_socket = socket_paths[0].display(), + vhost4_socket = socket_paths[1].display(), + vhost5_socket = socket_paths[2].display(), + vm3_vsock = uds_paths[0].display(), + vm4_vsock = uds_paths[1].display(), + vm5_vsock = uds_paths[2].display(), + ); let mut params = params.split_whitespace().collect::>(); params.insert(0, ""); // to make the test binary name agnostic @@ -324,61 +348,89 @@ mod tests { let config = configs.get(0).unwrap(); assert_eq!(config.get_guest_cid(), 3); - assert_eq!(config.get_socket_path(), "/tmp/vhost3.socket"); - assert_eq!(config.get_uds_path(), "/tmp/vm3.vsock"); + assert_eq!( + config.get_socket_path(), + socket_paths[0].display().to_string() + ); + assert_eq!(config.get_uds_path(), uds_paths[0].display().to_string()); assert_eq!(config.get_tx_buffer_size(), 65536); let config = configs.get(1).unwrap(); assert_eq!(config.get_guest_cid(), 4); - assert_eq!(config.get_socket_path(), "/tmp/vhost4.socket"); - assert_eq!(config.get_uds_path(), "/tmp/vm4.vsock"); + assert_eq!( + config.get_socket_path(), + socket_paths[1].display().to_string() + ); + assert_eq!(config.get_uds_path(), uds_paths[1].display().to_string()); assert_eq!(config.get_tx_buffer_size(), 65536); let config = configs.get(2).unwrap(); assert_eq!(config.get_guest_cid(), 5); - assert_eq!(config.get_socket_path(), "/tmp/vhost5.socket"); - assert_eq!(config.get_uds_path(), "/tmp/vm5.vsock"); + assert_eq!( + config.get_socket_path(), + socket_paths[2].display().to_string() + ); + assert_eq!(config.get_uds_path(), uds_paths[2].display().to_string()); assert_eq!(config.get_tx_buffer_size(), 32768); + + test_dir.close().unwrap(); } #[test] - #[serial] fn test_vsock_config_setup_from_file() { - let mut yaml = File::create("./config.yaml").unwrap(); + let test_dir = tempdir().expect("Could not create a temp test directory."); + + let config_path = test_dir.path().join("config.yaml"); + let socket_path = test_dir.path().join("vhost4.socket"); + let uds_path = test_dir.path().join("vm4.vsock"); + + let mut yaml = File::create(&config_path).unwrap(); yaml.write_all( - b"vms: + format!( + "vms: - guest_cid: 4 - socket: /tmp/vhost4.socket - uds_path: /tmp/vm4.vsock + socket: {} + uds_path: {} tx_buffer_size: 65536", + socket_path.display(), + uds_path.display(), + ) + .as_bytes(), ) .unwrap(); - let args = VsockArgs::from_file("./config.yaml"); + let args = VsockArgs::from_file(&config_path.display().to_string()); let configs = Vec::::try_from(args).unwrap(); assert_eq!(configs.len(), 1); let config = &configs[0]; assert_eq!(config.get_guest_cid(), 4); - assert_eq!(config.get_socket_path(), "/tmp/vhost4.socket"); - assert_eq!(config.get_uds_path(), "/tmp/vm4.vsock"); - std::fs::remove_file("./config.yaml").unwrap(); + assert_eq!(config.get_socket_path(), socket_path.display().to_string()); + assert_eq!(config.get_uds_path(), uds_path.display().to_string()); + std::fs::remove_file(&config_path).unwrap(); + + test_dir.close().unwrap(); } #[test] - #[serial] fn test_vsock_server() { const CID: u64 = 3; - const VHOST_SOCKET_PATH: &str = "test_vsock_server.socket"; - const VSOCK_SOCKET_PATH: &str = "test_vsock_server.vsock"; const CONN_TX_BUF_SIZE: u32 = 64 * 1024; - let config = VsockConfig::new( - CID, - VHOST_SOCKET_PATH.to_string(), - VSOCK_SOCKET_PATH.to_string(), - CONN_TX_BUF_SIZE, - ); + let test_dir = tempdir().expect("Could not create a temp test directory."); + + let vhost_socket_path = test_dir + .path() + .join("test_vsock_server.socket") + .display() + .to_string(); + let vsock_socket_path = test_dir + .path() + .join("test_vsock_server.vsock") + .display() + .to_string(); + + let config = VsockConfig::new(CID, vhost_socket_path, vsock_socket_path, CONN_TX_BUF_SIZE); let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); @@ -398,5 +450,7 @@ mod tests { assert_eq!(backend.threads.len(), 1); assert_eq!(vring_workers.len(), backend.threads.len()); + + test_dir.close().unwrap(); } } diff --git a/crates/vsock/src/thread_backend.rs b/crates/vsock/src/thread_backend.rs index 5a13ed1..5029465 100644 --- a/crates/vsock/src/thread_backend.rs +++ b/crates/vsock/src/thread_backend.rs @@ -329,30 +329,31 @@ impl VsockThreadBackend { mod tests { use super::*; use crate::vhu_vsock::{VhostUserVsockBackend, VsockConfig, VSOCK_OP_RW}; - use serial_test::serial; use std::os::unix::net::UnixListener; + use tempfile::tempdir; use virtio_vsock::packet::{VsockPacket, PKT_HEADER_SIZE}; const DATA_LEN: usize = 16; const CONN_TX_BUF_SIZE: u32 = 64 * 1024; #[test] - #[serial] fn test_vsock_thread_backend() { const CID: u64 = 3; - const VSOCK_SOCKET_PATH: &str = "test_vsock_thread_backend.vsock"; const VSOCK_PEER_PORT: u32 = 1234; - const VSOCK_PEER_PATH: &str = "test_vsock_thread_backend.vsock_1234"; - let _ = std::fs::remove_file(VSOCK_PEER_PATH); - let _listener = UnixListener::bind(VSOCK_PEER_PATH).unwrap(); + let test_dir = tempdir().expect("Could not create a temp test directory."); + + let vsock_socket_path = test_dir.path().join("test_vsock_thread_backend.vsock"); + let vsock_peer_path = test_dir.path().join("test_vsock_thread_backend.vsock_1234"); + + let _listener = UnixListener::bind(&vsock_peer_path).unwrap(); let epoll_fd = epoll::create(false).unwrap(); let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); let mut vtp = VsockThreadBackend::new( - VSOCK_SOCKET_PATH.to_string(), + vsock_socket_path.display().to_string(), epoll_fd, CID, CONN_TX_BUF_SIZE, @@ -394,26 +395,42 @@ mod tests { assert!(vtp.recv_pkt(&mut packet).is_ok()); // cleanup - let _ = std::fs::remove_file(VSOCK_PEER_PATH); + let _ = std::fs::remove_file(&vsock_peer_path); + let _ = std::fs::remove_file(&vsock_socket_path); + + test_dir.close().unwrap(); } #[test] - #[serial] fn test_vsock_thread_backend_sibling_vms() { const CID: u64 = 3; - const VSOCK_SOCKET_PATH: &str = "test_vsock_thread_backend.vsock"; - const SIBLING_CID: u64 = 4; - const SIBLING_VHOST_SOCKET_PATH: &str = "test_vsock_thread_backend_sibling.socket"; - const SIBLING_VSOCK_SOCKET_PATH: &str = "test_vsock_thread_backend_sibling.vsock"; const SIBLING_LISTENING_PORT: u32 = 1234; + let test_dir = tempdir().expect("Could not create a temp test directory."); + + let vsock_socket_path = test_dir + .path() + .join("test_vsock_thread_backend.vsock") + .display() + .to_string(); + let sibling_vhost_socket_path = test_dir + .path() + .join("test_vsock_thread_backend_sibling.socket") + .display() + .to_string(); + let sibling_vsock_socket_path = test_dir + .path() + .join("test_vsock_thread_backend_sibling.vsock") + .display() + .to_string(); + let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); let sibling_config = VsockConfig::new( SIBLING_CID, - SIBLING_VHOST_SOCKET_PATH.to_string(), - SIBLING_VSOCK_SOCKET_PATH.to_string(), + sibling_vhost_socket_path, + sibling_vsock_socket_path, CONN_TX_BUF_SIZE, ); @@ -425,13 +442,8 @@ mod tests { .insert(SIBLING_CID, sibling_backend.clone()); let epoll_fd = epoll::create(false).unwrap(); - let mut vtp = VsockThreadBackend::new( - VSOCK_SOCKET_PATH.to_string(), - epoll_fd, - CID, - CONN_TX_BUF_SIZE, - cid_map, - ); + let mut vtp = + VsockThreadBackend::new(vsock_socket_path, epoll_fd, CID, CONN_TX_BUF_SIZE, cid_map); assert!(!vtp.pending_raw_pkts()); @@ -489,5 +501,7 @@ mod tests { assert_eq!(recvd_data_raw[1], 0xFEu8); assert_eq!(recvd_data_raw[2], 0xBAu8); assert_eq!(recvd_data_raw[3], 0xBEu8); + + test_dir.close().unwrap(); } } diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index 1dfc0aa..7e2e947 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -348,24 +348,34 @@ impl VhostUserBackend for VhostUserVsockBackend { #[cfg(test)] mod tests { use super::*; - use serial_test::serial; use std::convert::TryInto; + use tempfile::tempdir; use vhost_user_backend::VringT; use vm_memory::GuestAddress; const CONN_TX_BUF_SIZE: u32 = 64 * 1024; #[test] - #[serial] fn test_vsock_backend() { const CID: u64 = 3; - const VHOST_SOCKET_PATH: &str = "test_vsock_backend.socket"; - const VSOCK_SOCKET_PATH: &str = "test_vsock_backend.vsock"; + + let test_dir = tempdir().expect("Could not create a temp test directory."); + + let vhost_socket_path = test_dir + .path() + .join("test_vsock_backend.socket") + .display() + .to_string(); + let vsock_socket_path = test_dir + .path() + .join("test_vsock_backend.vsock") + .display() + .to_string(); let config = VsockConfig::new( CID, - VHOST_SOCKET_PATH.to_string(), - VSOCK_SOCKET_PATH.to_string(), + vhost_socket_path.to_string(), + vsock_socket_path.to_string(), CONN_TX_BUF_SIZE, ); @@ -426,16 +436,28 @@ mod tests { assert!(!ret.unwrap()); // cleanup - let _ = std::fs::remove_file(VHOST_SOCKET_PATH); - let _ = std::fs::remove_file(VSOCK_SOCKET_PATH); + let _ = std::fs::remove_file(vhost_socket_path); + let _ = std::fs::remove_file(vsock_socket_path); + + test_dir.close().unwrap(); } #[test] - #[serial] fn test_vsock_backend_failures() { const CID: u64 = 3; - const VHOST_SOCKET_PATH: &str = "test_vsock_backend_failures.socket"; - const VSOCK_SOCKET_PATH: &str = "test_vsock_backend_failures.vsock"; + + let test_dir = tempdir().expect("Could not create a temp test directory."); + + let vhost_socket_path = test_dir + .path() + .join("test_vsock_backend_failures.socket") + .display() + .to_string(); + let vsock_socket_path = test_dir + .path() + .join("test_vsock_backend_failures.vsock") + .display() + .to_string(); let config = VsockConfig::new( CID, @@ -451,8 +473,8 @@ mod tests { let config = VsockConfig::new( CID, - VHOST_SOCKET_PATH.to_string(), - VSOCK_SOCKET_PATH.to_string(), + vhost_socket_path.to_string(), + vsock_socket_path.to_string(), CONN_TX_BUF_SIZE, ); @@ -487,7 +509,9 @@ mod tests { ); // cleanup - let _ = std::fs::remove_file(VHOST_SOCKET_PATH); - let _ = std::fs::remove_file(VSOCK_SOCKET_PATH); + let _ = std::fs::remove_file(vhost_socket_path); + let _ = std::fs::remove_file(vsock_socket_path); + + test_dir.close().unwrap(); } } diff --git a/crates/vsock/src/vhu_vsock_thread.rs b/crates/vsock/src/vhu_vsock_thread.rs index a6c170d..a073abd 100644 --- a/crates/vsock/src/vhu_vsock_thread.rs +++ b/crates/vsock/src/vhu_vsock_thread.rs @@ -665,8 +665,8 @@ impl Drop for VhostUserVsockThread { #[cfg(test)] mod tests { use super::*; - use serial_test::serial; use std::collections::HashMap; + use tempfile::tempdir; use vm_memory::GuestAddress; use vmm_sys_util::eventfd::EventFd; @@ -679,12 +679,17 @@ mod tests { } #[test] - #[serial] fn test_vsock_thread() { let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); + let test_dir = tempdir().expect("Could not create a temp test directory."); + let t = VhostUserVsockThread::new( - "test_vsock_thread.vsock".to_string(), + test_dir + .path() + .join("test_vsock_thread.vsock") + .display() + .to_string(), 3, CONN_TX_BUF_SIZE, cid_map, @@ -738,13 +743,16 @@ mod tests { dummy_fd.write(1).unwrap(); t.process_backend_evt(EventSet::empty()); + + test_dir.close().unwrap(); } #[test] - #[serial] fn test_vsock_thread_failures() { let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); + let test_dir = tempdir().expect("Could not create a temp test directory."); + let t = VhostUserVsockThread::new( "/sys/not_allowed.vsock".to_string(), 3, @@ -753,13 +761,13 @@ mod tests { ); assert!(t.is_err()); - let mut t = VhostUserVsockThread::new( - "test_vsock_thread_failures.vsock".to_string(), - 3, - CONN_TX_BUF_SIZE, - cid_map, - ) - .unwrap(); + let vsock_socket_path = test_dir + .path() + .join("test_vsock_thread_failures.vsock") + .display() + .to_string(); + let mut t = + VhostUserVsockThread::new(vsock_socket_path, 3, CONN_TX_BUF_SIZE, cid_map).unwrap(); assert!(VhostUserVsockThread::epoll_register(-1, -1, epoll::Events::EPOLLIN).is_err()); assert!(VhostUserVsockThread::epoll_modify(-1, -1, epoll::Events::EPOLLIN).is_err()); assert!(VhostUserVsockThread::epoll_unregister(-1, -1).is_err()); @@ -780,5 +788,7 @@ mod tests { .push_back(ConnMapKey::new(0, 0)); assert!(t.process_rx(&vring, false).is_err()); assert!(t.process_rx(&vring, true).is_err()); + + test_dir.close().unwrap(); } } diff --git a/crates/vsock/src/vsock_conn.rs b/crates/vsock/src/vsock_conn.rs index 834ece8..058c2e1 100644 --- a/crates/vsock/src/vsock_conn.rs +++ b/crates/vsock/src/vsock_conn.rs @@ -369,7 +369,6 @@ mod tests { use super::*; use crate::vhu_vsock::{VSOCK_HOST_CID, VSOCK_OP_RW, VSOCK_TYPE_STREAM}; - use serial_test::serial; use std::io::Result as IoResult; use std::ops::Deref; use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; @@ -505,7 +504,6 @@ mod tests { } #[test] - #[serial] fn test_vsock_conn_init() { // new locally inititated connection let dummy_file = VsockDummySocket::new(); @@ -554,7 +552,6 @@ mod tests { } #[test] - #[serial] fn test_vsock_conn_credit() { // new locally inititated connection let dummy_file = VsockDummySocket::new(); @@ -585,7 +582,6 @@ mod tests { } #[test] - #[serial] fn test_vsock_conn_init_pkt() { // parameters for packet head construction let head_params = HeadParams::new(PKT_HEADER_SIZE, 10); @@ -622,7 +618,6 @@ mod tests { } #[test] - #[serial] fn test_vsock_conn_recv_pkt() { // parameters for packet head construction let head_params = HeadParams::new(PKT_HEADER_SIZE, 5); @@ -719,7 +714,6 @@ mod tests { } #[test] - #[serial] fn test_vsock_conn_send_pkt() { // parameters for packet head construction let head_params = HeadParams::new(PKT_HEADER_SIZE, 5); From 9332a933f8aef6d06f686c0d96f7bdd564520863 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Thu, 6 Jul 2023 19:09:01 +0200 Subject: [PATCH 090/189] tree-wide: actually pretty-print error messages We setup pretty-printing with all the thiserror-based Error structs, but then only bubble the error up to the main, where it just gets printed with the `Debug` trait. This "catches" the error in the main and performs pretty printing using the `Display` trait. Before: Error: FailedCreatingListener(SocketError(Os { code: 98, kind: AddrInUse, message: "Address already in use" })) After: [2023-07-06T17:20:47Z ERROR vhost_device_scsi] Failed creating listener: socket error: Address already in use (os error 98) vhost-device-vsock is a bit special since it does not let error messages bubble up to the main. It also does .unwrap() in most places, but it _does_ pretty print errors during the main request handling part. Had to slightly adjust the coverage since we have no tests for the main functions. Signed-off-by: Erik Schilling --- coverage_config_x86_64.json | 2 +- crates/gpio/src/backend.rs | 10 +++++++--- crates/gpio/src/main.rs | 2 +- crates/i2c/src/main.rs | 10 +++++++--- crates/rng/src/main.rs | 10 +++++++--- crates/scsi/src/main.rs | 14 ++++++++++++-- 6 files changed, 35 insertions(+), 13 deletions(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 9cf6dcc..d528940 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 69.6, + "coverage_score": 69.0, "exclude_path": "", "crate_features": "" } diff --git a/crates/gpio/src/backend.rs b/crates/gpio/src/backend.rs index dec98f8..aff6913 100644 --- a/crates/gpio/src/backend.rs +++ b/crates/gpio/src/backend.rs @@ -5,8 +5,9 @@ // // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause -use log::{info, warn}; +use log::{error, info, warn}; use std::num::ParseIntError; +use std::process::exit; use std::sync::{Arc, RwLock}; use std::thread::spawn; @@ -184,10 +185,13 @@ fn start_backend(args: GpioArgs) -> Resul Ok(()) } -pub(crate) fn gpio_init() -> Result<()> { +pub(crate) fn gpio_init() { env_logger::init(); - start_backend::(GpioArgs::parse()) + if let Err(e) = start_backend::(GpioArgs::parse()) { + error!("{e}"); + exit(1); + } } #[cfg(test)] diff --git a/crates/gpio/src/main.rs b/crates/gpio/src/main.rs index 075b044..9dcb24d 100644 --- a/crates/gpio/src/main.rs +++ b/crates/gpio/src/main.rs @@ -13,7 +13,7 @@ mod gpio; mod vhu_gpio; #[cfg(target_env = "gnu")] -fn main() -> backend::Result<()> { +fn main() { backend::gpio_init() } diff --git a/crates/i2c/src/main.rs b/crates/i2c/src/main.rs index 288e254..be90ecb 100644 --- a/crates/i2c/src/main.rs +++ b/crates/i2c/src/main.rs @@ -8,8 +8,9 @@ mod i2c; mod vhu_i2c; -use log::{info, warn}; +use log::{error, info, warn}; use std::num::ParseIntError; +use std::process::exit; use std::sync::{Arc, RwLock}; use std::thread::spawn; @@ -233,10 +234,13 @@ fn start_backend(args: I2cArgs) -> Result< Ok(()) } -fn main() -> Result<()> { +fn main() { env_logger::init(); - start_backend::(I2cArgs::parse()) + if let Err(e) = start_backend::(I2cArgs::parse()) { + error!("{e}"); + exit(1); + } } #[cfg(test)] diff --git a/crates/rng/src/main.rs b/crates/rng/src/main.rs index 5d550bb..30bcc96 100644 --- a/crates/rng/src/main.rs +++ b/crates/rng/src/main.rs @@ -5,8 +5,9 @@ // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause mod vhu_rng; -use log::{info, warn}; +use log::{error, info, warn}; use std::fs::File; +use std::process::exit; use std::sync::{Arc, Mutex, RwLock}; use std::thread; @@ -161,10 +162,13 @@ pub(crate) fn start_backend(config: VuRngConfig) -> Result<()> { Ok(()) } -fn main() -> Result<()> { +fn main() { env_logger::init(); - start_backend(VuRngConfig::try_from(RngArgs::parse()).unwrap()) + if let Err(e) = start_backend(VuRngConfig::try_from(RngArgs::parse()).unwrap()) { + error!("{e}"); + exit(1); + } } #[cfg(test)] diff --git a/crates/scsi/src/main.rs b/crates/scsi/src/main.rs index 9e7813f..9a4c733 100644 --- a/crates/scsi/src/main.rs +++ b/crates/scsi/src/main.rs @@ -7,6 +7,7 @@ mod virtio; use std::{ fs::File, path::PathBuf, + process::exit, sync::{Arc, RwLock}, }; @@ -126,11 +127,20 @@ fn start_backend(backend: VhostUserScsiBackend, args: ScsiArgs) -> Result<()> { Ok(()) } -fn main() -> Result<()> { +fn run() -> Result<()> { env_logger::init(); let args = ScsiArgs::parse(); let backend = create_backend(&args)?; - start_backend(backend, args) + start_backend(backend, args)?; + + Ok(()) +} + +fn main() { + if let Err(e) = run() { + error!("{e}"); + exit(1); + } } #[cfg(test)] From 2b1b3644dae22918deb92b1f8d684e1ff5be5a59 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Fri, 7 Jul 2023 09:31:01 +0200 Subject: [PATCH 091/189] tree-wide: drop the Eq trait from Error I plan to add some wrapper errors around vhost errors. These end up nesting other errors all the way to std::error::Error, which has no Eq trait. The implementations were only used for comparisions in tests. While there is a assert_matches!() in nightly [1] it seems unlikely that further testing lib additions are getting standarized soon (or ever). One could use assert!(matches!()), but that would worsen the error messages for test failures. Hence, during review [2] we agreed on introducing the assert_matches crate. It got no dependencies and allows us to keep the good error messages while not needing to depend on nightly. Signed-off-by: Erik Schilling [1] https://doc.rust-lang.org/std/assert_matches/macro.assert_matches.html [2] https://github.com/rust-vmm/vhost-device/pull/388#discussion_r1257831748 --- Cargo.lock | 10 +++++++ crates/gpio/Cargo.toml | 1 + crates/gpio/src/backend.rs | 18 +++++++------ crates/i2c/Cargo.toml | 1 + crates/i2c/src/main.rs | 26 ++++++++++--------- crates/rng/Cargo.toml | 1 + crates/rng/src/main.rs | 20 +++++++------- crates/scsi/Cargo.toml | 1 + .../scsi/src/scsi/emulation/tests/generic.rs | 3 ++- 9 files changed, 51 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 720117f..0858e53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,6 +77,12 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + [[package]] name = "async-trait" version = "0.1.71" @@ -1225,6 +1231,7 @@ dependencies = [ name = "vhost-device-gpio" version = "0.1.0" dependencies = [ + "assert_matches", "clap", "env_logger", "libc", @@ -1243,6 +1250,7 @@ dependencies = [ name = "vhost-device-i2c" version = "0.1.0" dependencies = [ + "assert_matches", "clap", "env_logger", "libc", @@ -1260,6 +1268,7 @@ dependencies = [ name = "vhost-device-rng" version = "0.1.0" dependencies = [ + "assert_matches", "clap", "env_logger", "epoll", @@ -1280,6 +1289,7 @@ dependencies = [ name = "vhost-device-scsi" version = "0.1.0" dependencies = [ + "assert_matches", "clap", "env_logger", "epoll", diff --git a/crates/gpio/Cargo.toml b/crates/gpio/Cargo.toml index 8dd607b..9570030 100644 --- a/crates/gpio/Cargo.toml +++ b/crates/gpio/Cargo.toml @@ -31,5 +31,6 @@ vmm-sys-util = "0.11" libgpiod = { version = "0.1" } [dev-dependencies] +assert_matches = "1.5" virtio-queue = { version = "0.9", features = ["test-utils"] } vm-memory = { version = "0.12", features = ["backend-mmap", "backend-atomic"] } diff --git a/crates/gpio/src/backend.rs b/crates/gpio/src/backend.rs index aff6913..0d93af8 100644 --- a/crates/gpio/src/backend.rs +++ b/crates/gpio/src/backend.rs @@ -22,7 +22,7 @@ use crate::vhu_gpio::VhostUserGpioBackend; pub(crate) type Result = std::result::Result; -#[derive(Debug, Eq, PartialEq, ThisError)] +#[derive(Debug, ThisError)] /// Errors related to low level GPIO helpers pub(crate) enum Error { #[error("Invalid socket count: {0}")] @@ -196,6 +196,8 @@ pub(crate) fn gpio_init() { #[cfg(test)] mod tests { + use assert_matches::assert_matches; + use super::*; use crate::gpio::tests::DummyDevice; @@ -220,7 +222,7 @@ mod tests { config.push(5).unwrap(); config.push(6).unwrap(); - assert_eq!(config.push(5).unwrap_err(), Error::DeviceDuplicate(5)); + assert_matches!(config.push(5).unwrap_err(), Error::DeviceDuplicate(5)); } #[test] @@ -229,28 +231,28 @@ mod tests { // Invalid device number let cmd_args = get_cmd_args(socket_name, "1:4d:5", 3); - assert_eq!( + assert_matches!( GpioConfiguration::try_from(cmd_args).unwrap_err(), - Error::ParseFailure("4d".parse::().unwrap_err()) + Error::ParseFailure(e) if e == "4d".parse::().unwrap_err() ); // Zero socket count let cmd_args = get_cmd_args(socket_name, "1:4", 0); - assert_eq!( + assert_matches!( GpioConfiguration::try_from(cmd_args).unwrap_err(), Error::SocketCountInvalid(0) ); // Duplicate client address: 4 let cmd_args = get_cmd_args(socket_name, "1:4:5:6:4", 5); - assert_eq!( + assert_matches!( GpioConfiguration::try_from(cmd_args).unwrap_err(), Error::DeviceDuplicate(4) ); // Device count mismatch let cmd_args = get_cmd_args(socket_name, "1:4:5:6", 5); - assert_eq!( + assert_matches!( GpioConfiguration::try_from(cmd_args).unwrap_err(), Error::DeviceCountMismatch(5, 4) ); @@ -280,7 +282,7 @@ mod tests { let socket_name = "~/path/not/present/gpio"; let cmd_args = get_cmd_args(socket_name, "1:4:3:5", 4); - assert_eq!( + assert_matches!( start_backend::(cmd_args).unwrap_err(), Error::FailedJoiningThreads ); diff --git a/crates/i2c/Cargo.toml b/crates/i2c/Cargo.toml index bbf6487..d44992e 100644 --- a/crates/i2c/Cargo.toml +++ b/crates/i2c/Cargo.toml @@ -28,5 +28,6 @@ vm-memory = "0.12" vmm-sys-util = "0.11" [dev-dependencies] +assert_matches = "1.5" virtio-queue = { version = "0.9", features = ["test-utils"] } vm-memory = { version = "0.12", features = ["backend-mmap", "backend-atomic"] } diff --git a/crates/i2c/src/main.rs b/crates/i2c/src/main.rs index be90ecb..0fa537c 100644 --- a/crates/i2c/src/main.rs +++ b/crates/i2c/src/main.rs @@ -25,7 +25,7 @@ use vhu_i2c::VhostUserI2cBackend; type Result = std::result::Result; -#[derive(Debug, PartialEq, ThisError)] +#[derive(Debug, ThisError)] /// Errors related to low level i2c helpers pub(crate) enum Error { #[error("Invalid socket count: {0}")] @@ -245,6 +245,8 @@ fn main() { #[cfg(test)] mod tests { + use assert_matches::assert_matches; + use super::*; use crate::i2c::tests::DummyDevice; @@ -281,12 +283,12 @@ mod tests { config.push(5).unwrap(); config.push(6).unwrap(); - assert_eq!( + assert_matches!( config.push(invalid_addr).unwrap_err(), - Error::ClientAddressInvalid(invalid_addr) + Error::ClientAddressInvalid(a) if a == invalid_addr ); - assert_eq!( + assert_matches!( config.push(5).unwrap_err(), Error::ClientAddressDuplicate(5) ); @@ -298,21 +300,21 @@ mod tests { // Invalid client address let cmd_args = I2cArgs::from_args(socket_name, "1:4d", 5); - assert_eq!( + assert_matches!( I2cConfiguration::try_from(cmd_args).unwrap_err(), - Error::ParseFailure("4d".parse::().unwrap_err()) + Error::ParseFailure(e) if e == "4d".parse::().unwrap_err() ); // Zero socket count let cmd_args = I2cArgs::from_args(socket_name, "1:4", 0); - assert_eq!( + assert_matches!( I2cConfiguration::try_from(cmd_args).unwrap_err(), Error::SocketCountInvalid(0) ); // Duplicate client address: 4 let cmd_args = I2cArgs::from_args(socket_name, "1:4,2:32:21,5:4:23", 5); - assert_eq!( + assert_matches!( I2cConfiguration::try_from(cmd_args).unwrap_err(), Error::ClientAddressDuplicate(4) ); @@ -355,7 +357,7 @@ mod tests { .push(DeviceConfig::new_with(2, vec![32, 21])) .unwrap(); - assert_eq!( + assert_matches!( config .push(DeviceConfig::new_with(5, vec![4, 23])) .unwrap_err(), @@ -372,11 +374,11 @@ mod tests { .push(DeviceConfig::new_with(5, vec![10, 23])) .unwrap(); - assert_eq!( + assert_matches!( config .push(DeviceConfig::new_with(1, vec![32, 21])) .unwrap_err(), - Error::AdapterDuplicate(1.to_string()) + Error::AdapterDuplicate(n) if n == "1" ); } @@ -386,7 +388,7 @@ mod tests { let socket_name = "~/path/not/present/i2c"; let cmd_args = I2cArgs::from_args(socket_name, "1:4,3:5", 5); - assert_eq!( + assert_matches!( start_backend::(cmd_args).unwrap_err(), Error::FailedJoiningThreads ); diff --git a/crates/rng/Cargo.toml b/crates/rng/Cargo.toml index dd71b18..e0a17e4 100644 --- a/crates/rng/Cargo.toml +++ b/crates/rng/Cargo.toml @@ -29,5 +29,6 @@ vm-memory = "0.12" vmm-sys-util = "0.11" [dev-dependencies] +assert_matches = "1.5" virtio-queue = { version = "0.9", features = ["test-utils"] } vm-memory = { version = "0.12", features = ["backend-mmap", "backend-atomic"] } diff --git a/crates/rng/src/main.rs b/crates/rng/src/main.rs index 30bcc96..f1c97fb 100644 --- a/crates/rng/src/main.rs +++ b/crates/rng/src/main.rs @@ -25,7 +25,7 @@ const VHU_RNG_MAX_PERIOD_MS: u128 = 65536; type Result = std::result::Result; -#[derive(Debug, Eq, PartialEq, ThisError)] +#[derive(Debug, ThisError)] /// Errors related to vhost-device-rng daemon. pub(crate) enum Error { #[error("RNG source file doesn't exists or can't be accessed")] @@ -173,9 +173,11 @@ fn main() { #[cfg(test)] mod tests { - use super::*; + use assert_matches::assert_matches; use tempfile::tempdir; + use super::*; + #[test] fn verify_cmd_line_arguments() { // All parameters have default values, except for the socket path. White spaces are @@ -194,22 +196,22 @@ mod tests { // All configuration elements should be what we expect them to be. Using // VuRngConfig::try_from() ensures that strings have been properly trimmed. assert_eq!( - VuRngConfig::try_from(default_args), - VuRngConfig::try_from(args.clone()) + VuRngConfig::try_from(default_args).unwrap(), + VuRngConfig::try_from(args.clone()).unwrap() ); // Setting a invalid period should trigger an InvalidPeriodInput error. let mut invalid_period_args = args.clone(); invalid_period_args.period = VHU_RNG_MAX_PERIOD_MS + 1; - assert_eq!( + assert_matches!( VuRngConfig::try_from(invalid_period_args), - Err(Error::InvalidPeriodInput(VHU_RNG_MAX_PERIOD_MS + 1)) + Err(Error::InvalidPeriodInput(p)) if p == VHU_RNG_MAX_PERIOD_MS + 1 ); // Setting the socket count to 0 should trigger an InvalidSocketCount error. let mut invalid_socket_count_args = args; invalid_socket_count_args.socket_count = 0; - assert_eq!( + assert_matches!( VuRngConfig::try_from(invalid_socket_count_args), Err(Error::InvalidSocketCount(0)) ); @@ -230,7 +232,7 @@ mod tests { }; // An invalid RNG source file should trigger an AccessRngSourceFile error. - assert_eq!( + assert_matches!( start_backend(config.clone()).unwrap_err(), Error::AccessRngSourceFile ); @@ -239,7 +241,7 @@ mod tests { // of the socket file. Since the latter is invalid the vhost_user::Listener will // throw an error, forcing the thread to exit and the call to handle.join() to fail. config.rng_source = random_path.to_str().unwrap().to_string(); - assert_eq!( + assert_matches!( start_backend(config).unwrap_err(), Error::FailedJoiningThreads ); diff --git a/crates/scsi/Cargo.toml b/crates/scsi/Cargo.toml index 472935a..2dbfca5 100644 --- a/crates/scsi/Cargo.toml +++ b/crates/scsi/Cargo.toml @@ -29,5 +29,6 @@ vm-memory = "0.12" vmm-sys-util = "0.11" [dev-dependencies] +assert_matches = "1.5" tempfile = "3.2.0" diff --git a/crates/scsi/src/scsi/emulation/tests/generic.rs b/crates/scsi/src/scsi/emulation/tests/generic.rs index 19f4bd4..0ae1728 100644 --- a/crates/scsi/src/scsi/emulation/tests/generic.rs +++ b/crates/scsi/src/scsi/emulation/tests/generic.rs @@ -2,6 +2,7 @@ //! Tests for stuff shared between commands. +use assert_matches::assert_matches; use std::io::ErrorKind; use super::{do_command_fail, test_image}; @@ -103,5 +104,5 @@ fn test_short_cdb() { }, ); - assert!(matches!(res.unwrap_err(), CmdError::CdbTooShort)); + assert_matches!(res.unwrap_err(), CmdError::CdbTooShort); } From a9de87a1ced5049b2bcea448e2e444e94fa47136 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Fri, 7 Jul 2023 08:44:55 +0200 Subject: [PATCH 092/189] tree-wide: bubble up errors from daemon threads There was a mix of just unwrapping (panicking) and catching and logging errors. The unwrapping is not allowing for particulary pretty error handling, so let's bubble the errors up by not crashing the thread, but by just returning a Result<()> than is received when joining the threads. Not all .unwrap() uses were translated since a followup PR (#389) will rework that code anyway (and get rid of the .unwrap() in the process). Signed-off-by: Erik Schilling --- coverage_config_x86_64.json | 2 +- crates/gpio/src/backend.rs | 25 ++++++++++++++++++------- crates/i2c/src/main.rs | 14 +++++++++----- crates/rng/src/main.rs | 15 ++++++++++----- crates/vsock/src/main.rs | 35 +++++++++++++++++++++++++++-------- 5 files changed, 65 insertions(+), 26 deletions(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index d528940..1773554 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 69.0, + "coverage_score": 68.2, "exclude_path": "", "crate_features": "" } diff --git a/crates/gpio/src/backend.rs b/crates/gpio/src/backend.rs index 0d93af8..28408eb 100644 --- a/crates/gpio/src/backend.rs +++ b/crates/gpio/src/backend.rs @@ -9,7 +9,7 @@ use log::{error, info, warn}; use std::num::ParseIntError; use std::process::exit; use std::sync::{Arc, RwLock}; -use std::thread::spawn; +use std::thread::{spawn, JoinHandle}; use clap::Parser; use thiserror::Error as ThisError; @@ -35,6 +35,14 @@ pub(crate) enum Error { ParseFailure(ParseIntError), #[error("Failed to join threads")] FailedJoiningThreads, + #[error("Could not open gpio device: {0}")] + CouldNotOpenDevice(crate::gpio::Error), + #[error("Could not create gpio controller: {0}")] + CouldNotCreateGpioController(crate::gpio::Error), + #[error("Could not create gpio backend: {0}")] + CouldNotCreateBackend(crate::vhu_gpio::Error), + #[error("Could not create daemon: {0}")] + CouldNotCreateDaemon(vhost_user_backend::Error), } #[derive(Parser, Debug)] @@ -134,7 +142,7 @@ fn start_backend(args: GpioArgs) -> Resul let socket = config.socket_path.to_owned() + &i.to_string(); let device_num = config.devices.inner[i]; - let handle = spawn(move || loop { + let handle: JoinHandle> = spawn(move || loop { // A separate thread is spawned for each socket and can connect to a separate guest. // These are run in an infinite loop to not require the daemon to be restarted once a // guest exits. @@ -143,9 +151,12 @@ fn start_backend(args: GpioArgs) -> Resul // threads, and so the code uses unwrap() instead. The panic on a thread won't cause // trouble to other threads/guests or the main() function and should be safe for the // daemon. - let device = D::open(device_num).unwrap(); - let controller = GpioController::::new(device).unwrap(); - let backend = Arc::new(RwLock::new(VhostUserGpioBackend::new(controller).unwrap())); + let device = D::open(device_num).map_err(Error::CouldNotOpenDevice)?; + let controller = + GpioController::::new(device).map_err(Error::CouldNotCreateGpioController)?; + let backend = Arc::new(RwLock::new( + VhostUserGpioBackend::new(controller).map_err(Error::CouldNotCreateBackend)?, + )); let listener = Listener::new(socket.clone(), true).unwrap(); let mut daemon = VhostUserDaemon::new( @@ -153,7 +164,7 @@ fn start_backend(args: GpioArgs) -> Resul backend.clone(), GuestMemoryAtomic::new(GuestMemoryMmap::new()), ) - .unwrap(); + .map_err(Error::CouldNotCreateDaemon)?; daemon.start(listener).unwrap(); @@ -179,7 +190,7 @@ fn start_backend(args: GpioArgs) -> Resul } for handle in handles { - handle.join().map_err(|_| Error::FailedJoiningThreads)?; + handle.join().map_err(|_| Error::FailedJoiningThreads)??; } Ok(()) diff --git a/crates/i2c/src/main.rs b/crates/i2c/src/main.rs index 0fa537c..4698063 100644 --- a/crates/i2c/src/main.rs +++ b/crates/i2c/src/main.rs @@ -12,7 +12,7 @@ use log::{error, info, warn}; use std::num::ParseIntError; use std::process::exit; use std::sync::{Arc, RwLock}; -use std::thread::spawn; +use std::thread::{spawn, JoinHandle}; use clap::Parser; use thiserror::Error as ThisError; @@ -42,6 +42,10 @@ pub(crate) enum Error { ParseFailure(ParseIntError), #[error("Failed to join threads")] FailedJoiningThreads, + #[error("Could not create backend: {0}")] + CouldNotCreateBackend(vhu_i2c::Error), + #[error("Could not create daemon: {0}")] + CouldNotCreateDaemon(vhost_user_backend::Error), } #[derive(Parser, Debug)] @@ -183,7 +187,7 @@ fn start_backend(args: I2cArgs) -> Result< let socket = config.socket_path.to_owned() + &i.to_string(); let i2c_map = i2c_map.clone(); - let handle = spawn(move || loop { + let handle: JoinHandle> = spawn(move || loop { // A separate thread is spawned for each socket and can connect to a separate guest. // These are run in an infinite loop to not require the daemon to be restarted once a // guest exits. @@ -193,7 +197,7 @@ fn start_backend(args: I2cArgs) -> Result< // trouble to other threads/guests or the main() function and should be safe for the // daemon. let backend = Arc::new(RwLock::new( - VhostUserI2cBackend::new(i2c_map.clone()).unwrap(), + VhostUserI2cBackend::new(i2c_map.clone()).map_err(Error::CouldNotCreateBackend)?, )); let listener = Listener::new(socket.clone(), true).unwrap(); @@ -202,7 +206,7 @@ fn start_backend(args: I2cArgs) -> Result< backend.clone(), GuestMemoryAtomic::new(GuestMemoryMmap::new()), ) - .unwrap(); + .map_err(Error::CouldNotCreateDaemon)?; daemon.start(listener).unwrap(); @@ -228,7 +232,7 @@ fn start_backend(args: I2cArgs) -> Result< } for handle in handles { - handle.join().map_err(|_| Error::FailedJoiningThreads)?; + handle.join().map_err(|_| Error::FailedJoiningThreads)??; } Ok(()) diff --git a/crates/rng/src/main.rs b/crates/rng/src/main.rs index f1c97fb..dc9d666 100644 --- a/crates/rng/src/main.rs +++ b/crates/rng/src/main.rs @@ -9,7 +9,7 @@ use log::{error, info, warn}; use std::fs::File; use std::process::exit; use std::sync::{Arc, Mutex, RwLock}; -use std::thread; +use std::thread::{self, JoinHandle}; use clap::Parser; use thiserror::Error as ThisError; @@ -36,6 +36,10 @@ pub(crate) enum Error { InvalidSocketCount(u32), #[error("Threads can't be joined")] FailedJoiningThreads, + #[error("Could not create backend: {0}")] + CouldNotCreateBackend(std::io::Error), + #[error("Could not create daemon: {0}")] + CouldNotCreateDaemon(vhost_user_backend::Error), } #[derive(Clone, Parser, Debug, PartialEq)] @@ -110,13 +114,14 @@ pub(crate) fn start_backend(config: VuRngConfig) -> Result<()> { let max_bytes = config.max_bytes; let random = Arc::clone(&random_file); - let handle = thread::spawn(move || loop { + let handle: JoinHandle> = thread::spawn(move || loop { // If creating the VuRngBackend isn't successull there isn't much else to do than // killing the thread, which .unwrap() does. When that happens an error code is // generated and displayed by the runtime mechanic. Killing a thread doesn't affect // the other threads spun-off by the daemon. let vu_rng_backend = Arc::new(RwLock::new( - VuRngBackend::new(random.clone(), period_ms, max_bytes).unwrap(), + VuRngBackend::new(random.clone(), period_ms, max_bytes) + .map_err(Error::CouldNotCreateBackend)?, )); let mut daemon = VhostUserDaemon::new( @@ -124,7 +129,7 @@ pub(crate) fn start_backend(config: VuRngConfig) -> Result<()> { Arc::clone(&vu_rng_backend), GuestMemoryAtomic::new(GuestMemoryMmap::new()), ) - .unwrap(); + .map_err(Error::CouldNotCreateDaemon)?; let listener = Listener::new(socket.clone(), true).unwrap(); daemon.start(listener).unwrap(); @@ -156,7 +161,7 @@ pub(crate) fn start_backend(config: VuRngConfig) -> Result<()> { } for handle in handles { - handle.join().map_err(|_| Error::FailedJoiningThreads)?; + handle.join().map_err(|_| Error::FailedJoiningThreads)??; } Ok(()) diff --git a/crates/vsock/src/main.rs b/crates/vsock/src/main.rs index 98d8751..c1156b6 100644 --- a/crates/vsock/src/main.rs +++ b/crates/vsock/src/main.rs @@ -11,13 +11,14 @@ mod vsock_conn; use std::{ collections::HashMap, convert::TryFrom, + process::exit, sync::{Arc, RwLock}, thread, }; use crate::vhu_vsock::{CidMap, VhostUserVsockBackend, VsockConfig}; use clap::{Args, Parser}; -use log::{info, warn}; +use log::{error, info, warn}; use serde::Deserialize; use thiserror::Error as ThisError; use vhost::{vhost_user, vhost_user::Listener}; @@ -47,6 +48,14 @@ enum VmArgsParseError { RequiredKeyNotFound(String), } +#[derive(Debug, ThisError)] +enum BackendError { + #[error("Could not create backend: {0}")] + CouldNotCreateBackend(vhu_vsock::Error), + #[error("Could not create daemon: {0}")] + CouldNotCreateDaemon(vhost_user_backend::Error), +} + #[derive(Args, Clone, Debug, Deserialize)] struct VsockParam { /// Context identifier of the guest which uniquely identifies the device for its lifetime. @@ -177,10 +186,15 @@ impl TryFrom for Vec { /// This is the public API through which an external program starts the /// vhost-user-vsock backend server. -pub(crate) fn start_backend_server(config: VsockConfig, cid_map: Arc>) { +pub(crate) fn start_backend_server( + config: VsockConfig, + cid_map: Arc>, +) -> Result<(), BackendError> { loop { - let backend = - Arc::new(VhostUserVsockBackend::new(config.clone(), cid_map.clone()).unwrap()); + let backend = Arc::new( + VhostUserVsockBackend::new(config.clone(), cid_map.clone()) + .map_err(BackendError::CouldNotCreateBackend)?, + ); cid_map .write() .unwrap() @@ -193,7 +207,7 @@ pub(crate) fn start_backend_server(config: VsockConfig, cid_map: Arc Result<(), BackendError> { let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); let mut handles = Vec::new(); @@ -239,8 +253,10 @@ pub(crate) fn start_backend_servers(configs: &[VsockConfig]) { } for handle in handles { - handle.join().unwrap(); + handle.join().unwrap()?; } + + Ok(()) } fn main() { @@ -254,7 +270,10 @@ fn main() { } }; - start_backend_servers(&configs); + if let Err(e) = start_backend_servers(&configs) { + error!("{e}"); + exit(1); + } } #[cfg(test)] From 3cab62ea02fd554981bd555455c1cedf409743b2 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Mon, 10 Jul 2023 08:17:02 +0200 Subject: [PATCH 093/189] rng: also pretty print configuration errors If the parsed commandline options cannot be converted into a `VuRngConfig` we would only get a .unwrap() error message. Chain this before the check of the daemon launch. Suggested-by: Manos Pitsidianakis Signed-off-by: Erik Schilling --- crates/rng/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rng/src/main.rs b/crates/rng/src/main.rs index dc9d666..a1767a7 100644 --- a/crates/rng/src/main.rs +++ b/crates/rng/src/main.rs @@ -170,7 +170,7 @@ pub(crate) fn start_backend(config: VuRngConfig) -> Result<()> { fn main() { env_logger::init(); - if let Err(e) = start_backend(VuRngConfig::try_from(RngArgs::parse()).unwrap()) { + if let Err(e) = VuRngConfig::try_from(RngArgs::parse()).and_then(start_backend) { error!("{e}"); exit(1); } From 1b9dca824aa10268cbf63c507a1a63d39fb395ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 04:11:46 +0000 Subject: [PATCH 094/189] build(deps): bump ryu from 1.0.14 to 1.0.15 Bumps [ryu](https://github.com/dtolnay/ryu) from 1.0.14 to 1.0.15. - [Release notes](https://github.com/dtolnay/ryu/releases) - [Commits](https://github.com/dtolnay/ryu/compare/1.0.14...1.0.15) --- updated-dependencies: - dependency-name: ryu dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0858e53..5af6e3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -963,9 +963,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "serde" From 1ff8de46bf25560ccab146014affe8a4fece75c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jul 2023 04:42:56 +0000 Subject: [PATCH 095/189] build(deps): bump rust-vmm-ci from `9dfe5b2` to `7c1057e` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `9dfe5b2` to `7c1057e`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/9dfe5b267c4009150f0cdf49597b683f15848d1a...7c1057e9bcba7ed5090fd62b9fef0570fadb06e6) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 9dfe5b2..7c1057e 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 9dfe5b267c4009150f0cdf49597b683f15848d1a +Subproject commit 7c1057e9bcba7ed5090fd62b9fef0570fadb06e6 From 5c2c04ce4a105646cda8ca1191efeff0c25b4449 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Jul 2023 13:32:53 +0000 Subject: [PATCH 096/189] build(deps): bump is-terminal from 0.4.8 to 0.4.9 Bumps [is-terminal](https://github.com/sunfishcode/is-terminal) from 0.4.8 to 0.4.9. - [Commits](https://github.com/sunfishcode/is-terminal/compare/v0.4.8...v0.4.9) --- updated-dependencies: - dependency-name: is-terminal dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5af6e3b..551b5c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -561,9 +561,9 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix 0.38.3", From 6f81802c22193bb396509e3e16c8e15f2b1215e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Jul 2023 13:37:57 +0000 Subject: [PATCH 097/189] build(deps): bump unicode-ident from 1.0.10 to 1.0.11 Bumps [unicode-ident](https://github.com/dtolnay/unicode-ident) from 1.0.10 to 1.0.11. - [Release notes](https://github.com/dtolnay/unicode-ident/releases) - [Commits](https://github.com/dtolnay/unicode-ident/compare/1.0.10...1.0.11) --- updated-dependencies: - dependency-name: unicode-ident dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 551b5c0..efd8954 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1181,9 +1181,9 @@ checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicode-ident" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-segmentation" From bf752054784e123dfdb4cba9a948bb8fb66865e9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 13 Jul 2023 11:43:10 +0530 Subject: [PATCH 098/189] crates: Don't warn for vhost_user::Error::Disconnected The vhost_user::Error::Disconnected error code is returned by the daemon if the VM is shutting down. Don't Warn the user in this case but just point out that VM may be shutting down. Signed-off-by: Viresh Kumar --- crates/gpio/src/backend.rs | 2 +- crates/i2c/src/main.rs | 2 +- crates/rng/src/main.rs | 2 +- crates/scsi/src/main.rs | 4 +++- crates/vsock/src/main.rs | 4 +++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/gpio/src/backend.rs b/crates/gpio/src/backend.rs index 28408eb..872073b 100644 --- a/crates/gpio/src/backend.rs +++ b/crates/gpio/src/backend.rs @@ -173,7 +173,7 @@ fn start_backend(args: GpioArgs) -> Resul info!("Stopping cleanly."); } Err(vhost_user_backend::Error::HandleRequest( - vhost_user::Error::PartialMessage, + vhost_user::Error::PartialMessage | vhost_user::Error::Disconnected, )) => { info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug."); } diff --git a/crates/i2c/src/main.rs b/crates/i2c/src/main.rs index 4698063..457feba 100644 --- a/crates/i2c/src/main.rs +++ b/crates/i2c/src/main.rs @@ -215,7 +215,7 @@ fn start_backend(args: I2cArgs) -> Result< info!("Stopping cleanly."); } Err(vhost_user_backend::Error::HandleRequest( - vhost_user::Error::PartialMessage, + vhost_user::Error::PartialMessage | vhost_user::Error::Disconnected, )) => { info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug."); } diff --git a/crates/rng/src/main.rs b/crates/rng/src/main.rs index a1767a7..eac7ba8 100644 --- a/crates/rng/src/main.rs +++ b/crates/rng/src/main.rs @@ -139,7 +139,7 @@ pub(crate) fn start_backend(config: VuRngConfig) -> Result<()> { info!("Stopping cleanly."); } Err(vhost_user_backend::Error::HandleRequest( - vhost_user::Error::PartialMessage, + vhost_user::Error::PartialMessage | vhost_user::Error::Disconnected, )) => { info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug."); } diff --git a/crates/scsi/src/main.rs b/crates/scsi/src/main.rs index 9a4c733..8ca2922 100644 --- a/crates/scsi/src/main.rs +++ b/crates/scsi/src/main.rs @@ -108,7 +108,9 @@ fn start_backend(backend: VhostUserScsiBackend, args: ScsiArgs) -> Result<()> { Ok(()) => { info!("Stopping cleanly."); } - Err(vhost_user_backend::Error::HandleRequest(vhost_user::Error::PartialMessage)) => { + Err(vhost_user_backend::Error::HandleRequest( + vhost_user::Error::PartialMessage | vhost_user::Error::Disconnected, + )) => { info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug."); } Err(e) => { diff --git a/crates/vsock/src/main.rs b/crates/vsock/src/main.rs index c1156b6..4ff0f93 100644 --- a/crates/vsock/src/main.rs +++ b/crates/vsock/src/main.rs @@ -224,7 +224,9 @@ pub(crate) fn start_backend_server( Ok(()) => { info!("Stopping cleanly"); } - Err(vhost_user_backend::Error::HandleRequest(vhost_user::Error::PartialMessage)) => { + Err(vhost_user_backend::Error::HandleRequest( + vhost_user::Error::PartialMessage | vhost_user::Error::Disconnected, + )) => { info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug."); } Err(e) => { From e55b520cb2ac5f6d713b721078ce4882074fba9a Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Mon, 3 Jul 2023 15:50:29 +0530 Subject: [PATCH 099/189] vsock: Resolve deadlock in sibling VM communication The deadlock occurs when two sibling VMs simultaneously try to send each other packets. The `VhostUserVsockThread`s corresponding to both the VMs hold their own locks while executing `thread_backend.send_pkt` and then try to lock each other to access their counterpart's `raw_pkts_queue`. This ultimately results in a deadlock. Resolved by separating the mutex over `raw_pkts_queue` from the mutex over `VhostUserVsockThread`. Signed-off-by: Priyansh Rathi --- crates/vsock/src/main.rs | 8 +------ crates/vsock/src/thread_backend.rs | 27 ++++++++++++------------ crates/vsock/src/vhu_vsock.rs | 3 ++- crates/vsock/src/vhu_vsock_thread.rs | 31 +++++++++++++++++++++------- 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/crates/vsock/src/main.rs b/crates/vsock/src/main.rs index 4ff0f93..79f11ba 100644 --- a/crates/vsock/src/main.rs +++ b/crates/vsock/src/main.rs @@ -195,10 +195,6 @@ pub(crate) fn start_backend_server( VhostUserVsockBackend::new(config.clone(), cid_map.clone()) .map_err(BackendError::CouldNotCreateBackend)?, ); - cid_map - .write() - .unwrap() - .insert(config.get_guest_cid(), backend.clone()); let listener = Listener::new(config.get_socket_path(), true).unwrap(); @@ -236,7 +232,6 @@ pub(crate) fn start_backend_server( // No matter the result, we need to shut down the worker thread. backend.exit_event.write(1).unwrap(); - cid_map.write().unwrap().remove(&config.get_guest_cid()); } } @@ -455,8 +450,7 @@ mod tests { let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); - let backend = Arc::new(VhostUserVsockBackend::new(config, cid_map.clone()).unwrap()); - cid_map.write().unwrap().insert(CID, backend.clone()); + let backend = Arc::new(VhostUserVsockBackend::new(config, cid_map).unwrap()); let daemon = VhostUserDaemon::new( String::from("vhost-user-vsock"), diff --git a/crates/vsock/src/thread_backend.rs b/crates/vsock/src/thread_backend.rs index 5029465..5677ec5 100644 --- a/crates/vsock/src/thread_backend.rs +++ b/crates/vsock/src/thread_backend.rs @@ -23,6 +23,8 @@ use crate::{ vsock_conn::*, }; +pub(crate) type RawPktsQ = VecDeque; + pub(crate) struct RawVsockPacket { pub header: [u8; PKT_HEADER_SIZE], pub data: Vec, @@ -65,9 +67,9 @@ pub(crate) struct VsockThreadBackend { pub local_port_set: HashSet, tx_buffer_size: u32, /// Maps the guest CID to the corresponding backend. Used for sibling VM communication. - cid_map: Arc>, + pub cid_map: Arc>, /// Queue of raw vsock packets recieved from sibling VMs to be sent to the guest. - raw_pkts_queue: VecDeque, + pub raw_pkts_queue: Arc>, } impl VsockThreadBackend { @@ -92,7 +94,7 @@ impl VsockThreadBackend { local_port_set: HashSet::new(), tx_buffer_size, cid_map, - raw_pkts_queue: VecDeque::new(), + raw_pkts_queue: Arc::new(RwLock::new(VecDeque::new())), } } @@ -103,7 +105,7 @@ impl VsockThreadBackend { /// Checks if there are pending raw vsock packets to be sent to the guest. pub fn pending_raw_pkts(&self) -> bool { - !self.raw_pkts_queue.is_empty() + !self.raw_pkts_queue.read().unwrap().is_empty() } /// Deliver a vsock packet to the guest vsock driver. @@ -178,14 +180,13 @@ impl VsockThreadBackend { if dst_cid != VSOCK_HOST_CID { let cid_map = self.cid_map.read().unwrap(); if cid_map.contains_key(&dst_cid) { - let sibling_backend = cid_map.get(&dst_cid).unwrap(); - let mut sibling_backend_thread = sibling_backend.threads[0].lock().unwrap(); + let (sibling_raw_pkts_queue, sibling_event_fd) = cid_map.get(&dst_cid).unwrap(); - sibling_backend_thread - .thread_backend - .raw_pkts_queue + sibling_raw_pkts_queue + .write() + .unwrap() .push_back(RawVsockPacket::from_vsock_packet(pkt)?); - let _ = sibling_backend_thread.sibling_event_fd.write(1); + let _ = sibling_event_fd.write(1); } else { warn!("vsock: dropping packet for unknown cid: {:?}", dst_cid); } @@ -254,6 +255,8 @@ impl VsockThreadBackend { pub fn recv_raw_pkt(&mut self, pkt: &mut VsockPacket) -> Result<()> { let raw_vsock_pkt = self .raw_pkts_queue + .write() + .unwrap() .pop_front() .ok_or(Error::EmptyRawPktsQueue)?; @@ -436,10 +439,6 @@ mod tests { let sibling_backend = Arc::new(VhostUserVsockBackend::new(sibling_config, cid_map.clone()).unwrap()); - cid_map - .write() - .unwrap() - .insert(SIBLING_CID, sibling_backend.clone()); let epoll_fd = epoll::create(false).unwrap(); let mut vtp = diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index 7e2e947..27108ff 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -21,9 +21,10 @@ use vmm_sys_util::{ eventfd::{EventFd, EFD_NONBLOCK}, }; +use crate::thread_backend::RawPktsQ; use crate::vhu_vsock_thread::*; -pub(crate) type CidMap = HashMap>; +pub(crate) type CidMap = HashMap>, EventFd)>; const NUM_QUEUES: usize = 2; const QUEUE_SIZE: usize = 256; diff --git a/crates/vsock/src/vhu_vsock_thread.rs b/crates/vsock/src/vhu_vsock_thread.rs index a073abd..ed62a4a 100644 --- a/crates/vsock/src/vhu_vsock_thread.rs +++ b/crates/vsock/src/vhu_vsock_thread.rs @@ -92,21 +92,31 @@ impl VhostUserVsockThread { let sibling_event_fd = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?; + let thread_backend = VsockThreadBackend::new( + uds_path.clone(), + epoll_fd, + guest_cid, + tx_buffer_size, + cid_map.clone(), + ); + + cid_map.write().unwrap().insert( + guest_cid, + ( + thread_backend.raw_pkts_queue.clone(), + sibling_event_fd.try_clone().unwrap(), + ), + ); + let thread = VhostUserVsockThread { mem: None, event_idx: false, host_sock: host_sock.as_raw_fd(), - host_sock_path: uds_path.clone(), + host_sock_path: uds_path, host_listener: host_sock, vring_worker: None, epoll_file, - thread_backend: VsockThreadBackend::new( - uds_path, - epoll_fd, - guest_cid, - tx_buffer_size, - cid_map, - ), + thread_backend, guest_cid, pool: ThreadPoolBuilder::new() .pool_size(1) @@ -660,6 +670,11 @@ impl VhostUserVsockThread { impl Drop for VhostUserVsockThread { fn drop(&mut self) { let _ = std::fs::remove_file(&self.host_sock_path); + self.thread_backend + .cid_map + .write() + .unwrap() + .remove(&self.guest_cid); } } #[cfg(test)] From 6feb938c506e1be48b074ec844e557c353804bb9 Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Wed, 5 Jul 2023 23:13:25 +0530 Subject: [PATCH 100/189] vsock: Try processing raw packets on other events too Currently, the `raw_pkts_queue` is processed only when a `SIBLING_VM_EVENT` is received. But it may happen that the `raw_pkts_queue` could not be processed completely due to insufficient space in the RX virtqueue at that time. So, try to process raw packets on other events too similar to what happens in the RX of standard packets. Signed-off-by: Priyansh Rathi --- crates/vsock/src/vhu_vsock.rs | 2 +- crates/vsock/src/vhu_vsock_thread.rs | 70 +++++++++++++++++++--------- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index 27108ff..bb59e05 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -317,7 +317,7 @@ impl VhostUserBackend for VhostUserVsockBackend { } } - if device_event != EVT_QUEUE_EVENT && thread.thread_backend.pending_rx() { + if device_event != EVT_QUEUE_EVENT { thread.process_rx(vring_rx, evt_idx)?; } diff --git a/crates/vsock/src/vhu_vsock_thread.rs b/crates/vsock/src/vhu_vsock_thread.rs index ed62a4a..726e2b6 100644 --- a/crates/vsock/src/vhu_vsock_thread.rs +++ b/crates/vsock/src/vhu_vsock_thread.rs @@ -68,6 +68,9 @@ pub(crate) struct VhostUserVsockThread { /// EventFd to notify this thread for custom events. Currently used to notify /// this thread to process raw vsock packets sent from a sibling VM. pub sibling_event_fd: EventFd, + /// Keeps track of which RX queue was processed first in the last iteration. + /// Used to alternate between the RX queues to prevent the starvation of one by the other. + last_processed: RxQueueType, } impl VhostUserVsockThread { @@ -125,6 +128,7 @@ impl VhostUserVsockThread { local_port: Wrapping(0), tx_buffer_size, sibling_event_fd, + last_processed: RxQueueType::Standard, }; VhostUserVsockThread::epoll_register(epoll_fd, host_raw_fd, epoll::Events::EPOLLIN)?; @@ -527,7 +531,7 @@ impl VhostUserVsockThread { } /// Wrapper to process rx queue based on whether event idx is enabled or not. - pub fn process_rx(&mut self, vring: &VringRwLock, event_idx: bool) -> Result { + fn process_unix_sockets(&mut self, vring: &VringRwLock, event_idx: bool) -> Result { if event_idx { // To properly handle EVENT_IDX we need to keep calling // process_rx_queue until it stops finding new requests @@ -550,6 +554,50 @@ impl VhostUserVsockThread { Ok(false) } + /// Wrapper to process raw vsock packets queue based on whether event idx is enabled or not. + pub fn process_raw_pkts(&mut self, vring: &VringRwLock, event_idx: bool) -> Result { + if event_idx { + loop { + if !self.thread_backend.pending_raw_pkts() { + break; + } + vring.disable_notification().unwrap(); + + self.process_rx_queue(vring, RxQueueType::RawPkts)?; + if !vring.enable_notification().unwrap() { + break; + } + } + } else { + self.process_rx_queue(vring, RxQueueType::RawPkts)?; + } + Ok(false) + } + + pub fn process_rx(&mut self, vring: &VringRwLock, event_idx: bool) -> Result { + match self.last_processed { + RxQueueType::Standard => { + if self.thread_backend.pending_raw_pkts() { + self.process_raw_pkts(vring, event_idx)?; + self.last_processed = RxQueueType::RawPkts; + } + if self.thread_backend.pending_rx() { + self.process_unix_sockets(vring, event_idx)?; + } + } + RxQueueType::RawPkts => { + if self.thread_backend.pending_rx() { + self.process_unix_sockets(vring, event_idx)?; + self.last_processed = RxQueueType::Standard; + } + if self.thread_backend.pending_raw_pkts() { + self.process_raw_pkts(vring, event_idx)?; + } + } + } + Ok(false) + } + /// Process tx queue and send requests to the backend for processing. fn process_tx_queue(&mut self, vring: &VringRwLock) -> Result { let mut used_any = false; @@ -645,26 +693,6 @@ impl VhostUserVsockThread { } Ok(false) } - - /// Wrapper to process raw vsock packets queue based on whether event idx is enabled or not. - pub fn process_raw_pkts(&mut self, vring: &VringRwLock, event_idx: bool) -> Result { - if event_idx { - loop { - if !self.thread_backend.pending_raw_pkts() { - break; - } - vring.disable_notification().unwrap(); - - self.process_rx_queue(vring, RxQueueType::RawPkts)?; - if !vring.enable_notification().unwrap() { - break; - } - } - } else { - self.process_rx_queue(vring, RxQueueType::RawPkts)?; - } - Ok(false) - } } impl Drop for VhostUserVsockThread { From a4857f122d3e38befb48034b8aeae5093d7976e4 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Wed, 19 Jul 2023 10:53:32 +0200 Subject: [PATCH 101/189] README: add reference to the scsi device When we merged the SCSI device, we forgot to put it in the workspace README.md and put the link to the device README.md. Signed-off-by: Stefano Garzarella --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9c6e42f..372f225 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Here is the list of device backends that we support: - [GPIO](https://github.com/rust-vmm/vhost-device/blob/main/crates/gpio/README.md) - [I2C](https://github.com/rust-vmm/vhost-device/blob/main/crates/i2c/README.md) - [RNG](https://github.com/rust-vmm/vhost-device/blob/main/crates/rng/README.md) +- [SCSI](https://github.com/rust-vmm/vhost-device/blob/main/crates/scsi/README.md) - [VSOCK](https://github.com/rust-vmm/vhost-device/blob/main/crates/vsock/README.md) ## Testing and Code Coverage From 7f1994a40ee36ff119d0cbe2c0277d09dbb74602 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 04:42:34 +0000 Subject: [PATCH 102/189] build(deps): bump regex-syntax from 0.7.3 to 0.7.4 Bumps [regex-syntax](https://github.com/rust-lang/regex) from 0.7.3 to 0.7.4. - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/regex-syntax-0.7.3...regex-syntax-0.7.4) --- updated-dependencies: - dependency-name: regex-syntax dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index efd8954..af5f55a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -903,9 +903,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "ron" From 74f8152846137b33ed503988d1de183acb8a37eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 04:42:26 +0000 Subject: [PATCH 103/189] build(deps): bump clap from 4.3.11 to 4.3.19 Bumps [clap](https://github.com/clap-rs/clap) from 4.3.11 to 4.3.19. - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/v4.3.11...v4.3.19) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af5f55a..dc899d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -189,9 +189,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.11" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" dependencies = [ "clap_builder", "clap_derive", @@ -200,9 +200,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.11" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" dependencies = [ "anstream", "anstyle", @@ -212,9 +212,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck 0.4.1", "proc-macro2", From 9f5a3a3147c2dfead72c14cb2d48a106e811d316 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 04:42:18 +0000 Subject: [PATCH 104/189] build(deps): bump pest from 2.7.0 to 2.7.1 Bumps [pest](https://github.com/pest-parser/pest) from 2.7.0 to 2.7.1. - [Release notes](https://github.com/pest-parser/pest/releases) - [Commits](https://github.com/pest-parser/pest/compare/v2.7.0...v2.7.1) --- updated-dependencies: - dependency-name: pest dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc899d4..c29dd8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -745,9 +745,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pest" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9" +checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5" dependencies = [ "thiserror", "ucd-trie", From 66e1173d6dba78d190f28ce3f2366bbe3585bc02 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Fri, 21 Jul 2023 09:56:25 +0200 Subject: [PATCH 105/189] rng: fix clippy warning `timer` variable does not need to be mutable as clippy reported: warning: variable does not need to be mutable --> crates/rng/src/vhu_rng.rs:127:17 | 127 | let mut timer = &mut self.timer; Signed-off-by: Stefano Garzarella --- crates/rng/src/vhu_rng.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rng/src/vhu_rng.rs b/crates/rng/src/vhu_rng.rs index d51755e..66ba24e 100644 --- a/crates/rng/src/vhu_rng.rs +++ b/crates/rng/src/vhu_rng.rs @@ -124,7 +124,7 @@ impl VuRngBackend { let descriptor = descriptors[0]; let mut to_read = descriptor.len() as usize; - let mut timer = &mut self.timer; + let timer = &mut self.timer; if !descriptor.is_write_only() { return Err(VuRngError::UnexpectedReadDescriptor); From 540cc8758cb5d8478298109e84eb715ba986226a Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Fri, 21 Jul 2023 10:05:37 +0200 Subject: [PATCH 106/189] vsock: add myself as an author Harsha did most of the work, but I helped arrange the final pieces and maintain it. Signed-off-by: Stefano Garzarella --- crates/vsock/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index 55e4da7..4582093 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "vhost-user-vsock" version = "0.1.0" -authors = ["Harshavardhan Unnibhavi "] +authors = ["Harshavardhan Unnibhavi ", "Stefano Garzarella "] description = "A virtio-vsock device using the vhost-user protocol." repository = "https://github.com/rust-vmm/vhost-device" readme = "README.md" From df9094d94dad92c0ef552f87b667477ebf23e438 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Fri, 21 Jul 2023 10:08:17 +0200 Subject: [PATCH 107/189] vsock: rename the package to vhost-device-vsock All other devices follow the "vhost-device-*" pattern, while for vsock we used "vhost-user-vsock". Let's rename this as well to be consistent. Signed-off-by: Stefano Garzarella --- Cargo.lock | 32 ++++++++++++++++---------------- crates/vsock/Cargo.toml | 2 +- crates/vsock/README.md | 20 ++++++++++---------- crates/vsock/src/main.rs | 6 +++--- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c29dd8e..3309fc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1306,22 +1306,7 @@ dependencies = [ ] [[package]] -name = "vhost-user-backend" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ea9d5e8ec847cde4df1c04e586698a479706fd6beca37323f9d425b24b4c2f" -dependencies = [ - "libc", - "log", - "vhost", - "virtio-bindings", - "virtio-queue", - "vm-memory", - "vmm-sys-util", -] - -[[package]] -name = "vhost-user-vsock" +name = "vhost-device-vsock" version = "0.1.0" dependencies = [ "byteorder", @@ -1344,6 +1329,21 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "vhost-user-backend" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ea9d5e8ec847cde4df1c04e586698a479706fd6beca37323f9d425b24b4c2f" +dependencies = [ + "libc", + "log", + "vhost", + "virtio-bindings", + "virtio-queue", + "vm-memory", + "vmm-sys-util", +] + [[package]] name = "virtio-bindings" version = "0.2.1" diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index 4582093..2b37c56 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "vhost-user-vsock" +name = "vhost-device-vsock" version = "0.1.0" authors = ["Harshavardhan Unnibhavi ", "Stefano Garzarella "] description = "A virtio-vsock device using the vhost-user protocol." diff --git a/crates/vsock/README.md b/crates/vsock/README.md index 605b0a5..f4e946d 100644 --- a/crates/vsock/README.md +++ b/crates/vsock/README.md @@ -1,8 +1,8 @@ -# vhost-user-vsock +# vhost-device-vsock ## Design -The crate introduces a vhost-user-vsock device that enables communication between an +The crate introduces a vhost-device-vsock device that enables communication between an application running in the guest i.e inside a VM and an application running on the host i.e outside the VM. The application running in the guest communicates over VM sockets i.e over AF_VSOCK sockets. The application running on the host connects to a @@ -38,28 +38,28 @@ the crate are split into various files as described below: ## Usage -Run the vhost-user-vsock device: +Run the vhost-device-vsock device: ``` -vhost-user-vsock --guest-cid= \ +vhost-device-vsock --guest-cid= \ --socket= \ --uds-path= \ [--tx-buffer-size=host packets)>] ``` or ``` -vhost-user-vsock --vm guest_cid=,socket=,uds-path=[,tx-buffer-size=host packets)>] +vhost-device-vsock --vm guest_cid=,socket=,uds-path=[,tx-buffer-size=host packets)>] ``` Specify the `--vm` argument multiple times to specify multiple devices like this: ``` -vhost-user-vsock \ +vhost-device-vsock \ --vm guest-cid=3,socket=/tmp/vhost3.socket,uds-path=/tmp/vm3.vsock \ --vm guest-cid=4,socket=/tmp/vhost4.socket,uds-path=/tmp/vm4.vsock,tx-buffer-size=32768 ``` Or use a configuration file: ``` -vhost-user-vsock --config= +vhost-device-vsock --config= ``` Configuration file example: @@ -89,11 +89,11 @@ qemu-system-x86_64 \ ## Working example ```sh -shell1$ vhost-user-vsock --vm guest-cid=4,uds-path=/tmp/vm4.vsock,socket=/tmp/vhost4.socket +shell1$ vhost-device-vsock --vm guest-cid=4,uds-path=/tmp/vm4.vsock,socket=/tmp/vhost4.socket ``` or if you want to configure the TX buffer size ```sh -shell1$ vhost-user-vsock --vm guest-cid=4,uds-path=/tmp/vm4.vsock,socket=/tmp/vhost4.socket,tx-buffer-size=65536 +shell1$ vhost-device-vsock --vm guest-cid=4,uds-path=/tmp/vm4.vsock,socket=/tmp/vhost4.socket,tx-buffer-size=65536 ``` ```sh @@ -148,7 +148,7 @@ If you add multiple VMs, they can communicate with each other. For example, if y CID 3 and 4, you can run the following commands to make them communicate: ```sh -shell1$ vhost-user-vsock --vm guest-cid=3,uds-path=/tmp/vm3.vsock,socket=/tmp/vhost3.socket \ +shell1$ vhost-device-vsock --vm guest-cid=3,uds-path=/tmp/vm3.vsock,socket=/tmp/vhost3.socket \ --vm guest-cid=4,uds-path=/tmp/vm4.vsock,socket=/tmp/vhost4.socket shell2$ qemu-system-x86_64 \ -drive file=vm1.qcow2,format=qcow2,if=virtio -smp 2 -m 512M -mem-prealloc \ diff --git a/crates/vsock/src/main.rs b/crates/vsock/src/main.rs index 79f11ba..51f3759 100644 --- a/crates/vsock/src/main.rs +++ b/crates/vsock/src/main.rs @@ -185,7 +185,7 @@ impl TryFrom for Vec { } /// This is the public API through which an external program starts the -/// vhost-user-vsock backend server. +/// vhost-device-vsock backend server. pub(crate) fn start_backend_server( config: VsockConfig, cid_map: Arc>, @@ -199,7 +199,7 @@ pub(crate) fn start_backend_server( let listener = Listener::new(config.get_socket_path(), true).unwrap(); let mut daemon = VhostUserDaemon::new( - String::from("vhost-user-vsock"), + String::from("vhost-device-vsock"), backend.clone(), GuestMemoryAtomic::new(GuestMemoryMmap::new()), ) @@ -453,7 +453,7 @@ mod tests { let backend = Arc::new(VhostUserVsockBackend::new(config, cid_map).unwrap()); let daemon = VhostUserDaemon::new( - String::from("vhost-user-vsock"), + String::from("vhost-device-vsock"), backend.clone(), GuestMemoryAtomic::new(GuestMemoryMmap::new()), ) From 6305c66f227717e639b7269c6c37efc8a7b1c5f8 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Fri, 21 Jul 2023 10:19:40 +0200 Subject: [PATCH 108/189] scsi: replaced some leftover of vhost-user-scsi There was some reference left in the documentation and sources to "vhost-user-scsi" that we had changed during the rebase. Let's change them to "vhost-device-scsi". Everything should be safe. We leave "vhost-user-scsi" in crates/scsi/src/scsi/emulation/response_data.rs because it looks like an identifier with some constant size. We will fix in the future. Signed-off-by: Stefano Garzarella --- crates/scsi/ARCHITECTURE.md | 2 +- crates/scsi/README.md | 8 ++++---- crates/scsi/src/main.rs | 2 +- crates/scsi/src/scsi/emulation/target.rs | 2 +- crates/scsi/test/README.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/scsi/ARCHITECTURE.md b/crates/scsi/ARCHITECTURE.md index 2a2f7fa..f7b9094 100644 --- a/crates/scsi/ARCHITECTURE.md +++ b/crates/scsi/ARCHITECTURE.md @@ -1,4 +1,4 @@ -# vhost-user-scsi architecture +# vhost-device-scsi architecture Rough outline of the different pieces and how they fit together: diff --git a/crates/scsi/README.md b/crates/scsi/README.md index 46779df..515d7b9 100644 --- a/crates/scsi/README.md +++ b/crates/scsi/README.md @@ -1,13 +1,13 @@ -# vhost-user-scsi +# vhost-device-scsi -This is a Rust implementation of a vhost-user-scsi daemon. +This is a Rust implementation of a vhost-device-scsi daemon. ## Usage -Run the vhost-user-scsi daemon: +Run the vhost-device-scsi daemon: ``` -vhost-user-scsi -r --socket-path /tmp/vhost-user-scsi.sock /path/to/image.raw /path/to/second-image.raw ... +vhost-device-scsi -r --socket-path /tmp/vhost-user-scsi.sock /path/to/image.raw /path/to/second-image.raw ... ``` Run QEMU: diff --git a/crates/scsi/src/main.rs b/crates/scsi/src/main.rs index 8ca2922..cd075b8 100644 --- a/crates/scsi/src/main.rs +++ b/crates/scsi/src/main.rs @@ -94,7 +94,7 @@ fn create_backend(args: &ScsiArgs) -> Result { fn start_backend(backend: VhostUserScsiBackend, args: ScsiArgs) -> Result<()> { let backend = Arc::new(RwLock::new(backend)); let mut daemon = VhostUserDaemon::new( - "vhost-user-scsi".into(), + "vhost-device-scsi".into(), Arc::clone(&backend), GuestMemoryAtomic::new(GuestMemoryMmap::new()), ) diff --git a/crates/scsi/src/scsi/emulation/target.rs b/crates/scsi/src/scsi/emulation/target.rs index 82e660c..4ce1a34 100644 --- a/crates/scsi/src/scsi/emulation/target.rs +++ b/crates/scsi/src/scsi/emulation/target.rs @@ -44,7 +44,7 @@ pub(crate) trait LogicalUnit: Send + Sync { ) -> Result; } -/// A SCSI target implemented by emulating a device within vhost-user-scsi. +/// A SCSI target implemented by emulating a device within vhost-device-scsi. pub(crate) struct EmulatedTarget { luns: Vec>, } diff --git a/crates/scsi/test/README.md b/crates/scsi/test/README.md index 9322054..4b921ec 100644 --- a/crates/scsi/test/README.md +++ b/crates/scsi/test/README.md @@ -6,7 +6,7 @@ This folder contains some tooling for tests For running these tests, you need a KVM enabled x86_64 machine and `podman`. -vhost-user-scsi must have been built already. +vhost-device-scsi must have been built already. ## Performed tests From 9f10349f99e38b7c803ffe8c0fd0eeaa7a912fda Mon Sep 17 00:00:00 2001 From: Jeongik Cha Date: Thu, 27 Jul 2023 01:52:32 +0900 Subject: [PATCH 109/189] vsock: always epoll_register with cloned stream fd VsockConnection::stream which is cloned is always used for epoll_register, except add_new_guest_conn. Only in add_new_guest_conn, the original stream is used. Because a stream's raw fd is used for the key of listener_map, it cannot find proper listener after the first packet. Signed-off-by: Jeongik Cha --- crates/vsock/src/thread_backend.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/vsock/src/thread_backend.rs b/crates/vsock/src/thread_backend.rs index 5677ec5..1369122 100644 --- a/crates/vsock/src/thread_backend.rs +++ b/crates/vsock/src/thread_backend.rs @@ -290,10 +290,6 @@ impl VsockThreadBackend { stream: UnixStream, pkt: &VsockPacket, ) -> Result<()> { - let stream_fd = stream.as_raw_fd(); - self.listener_map - .insert(stream_fd, ConnMapKey::new(pkt.dst_port(), pkt.src_port())); - let conn = VsockConnection::new_peer_init( stream.try_clone().map_err(Error::UnixConnect)?, pkt.dst_cid(), @@ -304,6 +300,9 @@ impl VsockThreadBackend { pkt.buf_alloc(), self.tx_buffer_size, ); + let stream_fd = conn.stream.as_raw_fd(); + self.listener_map + .insert(stream_fd, ConnMapKey::new(pkt.dst_port(), pkt.src_port())); self.conn_map .insert(ConnMapKey::new(pkt.dst_port(), pkt.src_port()), conn); From f2acb05c33f2d4d104244b4d0b1beea1284689cc Mon Sep 17 00:00:00 2001 From: Jeongik Cha Date: Fri, 28 Jul 2023 00:16:48 +0900 Subject: [PATCH 110/189] vsock: Set BACKEND_EVENT based on NUM_QUEUES BACKEND_EVENT value depends on NUM_QUEUES, because it is the next value of NUM_QUEUES, so set it based on NUM_QUEUES Signed-off-by: Jeongik Cha --- crates/vsock/src/vhu_vsock.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index bb59e05..eedd234 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -37,10 +37,12 @@ const TX_QUEUE_EVENT: u16 = 1; const EVT_QUEUE_EVENT: u16 = 2; /// Notification coming from the backend. -pub(crate) const BACKEND_EVENT: u16 = 3; +/// Event range [0...num_queues] is reserved for queues and exit event. +/// So NUM_QUEUES + 1 is used. +pub(crate) const BACKEND_EVENT: u16 = (NUM_QUEUES + 1) as u16; /// Notification coming from the sibling VM. -pub(crate) const SIBLING_VM_EVENT: u16 = 4; +pub(crate) const SIBLING_VM_EVENT: u16 = BACKEND_EVENT + 1; /// CID of the host pub(crate) const VSOCK_HOST_CID: u64 = 2; From c2ba07dc0652a705aba0c3737e4ce09052839726 Mon Sep 17 00:00:00 2001 From: Jeongik Cha Date: Fri, 28 Jul 2023 00:18:15 +0900 Subject: [PATCH 111/189] vsock: add warning message for EVT_QUEUE_EVENT EVT_QUEUE_EVENT is an unexpected message from current impl, so add warning for that Signed-off-by: Jeongik Cha --- crates/vsock/src/vhu_vsock.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index eedd234..83d6baf 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -297,7 +297,9 @@ impl VhostUserBackend for VhostUserVsockBackend { TX_QUEUE_EVENT => { thread.process_tx(vring_tx, evt_idx)?; } - EVT_QUEUE_EVENT => {} + EVT_QUEUE_EVENT => { + warn!("Received an unexpected EVT_QUEUE_EVENT"); + } BACKEND_EVENT => { thread.process_backend_evt(evset); if let Err(e) = thread.process_tx(vring_tx, evt_idx) { From 637969d0e7f01a3b63853bbd2311bda0e758fea3 Mon Sep 17 00:00:00 2001 From: Jeongik Cha Date: Fri, 28 Jul 2023 00:19:16 +0900 Subject: [PATCH 112/189] vsock: Increase NUM_QUEUES to 3 In virtio standard, vsock uses 3 vqs. crosvm expects 3 vqs from vhost-user-vsock impl, but this vhost-user-vsock device sets up only 2 vqs because event vq isn't handled. And it causes crash in crosvm. To avoid crash in crosvm, I increase NUM_QUEUES to 3 Signed-off-by: Jeongik Cha --- crates/vsock/src/vhu_vsock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index 83d6baf..c64f11f 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -26,7 +26,7 @@ use crate::vhu_vsock_thread::*; pub(crate) type CidMap = HashMap>, EventFd)>; -const NUM_QUEUES: usize = 2; +const NUM_QUEUES: usize = 3; const QUEUE_SIZE: usize = 256; // New descriptors pending on the rx queue From 2c3786084d181fd615ded300334e3b9b56b5e2f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 04:48:53 +0000 Subject: [PATCH 113/189] build(deps): bump proc-macro2 from 1.0.63 to 1.0.66 Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.63 to 1.0.66. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.63...1.0.66) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3309fc5..57f9d52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -823,9 +823,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] From 005bdfa17a7cd6cb24e7d0c50a03db9fbfd83eee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 04:48:35 +0000 Subject: [PATCH 114/189] build(deps): bump num_enum from 0.5.11 to 0.6.1 Bumps [num_enum](https://github.com/illicitonion/num_enum) from 0.5.11 to 0.6.1. - [Commits](https://github.com/illicitonion/num_enum/compare/0.5.11...0.6.1) --- updated-dependencies: - dependency-name: num_enum dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 10 +++++----- crates/scsi/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 57f9d52..d54c929 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -696,23 +696,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.23", ] [[package]] diff --git a/crates/scsi/Cargo.toml b/crates/scsi/Cargo.toml index 2dbfca5..ae3ff8a 100644 --- a/crates/scsi/Cargo.toml +++ b/crates/scsi/Cargo.toml @@ -19,7 +19,7 @@ clap = { version = "4.3", features = ["derive"] } env_logger = "0.10" epoll = "4.3" log = "0.4" -num_enum = "0.5" +num_enum = "0.6" thiserror = "1.0" vhost = { version = "0.8", features = ["vhost-user-slave"] } vhost-user-backend = "0.10" From 1da9705549b6849c7a204767006e010258447591 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jul 2023 04:48:45 +0000 Subject: [PATCH 115/189] build(deps): bump thiserror from 1.0.41 to 1.0.44 Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.41 to 1.0.44. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.41...1.0.44) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d54c929..453d803 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1123,18 +1123,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.41" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.41" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", From bd59ff2462f458f49154006c14c99cd6dd8c14b1 Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Mon, 24 Jul 2023 01:17:06 -0700 Subject: [PATCH 116/189] vsock: Allow optional fields in config file Currently need to provide all the fields in the yaml config file, otherwise the application panics. Modify this behaviour to allow not specifying the optional fields to make it consistent with specifying the configuration using only CLI arguments. Signed-off-by: Priyansh Rathi --- crates/vsock/src/main.rs | 45 ++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/crates/vsock/src/main.rs b/crates/vsock/src/main.rs index 51f3759..69e23ed 100644 --- a/crates/vsock/src/main.rs +++ b/crates/vsock/src/main.rs @@ -56,7 +56,7 @@ enum BackendError { CouldNotCreateDaemon(vhost_user_backend::Error), } -#[derive(Args, Clone, Debug, Deserialize)] +#[derive(Args, Clone, Debug)] struct VsockParam { /// Context identifier of the guest which uniquely identifies the device for its lifetime. #[arg( @@ -80,6 +80,14 @@ struct VsockParam { tx_buffer_size: u32, } +#[derive(Clone, Debug, Deserialize)] +struct ConfigFileVsockParam { + guest_cid: Option, + socket: String, + uds_path: String, + tx_buffer_size: Option, +} + #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct VsockArgs { @@ -137,16 +145,16 @@ impl VsockArgs { .add_source(config::File::new(c.as_str(), config::FileFormat::Yaml)) .build(); if let Ok(s) = b { - let mut v = s.get::>("vms").unwrap(); + let mut v = s.get::>("vms").unwrap(); if !v.is_empty() { let parsed: Vec = v .drain(..) .map(|p| { VsockConfig::new( - p.guest_cid, + p.guest_cid.unwrap_or(DEFAULT_GUEST_CID), p.socket.trim().to_string(), p.uds_path.trim().to_string(), - p.tx_buffer_size, + p.tx_buffer_size.unwrap_or(DEFAULT_TX_BUFFER_SIZE), ) }) .collect(); @@ -407,7 +415,7 @@ mod tests { - guest_cid: 4 socket: {} uds_path: {} - tx_buffer_size: 65536", + tx_buffer_size: 32768", socket_path.display(), uds_path.display(), ) @@ -423,8 +431,33 @@ mod tests { assert_eq!(config.get_guest_cid(), 4); assert_eq!(config.get_socket_path(), socket_path.display().to_string()); assert_eq!(config.get_uds_path(), uds_path.display().to_string()); - std::fs::remove_file(&config_path).unwrap(); + assert_eq!(config.get_tx_buffer_size(), 32768); + // Now test that optional parameters are correctly set to their default values. + let mut yaml = File::create(&config_path).unwrap(); + yaml.write_all( + format!( + "vms: + - socket: {} + uds_path: {}", + socket_path.display(), + uds_path.display(), + ) + .as_bytes(), + ) + .unwrap(); + let args = VsockArgs::from_file(&config_path.display().to_string()); + + let configs = Vec::::try_from(args).unwrap(); + assert_eq!(configs.len(), 1); + + let config = &configs[0]; + assert_eq!(config.get_guest_cid(), DEFAULT_GUEST_CID); + assert_eq!(config.get_socket_path(), socket_path.display().to_string()); + assert_eq!(config.get_uds_path(), uds_path.display().to_string()); + assert_eq!(config.get_tx_buffer_size(), DEFAULT_TX_BUFFER_SIZE); + + std::fs::remove_file(&config_path).unwrap(); test_dir.close().unwrap(); } From decfd8bd7338574f5d370eece3f5a4355fedfdbf Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Mon, 24 Jul 2023 01:07:18 -0700 Subject: [PATCH 117/189] vsock: Add VM groups in sibling communication Restrict the VMs a given VM can communicate with by introducing VM groups. A group is simply a list of names assigned to the device in the configuration. A VM can communicate with another VM only if the list of group names assigned to their devices have atleast one group name in common. Signed-off-by: Priyansh Rathi --- crates/vsock/README.md | 26 ++++++++---- crates/vsock/src/main.rs | 63 ++++++++++++++++++++++++---- crates/vsock/src/thread_backend.rs | 45 ++++++++++++++++++-- crates/vsock/src/vhu_vsock.rs | 27 ++++++++++-- crates/vsock/src/vhu_vsock_thread.rs | 18 +++++++- 5 files changed, 157 insertions(+), 22 deletions(-) diff --git a/crates/vsock/README.md b/crates/vsock/README.md index f4e946d..2413971 100644 --- a/crates/vsock/README.md +++ b/crates/vsock/README.md @@ -43,17 +43,18 @@ Run the vhost-device-vsock device: vhost-device-vsock --guest-cid= \ --socket= \ --uds-path= \ - [--tx-buffer-size=host packets)>] + [--tx-buffer-size=host packets)>] \ + [--groups=] ``` or ``` -vhost-device-vsock --vm guest_cid=,socket=,uds-path=[,tx-buffer-size=host packets)>] +vhost-device-vsock --vm guest_cid=,socket=,uds-path=[,tx-buffer-size=host packets)>][,groups=] ``` Specify the `--vm` argument multiple times to specify multiple devices like this: ``` vhost-device-vsock \ ---vm guest-cid=3,socket=/tmp/vhost3.socket,uds-path=/tmp/vm3.vsock \ +--vm guest-cid=3,socket=/tmp/vhost3.socket,uds-path=/tmp/vm3.vsock,groups=group1+groupA \ --vm guest-cid=4,socket=/tmp/vhost4.socket,uds-path=/tmp/vm4.vsock,tx-buffer-size=32768 ``` @@ -69,10 +70,12 @@ vms: socket: /tmp/vhost3.socket uds_path: /tmp/vm3.sock tx_buffer_size: 65536 + groups: group1+groupA - guest_cid: 4 socket: /tmp/vhost4.socket uds_path: /tmp/vm4.sock tx_buffer_size: 32768 + groups: group2+groupB ``` Run VMM (e.g. QEMU): @@ -144,12 +147,17 @@ guest$ nc --vsock 2 1234 ### Sibling VM communication -If you add multiple VMs, they can communicate with each other. For example, if you have two VMs with -CID 3 and 4, you can run the following commands to make them communicate: +If you add multiple VMs with their devices configured with at least one common group name, they can communicate with +each other. If you don't explicitly specify a group name, a default group will be assigned to the device with name +`default`, and all such devices will be able to communicate with each other. Or you can choose a different list of +group names for each device, and only devices with the at least one group in commmon will be able to communicate with +each other. + +For example, if you have two VMs with CID 3 and 4, you can run the following commands to make them communicate: ```sh -shell1$ vhost-device-vsock --vm guest-cid=3,uds-path=/tmp/vm3.vsock,socket=/tmp/vhost3.socket \ - --vm guest-cid=4,uds-path=/tmp/vm4.vsock,socket=/tmp/vhost4.socket +shell1$ vhost-device-vsock --vm guest-cid=3,uds-path=/tmp/vm3.vsock,socket=/tmp/vhost3.socket,groups=group1+group2 \ + --vm guest-cid=4,uds-path=/tmp/vm4.vsock,socket=/tmp/vhost4.socket,groups=group1 shell2$ qemu-system-x86_64 \ -drive file=vm1.qcow2,format=qcow2,if=virtio -smp 2 -m 512M -mem-prealloc \ -object memory-backend-file,share=on,id=mem0,size=512M,mem-path="/dev/hugepages" \ @@ -164,6 +172,10 @@ shell3$ qemu-system-x86_64 \ -device vhost-user-vsock-pci,chardev=char0 ``` +Please note that here the `groups` parameter is specified just for clarity, but it is not necessary to specify it if you want +to use the default group and make all the devices communicate with one another. It is useful to specify a list of groups +when you want fine-grained control over which devices can communicate with each other. + ```sh # nc-vsock patched to set `.svm_flags = VMADDR_FLAG_TO_HOST` guest_cid3$ nc-vsock -l 1234 diff --git a/crates/vsock/src/main.rs b/crates/vsock/src/main.rs index 69e23ed..fa38bfb 100644 --- a/crates/vsock/src/main.rs +++ b/crates/vsock/src/main.rs @@ -27,6 +27,7 @@ use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; const DEFAULT_GUEST_CID: u64 = 3; const DEFAULT_TX_BUFFER_SIZE: u32 = 64 * 1024; +const DEFAULT_GROUP_NAME: &str = "default"; #[derive(Debug, ThisError)] enum CliError { @@ -78,6 +79,17 @@ struct VsockParam { /// The size of the buffer used for the TX virtqueue #[clap(long, default_value_t = DEFAULT_TX_BUFFER_SIZE, conflicts_with = "config", conflicts_with = "vm")] tx_buffer_size: u32, + + /// The list of group names to which the device belongs. + /// A group is a set of devices that allow sibling communication between their guests. + #[arg( + long, + default_value_t = String::from(DEFAULT_GROUP_NAME), + conflicts_with = "config", + conflicts_with = "vm", + verbatim_doc_comment + )] + groups: String, } #[derive(Clone, Debug, Deserialize)] @@ -86,6 +98,7 @@ struct ConfigFileVsockParam { socket: String, uds_path: String, tx_buffer_size: Option, + groups: Option, } #[derive(Parser, Debug)] @@ -95,8 +108,9 @@ struct VsockArgs { param: Option, /// Device parameters corresponding to a VM in the form of comma separated key=value pairs. - /// The allowed keys are: guest_cid, socket, uds_path and tx_buffer_size - /// Example: --vm guest-cid=3,socket=/tmp/vhost3.socket,uds-path=/tmp/vm3.vsock,tx-buffer-size=65536 + /// The allowed keys are: guest_cid, socket, uds_path, tx_buffer_size and group. + /// Example: + /// --vm guest-cid=3,socket=/tmp/vhost3.socket,uds-path=/tmp/vm3.vsock,tx-buffer-size=65536,groups=group1+group2 /// Multiple instances of this argument can be provided to configure devices for multiple guests. #[arg(long, conflicts_with = "config", verbatim_doc_comment, value_parser = parse_vm_params)] vm: Option>, @@ -111,6 +125,7 @@ fn parse_vm_params(s: &str) -> Result { let mut socket = None; let mut uds_path = None; let mut tx_buffer_size = None; + let mut groups = None; for arg in s.trim().split(',') { let mut parts = arg.split('='); @@ -126,6 +141,7 @@ fn parse_vm_params(s: &str) -> Result { "tx_buffer_size" | "tx-buffer-size" => { tx_buffer_size = Some(val.parse().map_err(VmArgsParseError::ParseInteger)?) } + "groups" => groups = Some(val.split('+').map(String::from).collect()), _ => return Err(VmArgsParseError::InvalidKey(key.to_string())), } } @@ -135,6 +151,7 @@ fn parse_vm_params(s: &str) -> Result { socket.ok_or_else(|| VmArgsParseError::RequiredKeyNotFound("socket".to_string()))?, uds_path.ok_or_else(|| VmArgsParseError::RequiredKeyNotFound("uds-path".to_string()))?, tx_buffer_size.unwrap_or(DEFAULT_TX_BUFFER_SIZE), + groups.unwrap_or(vec![DEFAULT_GROUP_NAME.to_string()]), )) } @@ -155,6 +172,9 @@ impl VsockArgs { p.socket.trim().to_string(), p.uds_path.trim().to_string(), p.tx_buffer_size.unwrap_or(DEFAULT_TX_BUFFER_SIZE), + p.groups.map_or(vec![DEFAULT_GROUP_NAME.to_string()], |g| { + g.trim().split('+').map(String::from).collect() + }), ) }) .collect(); @@ -185,6 +205,7 @@ impl TryFrom for Vec { p.socket.trim().to_string(), p.uds_path.trim().to_string(), p.tx_buffer_size, + p.groups.trim().split('+').map(String::from).collect(), )]) }), }, @@ -289,13 +310,20 @@ mod tests { use tempfile::tempdir; impl VsockArgs { - fn from_args(guest_cid: u64, socket: &str, uds_path: &str, tx_buffer_size: u32) -> Self { + fn from_args( + guest_cid: u64, + socket: &str, + uds_path: &str, + tx_buffer_size: u32, + groups: &str, + ) -> Self { VsockArgs { param: Some(VsockParam { guest_cid, socket: socket.to_string(), uds_path: uds_path.to_string(), tx_buffer_size, + groups: groups.to_string(), }), vm: None, config: None, @@ -316,7 +344,7 @@ mod tests { let socket_path = test_dir.path().join("vhost4.socket").display().to_string(); let uds_path = test_dir.path().join("vm4.vsock").display().to_string(); - let args = VsockArgs::from_args(3, &socket_path, &uds_path, 64 * 1024); + let args = VsockArgs::from_args(3, &socket_path, &uds_path, 64 * 1024, "group1"); let configs = Vec::::try_from(args); assert!(configs.is_ok()); @@ -329,6 +357,7 @@ mod tests { assert_eq!(config.get_socket_path(), socket_path); assert_eq!(config.get_uds_path(), uds_path); assert_eq!(config.get_tx_buffer_size(), 64 * 1024); + assert_eq!(config.get_groups(), vec!["group1".to_string()]); test_dir.close().unwrap(); } @@ -349,8 +378,8 @@ mod tests { ]; let params = format!( "--vm socket={vhost3_socket},uds_path={vm3_vsock} \ - --vm socket={vhost4_socket},uds-path={vm4_vsock},guest-cid=4,tx_buffer_size=65536 \ - --vm guest-cid=5,socket={vhost5_socket},uds_path={vm5_vsock},tx-buffer-size=32768", + --vm socket={vhost4_socket},uds-path={vm4_vsock},guest-cid=4,tx_buffer_size=65536,groups=group1 \ + --vm groups=group2+group3,guest-cid=5,socket={vhost5_socket},uds_path={vm5_vsock},tx-buffer-size=32768", vhost3_socket = socket_paths[0].display(), vhost4_socket = socket_paths[1].display(), vhost5_socket = socket_paths[2].display(), @@ -378,6 +407,7 @@ mod tests { ); assert_eq!(config.get_uds_path(), uds_paths[0].display().to_string()); assert_eq!(config.get_tx_buffer_size(), 65536); + assert_eq!(config.get_groups(), vec![DEFAULT_GROUP_NAME.to_string()]); let config = configs.get(1).unwrap(); assert_eq!(config.get_guest_cid(), 4); @@ -387,6 +417,7 @@ mod tests { ); assert_eq!(config.get_uds_path(), uds_paths[1].display().to_string()); assert_eq!(config.get_tx_buffer_size(), 65536); + assert_eq!(config.get_groups(), vec!["group1".to_string()]); let config = configs.get(2).unwrap(); assert_eq!(config.get_guest_cid(), 5); @@ -396,6 +427,10 @@ mod tests { ); assert_eq!(config.get_uds_path(), uds_paths[2].display().to_string()); assert_eq!(config.get_tx_buffer_size(), 32768); + assert_eq!( + config.get_groups(), + vec!["group2".to_string(), "group3".to_string()] + ); test_dir.close().unwrap(); } @@ -415,7 +450,8 @@ mod tests { - guest_cid: 4 socket: {} uds_path: {} - tx_buffer_size: 32768", + tx_buffer_size: 32768 + groups: group1+group2", socket_path.display(), uds_path.display(), ) @@ -432,6 +468,10 @@ mod tests { assert_eq!(config.get_socket_path(), socket_path.display().to_string()); assert_eq!(config.get_uds_path(), uds_path.display().to_string()); assert_eq!(config.get_tx_buffer_size(), 32768); + assert_eq!( + config.get_groups(), + vec!["group1".to_string(), "group2".to_string()] + ); // Now test that optional parameters are correctly set to their default values. let mut yaml = File::create(&config_path).unwrap(); @@ -456,6 +496,7 @@ mod tests { assert_eq!(config.get_socket_path(), socket_path.display().to_string()); assert_eq!(config.get_uds_path(), uds_path.display().to_string()); assert_eq!(config.get_tx_buffer_size(), DEFAULT_TX_BUFFER_SIZE); + assert_eq!(config.get_groups(), vec![DEFAULT_GROUP_NAME.to_string()]); std::fs::remove_file(&config_path).unwrap(); test_dir.close().unwrap(); @@ -479,7 +520,13 @@ mod tests { .display() .to_string(); - let config = VsockConfig::new(CID, vhost_socket_path, vsock_socket_path, CONN_TX_BUF_SIZE); + let config = VsockConfig::new( + CID, + vhost_socket_path, + vsock_socket_path, + CONN_TX_BUF_SIZE, + vec![DEFAULT_GROUP_NAME.to_string()], + ); let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); diff --git a/crates/vsock/src/thread_backend.rs b/crates/vsock/src/thread_backend.rs index 1369122..6d5e80e 100644 --- a/crates/vsock/src/thread_backend.rs +++ b/crates/vsock/src/thread_backend.rs @@ -2,6 +2,7 @@ use std::{ collections::{HashMap, HashSet, VecDeque}, + ops::Deref, os::unix::{ net::UnixStream, prelude::{AsRawFd, RawFd}, @@ -70,6 +71,8 @@ pub(crate) struct VsockThreadBackend { pub cid_map: Arc>, /// Queue of raw vsock packets recieved from sibling VMs to be sent to the guest. pub raw_pkts_queue: Arc>, + /// Set of groups assigned to the device which it is allowed to communicate with. + groups_set: Arc>>, } impl VsockThreadBackend { @@ -79,6 +82,7 @@ impl VsockThreadBackend { epoll_fd: i32, guest_cid: u64, tx_buffer_size: u32, + groups_set: Arc>>, cid_map: Arc>, ) -> Self { Self { @@ -95,6 +99,7 @@ impl VsockThreadBackend { tx_buffer_size, cid_map, raw_pkts_queue: Arc::new(RwLock::new(VecDeque::new())), + groups_set, } } @@ -180,7 +185,21 @@ impl VsockThreadBackend { if dst_cid != VSOCK_HOST_CID { let cid_map = self.cid_map.read().unwrap(); if cid_map.contains_key(&dst_cid) { - let (sibling_raw_pkts_queue, sibling_event_fd) = cid_map.get(&dst_cid).unwrap(); + let (sibling_raw_pkts_queue, sibling_groups_set, sibling_event_fd) = + cid_map.get(&dst_cid).unwrap(); + + if self + .groups_set + .read() + .unwrap() + .is_disjoint(sibling_groups_set.read().unwrap().deref()) + { + info!( + "vsock: dropping packet for cid: {:?} due to group mismatch", + dst_cid + ); + return Ok(()); + } sibling_raw_pkts_queue .write() @@ -337,6 +356,7 @@ mod tests { const DATA_LEN: usize = 16; const CONN_TX_BUF_SIZE: u32 = 64 * 1024; + const GROUP_NAME: &str = "default"; #[test] fn test_vsock_thread_backend() { @@ -352,6 +372,8 @@ mod tests { let epoll_fd = epoll::create(false).unwrap(); + let groups_set: HashSet = vec![GROUP_NAME.to_string()].into_iter().collect(); + let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); let mut vtp = VsockThreadBackend::new( @@ -359,6 +381,7 @@ mod tests { epoll_fd, CID, CONN_TX_BUF_SIZE, + Arc::new(RwLock::new(groups_set)), cid_map, ); @@ -434,14 +457,30 @@ mod tests { sibling_vhost_socket_path, sibling_vsock_socket_path, CONN_TX_BUF_SIZE, + vec!["group1", "group2", "group3"] + .into_iter() + .map(String::from) + .collect(), ); let sibling_backend = Arc::new(VhostUserVsockBackend::new(sibling_config, cid_map.clone()).unwrap()); let epoll_fd = epoll::create(false).unwrap(); - let mut vtp = - VsockThreadBackend::new(vsock_socket_path, epoll_fd, CID, CONN_TX_BUF_SIZE, cid_map); + + let groups_set: HashSet = vec!["groupA", "groupB", "group3"] + .into_iter() + .map(String::from) + .collect(); + + let mut vtp = VsockThreadBackend::new( + vsock_socket_path, + epoll_fd, + CID, + CONN_TX_BUF_SIZE, + Arc::new(RwLock::new(groups_set)), + cid_map, + ); assert!(!vtp.pending_raw_pkts()); diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index c64f11f..0d25b38 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause use std::{ - collections::HashMap, + collections::{HashMap, HashSet}, io::{self, Result as IoResult}, sync::{Arc, Mutex, RwLock}, u16, u32, u64, u8, @@ -24,7 +24,8 @@ use vmm_sys_util::{ use crate::thread_backend::RawPktsQ; use crate::vhu_vsock_thread::*; -pub(crate) type CidMap = HashMap>, EventFd)>; +pub(crate) type CidMap = + HashMap>, Arc>>, EventFd)>; const NUM_QUEUES: usize = 3; const QUEUE_SIZE: usize = 256; @@ -150,17 +151,25 @@ pub(crate) struct VsockConfig { socket: String, uds_path: String, tx_buffer_size: u32, + groups: Vec, } impl VsockConfig { /// Create a new instance of the VsockConfig struct, containing the /// parameters to be fed into the vsock-backend server. - pub fn new(guest_cid: u64, socket: String, uds_path: String, tx_buffer_size: u32) -> Self { + pub fn new( + guest_cid: u64, + socket: String, + uds_path: String, + tx_buffer_size: u32, + groups: Vec, + ) -> Self { Self { guest_cid, socket, uds_path, tx_buffer_size, + groups, } } @@ -184,6 +193,10 @@ impl VsockConfig { pub fn get_tx_buffer_size(&self) -> u32 { self.tx_buffer_size } + + pub fn get_groups(&self) -> Vec { + self.groups.clone() + } } /// A local port and peer port pair used to retrieve @@ -227,6 +240,7 @@ impl VhostUserVsockBackend { config.get_uds_path(), config.get_guest_cid(), config.get_tx_buffer_size(), + config.get_groups(), cid_map, )?); let queues_per_thread = vec![QUEUE_MASK]; @@ -364,6 +378,8 @@ mod tests { fn test_vsock_backend() { const CID: u64 = 3; + let groups_list: Vec = vec![String::from("default")]; + let test_dir = tempdir().expect("Could not create a temp test directory."); let vhost_socket_path = test_dir @@ -382,6 +398,7 @@ mod tests { vhost_socket_path.to_string(), vsock_socket_path.to_string(), CONN_TX_BUF_SIZE, + groups_list, ); let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); @@ -451,6 +468,8 @@ mod tests { fn test_vsock_backend_failures() { const CID: u64 = 3; + let groups: Vec = vec![String::from("default")]; + let test_dir = tempdir().expect("Could not create a temp test directory."); let vhost_socket_path = test_dir @@ -469,6 +488,7 @@ mod tests { "/sys/not_allowed.socket".to_string(), "/sys/not_allowed.vsock".to_string(), CONN_TX_BUF_SIZE, + groups.clone(), ); let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); @@ -481,6 +501,7 @@ mod tests { vhost_socket_path.to_string(), vsock_socket_path.to_string(), CONN_TX_BUF_SIZE, + groups, ); let backend = VhostUserVsockBackend::new(config, cid_map).unwrap(); diff --git a/crates/vsock/src/vhu_vsock_thread.rs b/crates/vsock/src/vhu_vsock_thread.rs index 726e2b6..0c2ab2f 100644 --- a/crates/vsock/src/vhu_vsock_thread.rs +++ b/crates/vsock/src/vhu_vsock_thread.rs @@ -1,9 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause use std::{ + collections::HashSet, fs::File, io, io::Read, + iter::FromIterator, num::Wrapping, ops::Deref, os::unix::{ @@ -79,6 +81,7 @@ impl VhostUserVsockThread { uds_path: String, guest_cid: u64, tx_buffer_size: u32, + groups: Vec, cid_map: Arc>, ) -> Result { // TODO: better error handling, maybe add a param to force the unlink @@ -93,6 +96,10 @@ impl VhostUserVsockThread { let host_raw_fd = host_sock.as_raw_fd(); + let mut groups = groups; + let groups_set: Arc>> = + Arc::new(RwLock::new(HashSet::from_iter(groups.drain(..)))); + let sibling_event_fd = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?; let thread_backend = VsockThreadBackend::new( @@ -100,6 +107,7 @@ impl VhostUserVsockThread { epoll_fd, guest_cid, tx_buffer_size, + groups_set.clone(), cid_map.clone(), ); @@ -107,6 +115,7 @@ impl VhostUserVsockThread { guest_cid, ( thread_backend.raw_pkts_queue.clone(), + groups_set, sibling_event_fd.try_clone().unwrap(), ), ); @@ -723,6 +732,8 @@ mod tests { #[test] fn test_vsock_thread() { + let groups: Vec = vec![String::from("default")]; + let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); let test_dir = tempdir().expect("Could not create a temp test directory."); @@ -735,6 +746,7 @@ mod tests { .to_string(), 3, CONN_TX_BUF_SIZE, + groups, cid_map, ); assert!(t.is_ok()); @@ -792,6 +804,8 @@ mod tests { #[test] fn test_vsock_thread_failures() { + let groups: Vec = vec![String::from("default")]; + let cid_map: Arc> = Arc::new(RwLock::new(HashMap::new())); let test_dir = tempdir().expect("Could not create a temp test directory."); @@ -800,6 +814,7 @@ mod tests { "/sys/not_allowed.vsock".to_string(), 3, CONN_TX_BUF_SIZE, + groups.clone(), cid_map.clone(), ); assert!(t.is_err()); @@ -810,7 +825,8 @@ mod tests { .display() .to_string(); let mut t = - VhostUserVsockThread::new(vsock_socket_path, 3, CONN_TX_BUF_SIZE, cid_map).unwrap(); + VhostUserVsockThread::new(vsock_socket_path, 3, CONN_TX_BUF_SIZE, groups, cid_map) + .unwrap(); assert!(VhostUserVsockThread::epoll_register(-1, -1, epoll::Events::EPOLLIN).is_err()); assert!(VhostUserVsockThread::epoll_modify(-1, -1, epoll::Events::EPOLLIN).is_err()); assert!(VhostUserVsockThread::epoll_unregister(-1, -1).is_err()); From eea36eefa5a40de982f4595137acae7ef24e836a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 04:36:17 +0000 Subject: [PATCH 118/189] build(deps): bump equivalent from 1.0.0 to 1.0.1 Bumps [equivalent](https://github.com/cuviper/equivalent) from 1.0.0 to 1.0.1. - [Commits](https://github.com/cuviper/equivalent/compare/v1.0.0...v1.0.1) --- updated-dependencies: - dependency-name: equivalent dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 453d803..9cd79c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -319,9 +319,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" From 87e51d55f036f23b6628dc5b8916771923e7672e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 04:36:07 +0000 Subject: [PATCH 119/189] build(deps): bump pest_meta from 2.7.0 to 2.7.1 Bumps [pest_meta](https://github.com/pest-parser/pest) from 2.7.0 to 2.7.1. - [Release notes](https://github.com/pest-parser/pest/releases) - [Commits](https://github.com/pest-parser/pest/compare/v2.7.0...v2.7.1) --- updated-dependencies: - dependency-name: pest_meta dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9cd79c1..1837cc1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -778,9 +778,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0" +checksum = "2674c66ebb4b4d9036012091b537aae5878970d6999f81a265034d85b136b341" dependencies = [ "once_cell", "pest", From ace68f6722026584325cd7843570b2aab88629eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Aug 2023 04:35:58 +0000 Subject: [PATCH 120/189] build(deps): bump pest_generator from 2.7.0 to 2.7.1 Bumps [pest_generator](https://github.com/pest-parser/pest) from 2.7.0 to 2.7.1. - [Release notes](https://github.com/pest-parser/pest/releases) - [Commits](https://github.com/pest-parser/pest/compare/v2.7.0...v2.7.1) --- updated-dependencies: - dependency-name: pest_generator dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1837cc1..ae47891 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -765,9 +765,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e8cba4ec22bada7fc55ffe51e2deb6a0e0db2d0b7ab0b103acc80d2510c190" +checksum = "99d490fe7e8556575ff6911e45567ab95e71617f43781e5c05490dc8d75c965c" dependencies = [ "pest", "pest_meta", From d8fdbb79cd4ba1a5f41ad6e153097c007d5fc7d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 04:26:19 +0000 Subject: [PATCH 121/189] build(deps): bump serde_json from 1.0.100 to 1.0.104 Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.100 to 1.0.104. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.100...v1.0.104) --- updated-dependencies: - dependency-name: serde_json dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae47891..bc3724f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -989,9 +989,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.100" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", From 89362cf3b5b77a1ff744de7e5e5fc2b5d8904b6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 04:26:05 +0000 Subject: [PATCH 122/189] build(deps): bump either from 1.8.1 to 1.9.0 Bumps [either](https://github.com/bluss/either) from 1.8.1 to 1.9.0. - [Commits](https://github.com/bluss/either/compare/1.8.1...1.9.0) --- updated-dependencies: - dependency-name: either dependency-type: indirect update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc3724f..7b7561b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -290,9 +290,9 @@ checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "env_logger" From 6754ca091f99632d431a3a917256fb06337c7147 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 04:25:56 +0000 Subject: [PATCH 123/189] build(deps): bump aho-corasick from 1.0.2 to 1.0.3 Bumps [aho-corasick](https://github.com/BurntSushi/aho-corasick) from 1.0.2 to 1.0.3. - [Commits](https://github.com/BurntSushi/aho-corasick/compare/1.0.2...1.0.3) --- updated-dependencies: - dependency-name: aho-corasick dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b7561b..049052d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" dependencies = [ "memchr", ] From 033a0ed6147ec96023d89cf15200634c96a8caa6 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 8 Aug 2023 15:11:54 +0530 Subject: [PATCH 124/189] Update cargo dependencies The main target of this update is vm-memory to a newer stable version, but lets update everything anyway. Signed-off-by: Viresh Kumar --- Cargo.lock | 177 ++++++++++++++++++++--------------------------------- 1 file changed, 68 insertions(+), 109 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 049052d..d9006c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,13 +85,13 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-trait" -version = "0.1.71" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.28", ] [[package]] @@ -157,9 +157,12 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" +dependencies = [ + "libc", +] [[package]] name = "cexpr" @@ -189,9 +192,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.19" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" dependencies = [ "clap_builder", "clap_derive", @@ -200,9 +203,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.19" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" dependencies = [ "anstream", "anstyle", @@ -219,7 +222,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.28", ] [[package]] @@ -336,9 +339,9 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" dependencies = [ "errno-dragonfly", "libc", @@ -357,12 +360,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "futures" @@ -421,7 +421,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.28", ] [[package]] @@ -533,32 +533,12 @@ dependencies = [ "hashbrown 0.14.0", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "intmap" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee87fd093563344074bacf24faa0bb0227fb6969fb223e922db798516de924d6" -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys", -] - [[package]] name = "is-terminal" version = "0.4.9" @@ -566,15 +546,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.3", + "rustix", "windows-sys", ] [[package]] name = "itoa" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "json5" @@ -646,15 +626,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.3.8" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "log" @@ -712,7 +686,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.28", ] [[package]] @@ -745,9 +719,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pest" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5" +checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" dependencies = [ "thiserror", "ucd-trie", @@ -755,9 +729,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef623c9bbfa0eedf5a0efba11a5ee83209c326653ca31ff019bec3a95bfff2b" +checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" dependencies = [ "pest", "pest_generator", @@ -765,22 +739,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d490fe7e8556575ff6911e45567ab95e71617f43781e5c05490dc8d75c965c" +checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.28", ] [[package]] name = "pest_meta" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674c66ebb4b4d9036012091b537aae5878970d6999f81a265034d85b136b341" +checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" dependencies = [ "once_cell", "pest", @@ -789,9 +763,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" +checksum = "2c516611246607d0c04186886dbb3a754368ef82c79e9827a802c6d836dd111c" [[package]] name = "pin-utils" @@ -832,9 +806,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.29" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -880,9 +854,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" dependencies = [ "aho-corasick", "memchr", @@ -892,9 +866,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.2" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" dependencies = [ "aho-corasick", "memchr", @@ -936,28 +910,14 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.37.23" +version = "0.38.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" -dependencies = [ - "bitflags 1.3.2", - "errno 0.3.1", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys", -] - -[[package]] -name = "rustix" -version = "0.38.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +checksum = "172891ebdceb05aa0005f533a6cbfca599ddd7d966f6f5d4d9b2e70478e70399" dependencies = [ "bitflags 2.3.3", - "errno 0.3.1", + "errno 0.3.2", "libc", - "linux-raw-sys 0.4.3", + "linux-raw-sys", "windows-sys", ] @@ -969,22 +929,22 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "serde" -version = "1.0.168" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d614f89548720367ded108b3c843be93f3a341e22d5674ca0dd5cd57f34926af" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.168" +version = "1.0.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fe589678c688e44177da4f27152ee2d190757271dc7f1d5b6b9f68d869d641" +checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.28", ] [[package]] @@ -1000,9 +960,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.22" +version = "0.9.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "452e67b9c20c37fa79df53201dc03839651086ed9bbe92b3ca585ca9fdaa7d85" +checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" dependencies = [ "indexmap", "itoa", @@ -1074,9 +1034,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.23" +version = "2.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" dependencies = [ "proc-macro2", "quote", @@ -1100,15 +1060,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.6.0" +version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" dependencies = [ - "autocfg", "cfg-if", "fastrand", "redox_syscall", - "rustix 0.37.23", + "rustix", "windows-sys", ] @@ -1138,7 +1097,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.28", ] [[package]] @@ -1158,9 +1117,9 @@ checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.19.12" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ "indexmap", "toml_datetime", @@ -1193,9 +1152,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unsafe-libyaml" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" [[package]] name = "utf8parse" @@ -1217,9 +1176,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vhost" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73832f4d8d636d63d9b145e8ef22a2c50b93f4d24eb7a99c9e6781b1b08549cf" +checksum = "61957aeb36daf0b00b87fff9c10dd28a161bd35ab157553d340d183b3d8756e6" dependencies = [ "bitflags 1.3.2", "libc", @@ -1375,9 +1334,9 @@ dependencies = [ [[package]] name = "vm-memory" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c7a0891cbac53618f5f6eec650ed1dc4f7e506bbe14877aff49d94b8408b0" +checksum = "3750e9b70da7f2ce2f7bf942c886d45f9bae064135c398f05635bf77e926a2ef" dependencies = [ "arc-swap", "bitflags 1.3.2", @@ -1513,9 +1472,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.7" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +checksum = "acaaa1190073b2b101e15083c38ee8ec891b5e05cbee516521e94ec008f61e64" dependencies = [ "memchr", ] From 4fa44ea06cd6ee9b64911a2432b881f862cc7d0e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 04:20:05 +0000 Subject: [PATCH 125/189] build(deps): bump serde_json from 1.0.104 to 1.0.105 Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.104 to 1.0.105. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.104...v1.0.105) --- updated-dependencies: - dependency-name: serde_json dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d9006c5..d826b51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -949,9 +949,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.104" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "itoa", "ryu", From a9770822f96b693bfb4014f7d9b8fe9091b30c75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 04:19:58 +0000 Subject: [PATCH 126/189] build(deps): bump serde from 1.0.183 to 1.0.184 Bumps [serde](https://github.com/serde-rs/serde) from 1.0.183 to 1.0.184. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.183...v1.0.184) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d826b51..18401f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -929,18 +929,18 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "serde" -version = "1.0.183" +version = "1.0.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" +checksum = "2c911f4b04d7385c9035407a4eff5903bf4fe270fa046fda448b69e797f4fff0" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.183" +version = "1.0.184" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" +checksum = "c1df27f5b29406ada06609b2e2f77fb34f6dbb104a457a671cc31dbed237e09e" dependencies = [ "proc-macro2", "quote", From 4718ef80ad0b5bf065468135ee53d4e2534d3593 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 04:19:50 +0000 Subject: [PATCH 127/189] build(deps): bump log from 0.4.19 to 0.4.20 Bumps [log](https://github.com/rust-lang/log) from 0.4.19 to 0.4.20. - [Release notes](https://github.com/rust-lang/log/releases) - [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/log/compare/0.4.19...0.4.20) --- updated-dependencies: - dependency-name: log dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 18401f4..d2b7069 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -632,9 +632,9 @@ checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" From 8da7657df03cdadcd16f75e22a7a2a1fc15f94a8 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Tue, 22 Aug 2023 10:18:57 +0200 Subject: [PATCH 128/189] Update vhost-user-backend package dependency vhost-user-backend v0.10.0 introduced an issue that affects all vhost-user backends. I easily reproduced the problem with vhost-device-vsock: just restart the guest kernel and the device no longer works. vhost-user-backend v0.10.1 includes the fix [1] for that issue. [1] https://github.com/rust-vmm/vhost/pull/180 Signed-off-by: Stefano Garzarella --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d2b7069..16cb81a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1290,9 +1290,9 @@ dependencies = [ [[package]] name = "vhost-user-backend" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ea9d5e8ec847cde4df1c04e586698a479706fd6beca37323f9d425b24b4c2f" +checksum = "ab069cdedaf18a0673766eb0a07a0f4ee3ed1b8e17fbfe4aafe5b988e2de1d01" dependencies = [ "libc", "log", From 05cfece5f8f6de4377b6da911e1cd363f4a6cfbc Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Wed, 23 Aug 2023 12:29:27 +0200 Subject: [PATCH 129/189] i2c: Don't take a mut ref for reqs Fixing a nightly clippy warning Signed-off-by: Bilal Elmoussaoui --- crates/i2c/src/i2c.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/i2c/src/i2c.rs b/crates/i2c/src/i2c.rs index ce55a30..e214b3b 100644 --- a/crates/i2c/src/i2c.rs +++ b/crates/i2c/src/i2c.rs @@ -183,7 +183,7 @@ impl SmbusMsg { /// /// These smbus related functions try to reverse what Linux does, only /// support basic modes (up to word transfer). - fn new(reqs: &mut [I2cReq]) -> Result { + fn new(reqs: &[I2cReq]) -> Result { let mut data = I2cSmbusData { block: [0; I2C_SMBUS_BLOCK_MAX + 2], }; From 38624410db3eff26be70a7399374c392b47e746b Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Wed, 23 Aug 2023 12:32:03 +0200 Subject: [PATCH 130/189] misc: Set workspace resolver to 2 cargo complains with the following otherwise: some crates are on edition 2021 which defaults to resolver = 2, but virtual workspaces default to resolver = 1 Signed-off-by: Bilal Elmoussaoui --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 633fa4d..514eca8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = [ "crates/gpio", From fad00d3e3cd50663703620bb1c8cde618fca14a6 Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Wed, 23 Aug 2023 12:34:55 +0200 Subject: [PATCH 131/189] vsock: Set edition to 2021 Similar to other crates Signed-off-by: Bilal Elmoussaoui --- crates/vsock/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index 2b37c56..46d4e16 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -7,7 +7,7 @@ repository = "https://github.com/rust-vmm/vhost-device" readme = "README.md" keywords = ["vhost", "vsock"] license = "Apache-2.0 OR BSD-3-Clause" -edition = "2018" +edition = "2021" [features] xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] From ee2abe6df06bae46db2d3ed4a4c2754055c23333 Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Wed, 23 Aug 2023 12:36:27 +0200 Subject: [PATCH 132/189] scsi: Bump num_enum Signed-off-by: Bilal Elmoussaoui --- Cargo.lock | 8 ++++---- crates/scsi/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 16cb81a..5b5bdca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -670,18 +670,18 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" dependencies = [ "proc-macro-crate", "proc-macro2", diff --git a/crates/scsi/Cargo.toml b/crates/scsi/Cargo.toml index ae3ff8a..3cba3c9 100644 --- a/crates/scsi/Cargo.toml +++ b/crates/scsi/Cargo.toml @@ -19,7 +19,7 @@ clap = { version = "4.3", features = ["derive"] } env_logger = "0.10" epoll = "4.3" log = "0.4" -num_enum = "0.6" +num_enum = "0.7" thiserror = "1.0" vhost = { version = "0.8", features = ["vhost-user-slave"] } vhost-user-backend = "0.10" From 2fa80555d242f5aaa1d188cf79f982263c8bfd51 Mon Sep 17 00:00:00 2001 From: Bilal Elmoussaoui Date: Wed, 23 Aug 2023 12:37:41 +0200 Subject: [PATCH 133/189] misc: Update dependencies lockfile Following up the update num_enum dependency Signed-off-by: Bilal Elmoussaoui --- Cargo.lock | 122 ++++++++++++++++++++++++++--------------------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b5bdca..b627a2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" +checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" dependencies = [ "memchr", ] @@ -63,9 +63,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" dependencies = [ "anstyle", "windows-sys", @@ -85,13 +85,13 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-trait" -version = "0.1.72" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -136,9 +136,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "block-buffer" @@ -157,9 +157,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "libc", ] @@ -192,9 +192,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.21" +version = "4.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" +checksum = "03aef18ddf7d879c15ce20f04826ef8418101c7e528014c3eeea13321047dca3" dependencies = [ "clap_builder", "clap_derive", @@ -203,9 +203,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.21" +version = "4.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" +checksum = "f8ce6fffb678c9b80a70b6b6de0aad31df727623a70fd9a842c30cd573e2fa98" dependencies = [ "anstream", "anstyle", @@ -222,7 +222,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -316,7 +316,7 @@ version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "libc", ] @@ -421,7 +421,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -686,7 +686,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -747,7 +747,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -763,9 +763,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c516611246607d0c04186886dbb3a754368ef82c79e9827a802c6d836dd111c" +checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" [[package]] name = "pin-utils" @@ -806,9 +806,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -910,11 +910,11 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.7" +version = "0.38.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172891ebdceb05aa0005f533a6cbfca599ddd7d966f6f5d4d9b2e70478e70399" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "errno 0.3.2", "libc", "linux-raw-sys", @@ -929,22 +929,22 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "serde" -version = "1.0.184" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c911f4b04d7385c9035407a4eff5903bf4fe270fa046fda448b69e797f4fff0" +checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.184" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1df27f5b29406ada06609b2e2f77fb34f6dbb104a457a671cc31dbed237e09e" +checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -990,9 +990,9 @@ checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] @@ -1034,9 +1034,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.28" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", @@ -1060,9 +1060,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.7.1" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", @@ -1082,22 +1082,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.44" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.44" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.29", ] [[package]] @@ -1415,9 +1415,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -1430,51 +1430,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.4" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acaaa1190073b2b101e15083c38ee8ec891b5e05cbee516521e94ec008f61e64" +checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97" dependencies = [ "memchr", ] From 9926e75c41906484478a122743a71befd01572a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Aug 2023 04:14:45 +0000 Subject: [PATCH 134/189] build(deps): bump winnow from 0.5.14 to 0.5.15 Bumps [winnow](https://github.com/winnow-rs/winnow) from 0.5.14 to 0.5.15. - [Changelog](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md) - [Commits](https://github.com/winnow-rs/winnow/compare/v0.5.14...v0.5.15) --- updated-dependencies: - dependency-name: winnow dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b627a2c..6663eff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1472,9 +1472,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d09770118a7eb1ccaf4a594a221334119a44a814fcb0d31c5b85e83e97227a97" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" dependencies = [ "memchr", ] From e7d0e80282f1e62c0cff3ae3be779f112e2c684b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Aug 2023 14:33:57 +0000 Subject: [PATCH 135/189] build(deps): bump regex-syntax from 0.7.4 to 0.7.5 Bumps [regex-syntax](https://github.com/rust-lang/regex) from 0.7.4 to 0.7.5. - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/commits) --- updated-dependencies: - dependency-name: regex-syntax dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6663eff..2d56cb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -877,9 +877,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "ron" From 9513ed841cb9301062888704343b77a7a1c330db Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Wed, 7 Jun 2023 18:50:02 +0200 Subject: [PATCH 136/189] scmi: Initial skeleton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for a SCMI vhost-user device. It implements the basic skeleton of the vhost-user daemon and of SCMI processing. It doesn’t provide any real functionality yet, adding it will be the subject of followup patches. Signed-off-by: Milan Zamazal --- Cargo.lock | 17 + Cargo.toml | 1 + crates/scmi/Cargo.toml | 26 + crates/scmi/LICENSE-APACHE | 1 + crates/scmi/LICENSE-BSD-3-Clause | 1 + crates/scmi/README.md | 64 +++ crates/scmi/src/main.rs | 98 ++++ crates/scmi/src/scmi.rs | 281 +++++++++++ crates/scmi/src/vhu_scmi.rs | 787 +++++++++++++++++++++++++++++++ 9 files changed, 1276 insertions(+) create mode 100644 crates/scmi/Cargo.toml create mode 120000 crates/scmi/LICENSE-APACHE create mode 120000 crates/scmi/LICENSE-BSD-3-Clause create mode 100644 crates/scmi/README.md create mode 100644 crates/scmi/src/main.rs create mode 100644 crates/scmi/src/scmi.rs create mode 100644 crates/scmi/src/vhu_scmi.rs diff --git a/Cargo.lock b/Cargo.lock index 2d56cb0..3155b5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1244,6 +1244,23 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "vhost-device-scmi" +version = "0.1.0" +dependencies = [ + "assert_matches", + "clap", + "env_logger", + "log", + "thiserror", + "vhost", + "vhost-user-backend", + "virtio-bindings", + "virtio-queue", + "vm-memory", + "vmm-sys-util", +] + [[package]] name = "vhost-device-scsi" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 514eca8..72e3adf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,6 @@ members = [ "crates/i2c", "crates/rng", "crates/scsi", + "crates/scmi", "crates/vsock", ] diff --git a/crates/scmi/Cargo.toml b/crates/scmi/Cargo.toml new file mode 100644 index 0000000..b5e0a42 --- /dev/null +++ b/crates/scmi/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "vhost-device-scmi" +version = "0.1.0" +authors = ["Milan Zamazal "] +description = "vhost-user SCMI backend device" +repository = "https://github.com/rust-vmm/vhost-device" +readme = "README.md" +keywords = ["scmi", "vhost", "virt", "backend"] +license = "Apache-2.0 OR BSD-3-Clause" +edition = "2021" + +[dependencies] +clap = { version = "4.3", features = ["derive"] } +env_logger = "0.10" +log = "0.4" +thiserror = "1.0" +vhost = { version = "0.8", features = ["vhost-user-slave"] } +vhost-user-backend = "0.10" +virtio-bindings = "0.2" +virtio-queue = "0.9" +vm-memory = "0.12" +vmm-sys-util = "0.11" + +[dev-dependencies] +assert_matches = "1.5" +virtio-queue = { version = "0.9", features = ["test-utils"] } diff --git a/crates/scmi/LICENSE-APACHE b/crates/scmi/LICENSE-APACHE new file mode 120000 index 0000000..1cd601d --- /dev/null +++ b/crates/scmi/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/crates/scmi/LICENSE-BSD-3-Clause b/crates/scmi/LICENSE-BSD-3-Clause new file mode 120000 index 0000000..a60f1af --- /dev/null +++ b/crates/scmi/LICENSE-BSD-3-Clause @@ -0,0 +1 @@ +../../LICENSE-BSD-3-Clause \ No newline at end of file diff --git a/crates/scmi/README.md b/crates/scmi/README.md new file mode 100644 index 0000000..b555e7b --- /dev/null +++ b/crates/scmi/README.md @@ -0,0 +1,64 @@ +# vhost-device-scmi + +This program is a vhost-user backend for a VirtIO SCMI device. +It provides SCMI access to various entities on the host; not +necessarily only those providing an SCMI interface themselves. + +It is tested with QEMU's `-device vhost-user-scmi-pci` but should work +with any virtual machine monitor (VMM) that supports vhost-user. See +the Examples section below. + +The currently supported SCMI protocols are: + +- base protocol + +The currently supported SCMI connections on the host are: + +- none + +## Synopsis + +**vhost-device-scmi** [*OPTIONS*] + +## Options + +.. program:: vhost-device-scmi + +.. option:: -h, --help + + Print help. + +.. option:: -s, --socket-path=PATH + + Location of the vhost-user Unix domain sockets. + +You can set `RUST_LOG` environment variable to `debug` to get maximum +messages on the standard error output. + +## Examples + +The daemon should be started first: + +:: + + host# vhost-device-scmi --socket-path=scmi.sock + +The QEMU invocation needs to create a chardev socket the device can +use to communicate as well as share the guests memory over a memfd: + +:: + + host# qemu-system \ + -chardev socket,path=scmi.sock,id=scmi \ + -device vhost-user-scmi-pci,chardev=vscmi,id=scmi \ + -machine YOUR-MACHINE-OPTIONS,memory-backend=mem \ + -m 4096 \ + -object memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on \ + ... + +## License + +This project is licensed under either of + +- [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0 +- [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause) diff --git a/crates/scmi/src/main.rs b/crates/scmi/src/main.rs new file mode 100644 index 0000000..02b8e20 --- /dev/null +++ b/crates/scmi/src/main.rs @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: Red Hat, Inc. +// SPDX-License-Identifier: Apache-2.0 +// Based on implementation of other devices here, Copyright by Linaro Ltd. + +mod scmi; +mod vhu_scmi; + +use std::process::exit; +use std::sync::{Arc, RwLock}; + +use clap::Parser; +use log::{debug, error, info, warn}; + +use vhost::vhost_user; +use vhost::vhost_user::Listener; +use vhost_user_backend::VhostUserDaemon; +use vhu_scmi::{VuScmiBackend, VuScmiError}; +use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; + +type Result = std::result::Result; + +#[derive(Parser)] +struct ScmiArgs { + // Location of vhost-user Unix domain socket. + #[clap(short, long, help = "vhost-user socket to use")] + socket_path: String, +} + +struct VuScmiConfig { + socket_path: String, +} + +impl TryFrom for VuScmiConfig { + type Error = VuScmiError; + + fn try_from(cmd_args: ScmiArgs) -> Result { + let socket_path = cmd_args.socket_path.trim().to_string(); + Ok(Self { socket_path }) + } +} + +fn start_backend(config: VuScmiConfig) -> Result<()> { + loop { + debug!("Starting backend"); + let backend = Arc::new(RwLock::new(VuScmiBackend::new().unwrap())); + let listener = Listener::new(config.socket_path.clone(), true).unwrap(); + let mut daemon = VhostUserDaemon::new( + "vhost-device-scmi".to_owned(), + backend.clone(), + GuestMemoryAtomic::new(GuestMemoryMmap::new()), + ) + .unwrap(); + + daemon.start(listener).unwrap(); + + match daemon.wait() { + Ok(()) => { + info!("Stopping cleanly"); + } + Err(vhost_user_backend::Error::HandleRequest(vhost_user::Error::PartialMessage)) => { + info!( + "vhost-user connection closed with partial message. + If the VM is shutting down, this is expected behavior; + otherwise, it might be a bug." + ); + } + Err(e) => { + warn!("Error running daemon: {:?}", e); + } + } + + // No matter the result, we need to shut down the worker thread. + backend.read().unwrap().exit_event.write(1).unwrap(); + debug!("Finishing backend"); + } +} + +fn main() { + env_logger::init(); + if let Err(error) = start_backend(VuScmiConfig::try_from(ScmiArgs::parse()).unwrap()) { + error!("{error}"); + exit(1); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_command_line() { + let path = "/foo/scmi.sock".to_owned(); + let command_line = format!("-s {path}"); + let args: ScmiArgs = Parser::parse_from(["", &command_line]); + let config: VuScmiConfig = args.try_into().unwrap(); + assert_eq!(config.socket_path, path); + } +} diff --git a/crates/scmi/src/scmi.rs b/crates/scmi/src/scmi.rs new file mode 100644 index 0000000..9429e0c --- /dev/null +++ b/crates/scmi/src/scmi.rs @@ -0,0 +1,281 @@ +// SPDX-FileCopyrightText: Red Hat, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use log::debug; + +pub type MessageHeader = u32; +// SCMI specification talks about Le32 parameter and return values. +// VirtIO SCMI specification talks about u8 SCMI values. +// Let's stick with SCMI specification for implementation simplicity. +#[derive(Clone, Debug, PartialEq)] +#[allow(dead_code)] +enum MessageValue { + Signed(i32), + Unsigned(u32), + String(String, usize), // string, expected characters +} +type MessageValues = Vec; + +#[derive(Debug, PartialEq)] +enum MessageType { + // 4-bit unsigned integer + Command, // 0 + Unsupported, // anything else +} +type MessageId = u8; +type ProtocolId = u8; +type NParameters = u8; + +#[derive(Clone, Copy)] +// Not all the codes are currently used but let's have a complete return status +// enumeration from the SCMI specification here. +#[allow(dead_code)] +enum ReturnStatus { + // 32-bit signed integer + Success = 0, + NotSupported = -1, + InvalidParameters = -2, + Denied = -3, + NotFound = -4, + OutOfRange = -5, + Busy = -6, + CommsError = -7, + GenericError = -8, + HardwareError = -9, + ProtocolError = -10, + // -11..-127: reserved + // <-127: vendor specific +} + +impl ReturnStatus { + const fn as_value(&self) -> MessageValue { + MessageValue::Signed(*self as i32) + } +} + +struct Response { + values: MessageValues, +} + +impl From for Response { + fn from(value: ReturnStatus) -> Self { + Self { + values: vec![value.as_value()], + } + } +} + +impl From<&MessageValues> for Response { + #[allow(dead_code)] + fn from(value: &MessageValues) -> Self { + let mut response_values = vec![ReturnStatus::Success.as_value()]; + response_values.extend_from_slice(value.as_slice()); + Self { + values: response_values, + } + } +} + +#[derive(Debug)] +pub struct ScmiResponse { + header: MessageHeader, + ret_bytes: Vec, +} + +impl ScmiResponse { + fn from(header: MessageHeader, response: Response) -> Self { + debug!("response arguments: {:?}", response.values); + let mut ret_bytes: Vec = vec![]; + ret_bytes.extend_from_slice(&header.to_le_bytes()); + for v in response.values { + let mut bytes = match v { + MessageValue::Signed(n) => n.to_le_bytes().to_vec(), + MessageValue::Unsigned(n) => n.to_le_bytes().to_vec(), + // Strings can be UTF-8 or ASCII and they must be + // null-terminated in either case. Let's put the + // null-terminator here rather than having to put it + // to all the strings anywhere. + MessageValue::String(s, size) => { + let mut v = s.as_bytes().to_vec(); + let v_len = v.len(); + // The string must be NULL terminated, at least one NULL must be present. + assert!( + v_len < size, + "String longer than specified: {v_len} >= {size}" + ); + v.resize(size, b'\0'); + v + } + }; + ret_bytes.append(&mut bytes) + } + debug!("ret bytes: {:?}", ret_bytes); + Self { header, ret_bytes } + } + + pub(crate) fn as_slice(&self) -> &[u8] { + self.ret_bytes.as_slice() + } + + pub(crate) fn len(&self) -> usize { + self.ret_bytes.len() + } + + pub(crate) fn communication_error(&self) -> Self { + Self::from(self.header, Response::from(ReturnStatus::CommsError)) + } +} + +pub struct ScmiHandler {} + +impl ScmiHandler { + pub const fn new() -> Self { + Self {} + } + + pub fn handle(&mut self, request: ScmiRequest) -> ScmiResponse { + let response = match request.message_type { + // TODO: Implement a mechanism to invoke the proper protocol & + // message handling on command requests. + MessageType::Command => Response::from(ReturnStatus::NotSupported), + MessageType::Unsupported => Response::from(ReturnStatus::NotSupported), + }; + ScmiResponse::from(request.header, response) + } + + pub fn number_of_parameters(&self, _request: &ScmiRequest) -> Option { + // TODO: Implement. + Some(0) + } + + pub fn store_parameters(&self, _request: &mut ScmiRequest, _buffer: &[u8]) { + // TODO: Implement (depends on knowledge of the number of parameters). + } +} + +#[allow(dead_code)] +pub struct ScmiRequest { + header: MessageHeader, // 32-bit unsigned integer, split below: + message_id: MessageId, // bits 7:0 + message_type: MessageType, // bits 9:8 + protocol_id: ProtocolId, // bits 17:10 + // token: u16, // bits 27:18 + // bits 31:28 are reserved, must be 0 + _parameters: Option, // set later based on the number of parameters +} + +impl ScmiRequest { + pub(crate) fn new(header: MessageHeader) -> Self { + let protocol_id: u8 = ((header >> 10) & 0xFF).try_into().unwrap(); + let message_id: u8 = (header & 0xFF).try_into().unwrap(); + // Token is an arbitrary info, the Linux SCMI driver uses it as a sequence number. + // No actual meaning for vhost except copying the unchanged header in the response + // as required by SCMI specification. We extract it here only for debugging purposes. + let token: u16 = ((header >> 18) & 0x3FF).try_into().unwrap(); + let message_type = match (header >> 8) & 0x3 { + 0 => MessageType::Command, + _ => MessageType::Unsupported, + }; + debug!( + "SCMI request: protocol id={}, message id={}, message_type={:?}, token={}", + protocol_id, message_id, message_type, token + ); + Self { + header, + message_id, + message_type, + protocol_id, + _parameters: None, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_response_from_status() { + let status = ReturnStatus::Busy; + let response = Response::from(status); + assert_eq!(response.values.len(), 1); + assert_eq!(response.values[0], MessageValue::Signed(status as i32)); + } + + #[test] + fn test_response_from_values() { + let status = ReturnStatus::Success; + let values = vec![ + MessageValue::Signed(-2), + MessageValue::Unsigned(8), + MessageValue::String("foo".to_owned(), 16), + ]; + let len = values.len() + 1; + let response = Response::from(&values); + assert_eq!(response.values.len(), len); + assert_eq!(response.values[0], MessageValue::Signed(status as i32)); + for i in 1..len { + assert_eq!(response.values[i], values[i - 1]); + } + } + + fn make_response(header: MessageHeader) -> ScmiResponse { + let values = vec![ + MessageValue::Signed(-2), + MessageValue::Unsigned(800_000_000), + MessageValue::String("foo".to_owned(), 16), + ]; + let response = Response::from(&values); + ScmiResponse::from(header, response) + } + + #[test] + fn test_response() { + let header: MessageHeader = 1_000_000; + let scmi_response = make_response(header); + assert_eq!(scmi_response.header, header); + let bytes: Vec = vec![ + 0x40, 0x42, 0x0F, 0x00, // header + 0x00, 0x00, 0x00, 0x00, // SUCCESS + 0xFE, 0xFF, 0xFF, 0xFF, // -2 + 0x00, 0x08, 0xAF, 0x2F, // 800 000 000 + 0x66, 0x6F, 0x6F, 0x00, // "foo" + NULLs + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + assert_eq!(scmi_response.ret_bytes, bytes); + assert_eq!(scmi_response.len(), bytes.len()); + assert_eq!(scmi_response.as_slice(), bytes.as_slice()); + } + + #[test] + fn test_communication_error_response() { + let header: MessageHeader = 1_000_000; + let scmi_response = make_response(header).communication_error(); + assert_eq!(scmi_response.header, header); + let bytes: Vec = vec![ + 0x40, 0x42, 0x0F, 0x00, // header + 0xF9, 0xFF, 0xFF, 0xFF, // ComsError + ]; + assert_eq!(scmi_response.ret_bytes, bytes); + } + + #[test] + fn test_request() { + let header: MessageHeader = 0x000304AB; + let request = ScmiRequest::new(header); + assert_eq!(request.header, header); + assert_eq!(request.message_id, 0xAB); + assert_eq!(request.message_type, MessageType::Command); + assert_eq!(request.protocol_id, 0xC1); + } + + #[test] + fn test_request_unsupported() { + let header: MessageHeader = 0x000102AB; + let request = ScmiRequest::new(header); + assert_eq!(request.header, header); + assert_eq!(request.message_id, 0xAB); + assert_eq!(request.message_type, MessageType::Unsupported); + assert_eq!(request.protocol_id, 0x40); + } +} diff --git a/crates/scmi/src/vhu_scmi.rs b/crates/scmi/src/vhu_scmi.rs new file mode 100644 index 0000000..0128666 --- /dev/null +++ b/crates/scmi/src/vhu_scmi.rs @@ -0,0 +1,787 @@ +// SPDX-FileCopyrightText: Red Hat, Inc. +// SPDX-License-Identifier: Apache-2.0 +// Based on https://github.com/rust-vmm/vhost-device, Copyright by Linaro Ltd. + +use log::{debug, error, warn}; +use std::io; +use std::io::Result as IoResult; +use std::mem::size_of; +use thiserror::Error as ThisError; +use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures}; +use vhost_user_backend::{VhostUserBackendMut, VringRwLock, VringT}; +use virtio_bindings::bindings::virtio_config::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1}; +use virtio_bindings::bindings::virtio_ring::{ + VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC, +}; +use virtio_queue::{DescriptorChain, QueueOwnedT}; +use vm_memory::{ + Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap, +}; +use vmm_sys_util::epoll::EventSet; +use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; + +use crate::scmi::{MessageHeader, ScmiHandler, ScmiRequest}; + +// QUEUE_SIZE must be apparently at least 1024 for MMIO. +// There is probably a maximum size per descriptor defined in the kernel. +const QUEUE_SIZE: usize = 1024; +const NUM_QUEUES: usize = 2; + +const COMMAND_QUEUE: u16 = 0; +const EVENT_QUEUE: u16 = 1; + +const VIRTIO_SCMI_F_P2A_CHANNELS: u16 = 0; + +#[derive(Debug, PartialEq, Eq, ThisError)] +pub enum VuScmiError { + #[error("Descriptor not found")] + DescriptorNotFound, + #[error("Descriptor read failed")] + DescriptorReadFailed, + #[error("Descriptor write failed")] + DescriptorWriteFailed, + #[error("Failed to create new EventFd")] + EventFdFailed, + #[error("Failed to handle event, didn't match EPOLLIN")] + HandleEventNotEpollIn, + #[error("Failed to handle unknown event")] + HandleEventUnknownEvent, + #[error("Isufficient descriptor size, required: {0}, found: {1}")] + InsufficientDescriptorSize(usize, usize), + #[error("Failed to send notification")] + SendNotificationFailed, + #[error("Invalid descriptor count {0}")] + UnexpectedDescriptorCount(usize), + #[error("Invalid descriptor size, expected: {0}, found: {1}")] + UnexpectedDescriptorSize(usize, usize), + #[error("Invalid descriptor size, expected at least: {0}, found: {1}")] + UnexpectedMinimumDescriptorSize(usize, usize), + #[error("Received unexpected readable descriptor at index {0}")] + UnexpectedReadableDescriptor(usize), + #[error("Received unexpected write only descriptor at index {0}")] + UnexpectedWriteOnlyDescriptor(usize), +} + +impl From for io::Error { + fn from(e: VuScmiError) -> Self { + Self::new(io::ErrorKind::Other, e) + } +} + +type Result = std::result::Result; + +type ScmiDescriptorChain = DescriptorChain>>; + +pub struct VuScmiBackend { + event_idx: bool, + pub exit_event: EventFd, + mem: Option>, + // Event vring and descriptors serve for asynchronous responses and notifications. + // They are obtained from the driver and we store them here for later use. + // (We currently don't implement asynchronous responses or notifications but we support + // the event queue because the Linux VIRTIO SCMI driver seems to be unhappy if it is not + // present. And it doesn't harm to be ready for possible event queue use in future.) + event_vring: Option, + event_descriptors: Vec>>, + // The abstraction of request handling, with all the needed information stored inside. + scmi_handler: ScmiHandler, +} + +impl VuScmiBackend { + pub fn new() -> Result { + Ok(Self { + event_idx: false, + exit_event: EventFd::new(EFD_NONBLOCK).map_err(|_| VuScmiError::EventFdFailed)?, + mem: None, + event_vring: None, + event_descriptors: vec![], + scmi_handler: ScmiHandler::new(), + }) + } + + pub fn process_requests( + &mut self, + requests: Vec, + vring: &VringRwLock, + ) -> Result { + if requests.is_empty() { + return Ok(true); + } + + for desc_chain in requests { + let descriptors: Vec<_> = desc_chain.clone().collect(); + if descriptors.len() != 2 { + return Err(VuScmiError::UnexpectedDescriptorCount(descriptors.len())); + } + + let desc_request = descriptors[0]; + if desc_request.is_write_only() { + return Err(VuScmiError::UnexpectedWriteOnlyDescriptor(0)); + } + + let read_desc_len: usize = desc_request.len() as usize; + let header_size = size_of::(); + if read_desc_len < header_size { + return Err(VuScmiError::UnexpectedMinimumDescriptorSize( + header_size, + read_desc_len, + )); + } + + let header = desc_chain + .memory() + .read_obj::(desc_request.addr()) + .map_err(|_| VuScmiError::DescriptorReadFailed)?; + let mut scmi_request = ScmiRequest::new(header); + let n_parameters = self.scmi_handler.number_of_parameters(&scmi_request); + debug!("SCMI request with n parameters: {:?}", n_parameters); + let value_size = 4; + if let Some(expected_parameters) = n_parameters { + if expected_parameters > 0 { + let param_bytes = (expected_parameters as usize) * value_size; + let total_size = value_size + param_bytes; + if read_desc_len != total_size { + return Err(VuScmiError::UnexpectedDescriptorSize( + total_size, + read_desc_len, + )); + } + let mut buffer: Vec = vec![0; header_size + param_bytes]; + desc_chain + .memory() + .read_slice(&mut buffer, desc_request.addr()) + .map_err(|_| VuScmiError::DescriptorReadFailed)?; + self.scmi_handler + .store_parameters(&mut scmi_request, &buffer[header_size..]); + } else if read_desc_len != value_size { + return Err(VuScmiError::UnexpectedDescriptorSize( + value_size, + read_desc_len, + )); + } + } + + debug!("Calling SCMI request handler"); + let mut response = self.scmi_handler.handle(scmi_request); + debug!("SCMI response: {:?}", response); + + let desc_response = descriptors[1]; + if !desc_response.is_write_only() { + return Err(VuScmiError::UnexpectedReadableDescriptor(1)); + } + + let write_desc_len: usize = desc_response.len() as usize; + if response.len() > write_desc_len { + error!( + "Response of length {} cannot fit into the descriptor size {}", + response.len(), + write_desc_len + ); + response = response.communication_error(); + if response.len() > write_desc_len { + return Err(VuScmiError::InsufficientDescriptorSize( + response.len(), + write_desc_len, + )); + } + } + desc_chain + .memory() + .write_slice(response.as_slice(), desc_response.addr()) + .map_err(|_| VuScmiError::DescriptorWriteFailed)?; + + if vring + .add_used(desc_chain.head_index(), response.len() as u32) + .is_err() + { + error!("Couldn't return used descriptors to the ring"); + } + } + Ok(true) + } + + fn process_command_queue(&mut self, vring: &VringRwLock) -> Result<()> { + debug!("Processing command queue"); + let requests: Vec<_> = vring + .get_mut() + .get_queue_mut() + .iter(self.mem.as_ref().unwrap().memory()) + .map_err(|_| VuScmiError::DescriptorNotFound)? + .collect(); + + debug!("Requests to process: {}", requests.len()); + match self.process_requests(requests, vring) { + Ok(_) => { + // Send notification once all the requests are processed + debug!("Sending processed request notification"); + vring + .signal_used_queue() + .map_err(|_| VuScmiError::SendNotificationFailed)?; + debug!("Notification sent"); + } + Err(err) => { + warn!("Failed SCMI request: {}", err); + return Err(err); + } + } + debug!("Processing command queue finished"); + Ok(()) + } + + fn start_event_queue(&mut self, vring: &VringRwLock) { + if self.event_vring.is_none() { + self.event_vring = Some(vring.clone()); + } + } + + pub fn process_event_requests( + &mut self, + requests: Vec, + _vring: &VringRwLock, + ) -> Result { + // The requests here are notifications from the guest about adding + // fresh buffers for the used ring. The Linux driver allocates 256 + // buffers for the event queue initially (arriving here in several + // batches) and then adds a free buffer after each message delivered + // through the event queue. + for desc_chain in requests { + let descriptors: Vec<_> = desc_chain.clone().collect(); + debug!( + "SCMI event request with n descriptors: {}", + descriptors.len() + ); + if descriptors.len() != 1 { + return Err(VuScmiError::UnexpectedDescriptorCount(descriptors.len())); + } + + let desc = descriptors[0]; + if !desc.is_write_only() { + return Err(VuScmiError::UnexpectedReadableDescriptor(0)); + } + debug!("SCMI event request avail descriptor length: {}", desc.len()); + + self.event_descriptors.push(desc_chain); + } + Ok(true) + } + + fn process_event_queue(&mut self, vring: &VringRwLock) -> Result<()> { + debug!("Processing event queue"); + + let requests: Vec<_> = vring + .get_mut() + .get_queue_mut() + .iter(self.mem.as_ref().unwrap().memory()) + .map_err(|_| VuScmiError::DescriptorNotFound)? + .collect(); + debug!("Requests to process: {}", requests.len()); + match self.process_event_requests(requests, vring) { + Ok(_) => { + // Send notification once all the requests are processed + debug!("Sending processed request notification"); + vring + .signal_used_queue() + .map_err(|_| VuScmiError::SendNotificationFailed)?; + debug!("Notification sent"); + } + Err(err) => { + warn!("Failed SCMI request: {}", err); + return Err(err); + } + } + self.start_event_queue(vring); + debug!("Processing event queue finished"); + Ok(()) + } +} + +/// VhostUserBackend trait methods +impl VhostUserBackendMut for VuScmiBackend { + fn num_queues(&self) -> usize { + debug!("Num queues called"); + NUM_QUEUES + } + + fn max_queue_size(&self) -> usize { + debug!("Max queue size called"); + QUEUE_SIZE + } + + fn features(&self) -> u64 { + debug!("Features called"); + 1 << VIRTIO_F_VERSION_1 + | 1 << VIRTIO_F_NOTIFY_ON_EMPTY + | 1 << VIRTIO_RING_F_INDIRECT_DESC + | 1 << VIRTIO_RING_F_EVENT_IDX + | 1 << VIRTIO_SCMI_F_P2A_CHANNELS + | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits() + } + + fn protocol_features(&self) -> VhostUserProtocolFeatures { + debug!("Protocol features called"); + VhostUserProtocolFeatures::MQ + } + + fn set_event_idx(&mut self, enabled: bool) { + self.event_idx = enabled; + debug!("Event idx set to: {}", enabled); + } + + fn update_memory(&mut self, mem: GuestMemoryAtomic) -> IoResult<()> { + debug!("Update memory called"); + self.mem = Some(mem); + Ok(()) + } + + fn handle_event( + &mut self, + device_event: u16, + evset: EventSet, + vrings: &[VringRwLock], + _thread_id: usize, + ) -> IoResult { + debug!("Handle event called"); + if evset != EventSet::IN { + warn!("Non-input event"); + return Err(VuScmiError::HandleEventNotEpollIn.into()); + } + + match device_event { + COMMAND_QUEUE => { + let vring = &vrings[COMMAND_QUEUE as usize]; + + if self.event_idx { + // vm-virtio's Queue implementation only checks avail_index + // once, so to properly support EVENT_IDX we need to keep + // calling process_queue() until it stops finding new + // requests on the queue. + loop { + vring.disable_notification().unwrap(); + self.process_command_queue(vring)?; + if !vring.enable_notification().unwrap() { + break; + } + } + } else { + // Without EVENT_IDX, a single call is enough. + self.process_command_queue(vring)?; + } + } + + EVENT_QUEUE => { + let vring = &vrings[EVENT_QUEUE as usize]; + + if self.event_idx { + // vm-virtio's Queue implementation only checks avail_index + // once, so to properly support EVENT_IDX we need to keep + // calling process_queue() until it stops finding new + // requests on the queue. + loop { + vring.disable_notification().unwrap(); + self.process_event_queue(vring)?; + if !vring.enable_notification().unwrap() { + break; + } + } + } else { + // Without EVENT_IDX, a single call is enough. + self.process_event_queue(vring)?; + } + } + + _ => { + warn!("unhandled device_event: {}", device_event); + return Err(VuScmiError::HandleEventUnknownEvent.into()); + } + } + debug!("Handle event finished"); + Ok(false) + } + + fn exit_event(&self, _thread_index: usize) -> Option { + debug!("Exit event called"); + self.exit_event.try_clone().ok() + } +} + +#[cfg(test)] +mod tests { + use virtio_bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; + use virtio_queue::{mock::MockSplitQueue, Descriptor, Queue}; + use vm_memory::{Address, GuestAddress, GuestMemoryAtomic, GuestMemoryMmap}; + + use super::*; + + fn build_event_desc_chain() -> ScmiDescriptorChain { + let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); + let vq = MockSplitQueue::new(mem, 16); + let next_addr = vq.desc_table().total_size() + 0x100; + + // Descriptor for the SCMI event + let desc_response = Descriptor::new(next_addr, 0x100, VRING_DESC_F_WRITE as u16, 0); + vq.desc_table().store(0, desc_response).unwrap(); + + // Put the descriptor index 0 in the first available ring position. + mem.write_obj(0u16, vq.avail_addr().unchecked_add(4)) + .unwrap(); + // Set `avail_idx` to 1. + mem.write_obj(1u16, vq.avail_addr().unchecked_add(2)) + .unwrap(); + // Create descriptor chain from pre-filled memory. + vq.create_queue::() + .unwrap() + .iter(GuestMemoryAtomic::new(mem.clone()).memory()) + .unwrap() + .next() + .unwrap() + } + + // Build just empty descriptors + struct DescParameters { + addr: Option, + flags: u16, + len: u32, + } + fn build_dummy_desc_chain(parameters: Vec<&DescParameters>) -> ScmiDescriptorChain { + let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); + let vq = MockSplitQueue::new(mem, 16); + + for (i, p) in parameters.iter().enumerate() { + let mut f: u16 = if i == parameters.len() - 1 { + 0 + } else { + VRING_DESC_F_NEXT as u16 + }; + f |= p.flags; + let offset = match p.addr { + Some(addr) => addr, + _ => 0x100, + }; + let desc = Descriptor::new(offset, p.len, f, (i + 1) as u16); + vq.desc_table().store(i as u16, desc).unwrap(); + } + + // Put the descriptor index 0 in the first available ring position. + mem.write_obj(0u16, vq.avail_addr().unchecked_add(4)) + .unwrap(); + // Set `avail_idx` to 1. + mem.write_obj(1u16, vq.avail_addr().unchecked_add(2)) + .unwrap(); + // Create descriptor chain from pre-filled memory + vq.create_queue::() + .unwrap() + .iter(GuestMemoryAtomic::new(mem.clone()).memory()) + .unwrap() + .next() + .unwrap() + } + + #[test] + fn test_process_requests_failure() { + let mut backend = VuScmiBackend::new().unwrap(); + let mem = GuestMemoryAtomic::new( + GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), + ); + let vring = VringRwLock::new(mem, 0x1000).unwrap(); + let default = DescParameters { + addr: None, + flags: 0, + len: 0, + }; + + // Have only one descriptor, expected two. + let parameters = vec![&default]; + let desc_chain = build_dummy_desc_chain(parameters); + assert_eq!( + backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err(), + VuScmiError::UnexpectedDescriptorCount(1) + ); + + // Have three descriptors, expected two. + let parameters = vec![&default, &default, &default]; + let desc_chain = build_dummy_desc_chain(parameters); + assert_eq!( + backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err(), + VuScmiError::UnexpectedDescriptorCount(3) + ); + + // Write only descriptors. + let p = DescParameters { + addr: None, + flags: VRING_DESC_F_WRITE as u16, + len: 0, + }; + let parameters = vec![&p, &p]; + let desc_chain = build_dummy_desc_chain(parameters); + assert_eq!( + backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err(), + VuScmiError::UnexpectedWriteOnlyDescriptor(0) + ); + + // Invalid request address. + let parameters = vec![ + &DescParameters { + addr: Some(0x10000), + flags: 0, + len: 4, + }, + &DescParameters { + addr: None, + flags: VRING_DESC_F_WRITE as u16, + len: 4, + }, + ]; + let desc_chain = build_dummy_desc_chain(parameters); + assert_eq!( + backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err(), + VuScmiError::DescriptorReadFailed + ); + + // Invalid request length (very small). + let parameters = vec![ + &DescParameters { + addr: None, + flags: 0, + len: 2, + }, + &DescParameters { + addr: None, + flags: VRING_DESC_F_WRITE as u16, + len: 4, + }, + ]; + let desc_chain = build_dummy_desc_chain(parameters); + assert_eq!( + backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err(), + VuScmiError::UnexpectedMinimumDescriptorSize(4, 2) + ); + + // Read only descriptors. + let p = DescParameters { + addr: None, + flags: 0, + len: 4, + }; + let parameters = vec![&p, &p]; + let desc_chain = build_dummy_desc_chain(parameters); + assert_eq!( + backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err(), + VuScmiError::UnexpectedReadableDescriptor(1) + ); + + // Invalid response address. + let parameters = vec![ + &DescParameters { + addr: None, + flags: 0, + len: 4, + }, + &DescParameters { + addr: Some(0x10000), + flags: VRING_DESC_F_WRITE as u16, + len: 8, + }, + ]; + let desc_chain = build_dummy_desc_chain(parameters); + assert_eq!( + backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err(), + VuScmiError::DescriptorWriteFailed + ); + + // Invalid response length. + let parameters = vec![ + &DescParameters { + addr: None, + flags: 0, + len: 4, + }, + &DescParameters { + addr: None, + flags: VRING_DESC_F_WRITE as u16, + len: 6, + }, + ]; + let desc_chain = build_dummy_desc_chain(parameters); + assert_eq!( + backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err(), + VuScmiError::InsufficientDescriptorSize(8, 6) + ); + } + + #[test] + fn test_event_requests() { + let mut backend = VuScmiBackend::new().unwrap(); + let mem = GuestMemoryAtomic::new( + GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), + ); + let vring = VringRwLock::new(mem, 0x1000).unwrap(); + + // Descriptor chain size zero, shouldn't fail and should be no-op + backend + .process_event_requests(Vec::::new(), &vring) + .unwrap(); + assert_eq!(backend.event_descriptors.len(), 0); + + // Valid event descriptors, should get stored + let desc_chains = vec![build_event_desc_chain(), build_event_desc_chain()]; + backend.process_event_requests(desc_chains, &vring).unwrap(); + assert_eq!(backend.event_descriptors.len(), 2); + + // Some more event descriptors + let desc_chains = vec![ + build_event_desc_chain(), + build_event_desc_chain(), + build_event_desc_chain(), + ]; + backend.process_event_requests(desc_chains, &vring).unwrap(); + assert_eq!(backend.event_descriptors.len(), 5); + } + + #[test] + fn test_event_requests_failure() { + let mut backend = VuScmiBackend::new().unwrap(); + let mem = GuestMemoryAtomic::new( + GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), + ); + let vring = VringRwLock::new(mem, 0x1000).unwrap(); + + // Invalid number of desc chains + let p = DescParameters { + addr: None, + flags: 0, + len: 0, + }; + let desc_chain = build_dummy_desc_chain(vec![&p, &p]); + assert_eq!( + backend + .process_event_requests(vec![desc_chain], &vring) + .unwrap_err(), + VuScmiError::UnexpectedDescriptorCount(2) + ); + + // Read only descriptor + let p = DescParameters { + addr: None, + flags: 0, + len: 0, + }; + let desc_chain = build_dummy_desc_chain(vec![&p]); + assert_eq!( + backend + .process_event_requests(vec![desc_chain], &vring) + .unwrap_err(), + VuScmiError::UnexpectedReadableDescriptor(0) + ); + } + + #[test] + fn test_backend() { + let mut backend = VuScmiBackend::new().unwrap(); + + assert_eq!(backend.num_queues(), NUM_QUEUES); + assert_eq!(backend.max_queue_size(), QUEUE_SIZE); + assert_eq!(backend.features(), 0x171000001); + assert_eq!(backend.protocol_features(), VhostUserProtocolFeatures::MQ); + + assert_eq!(backend.queues_per_thread(), vec![0xffff_ffff]); + + backend.set_event_idx(true); + assert!(backend.event_idx); + + assert!(backend.exit_event(0).is_some()); + + let mem = GuestMemoryAtomic::new( + GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), + ); + backend.update_memory(mem.clone()).unwrap(); + + let vring_request = VringRwLock::new(mem.clone(), 0x1000).unwrap(); + vring_request.set_queue_info(0x100, 0x200, 0x300).unwrap(); + vring_request.set_queue_ready(true); + + let vring_event = VringRwLock::new(mem, 0x1000).unwrap(); + vring_event.set_queue_info(0x100, 0x200, 0x300).unwrap(); + vring_event.set_queue_ready(true); + + assert_eq!( + backend + .handle_event( + 0, + EventSet::OUT, + &[vring_request.clone(), vring_event.clone()], + 0, + ) + .unwrap_err() + .kind(), + io::ErrorKind::Other + ); + + assert_eq!( + backend + .handle_event( + 2, + EventSet::IN, + &[vring_request.clone(), vring_event.clone()], + 0, + ) + .unwrap_err() + .kind(), + io::ErrorKind::Other + ); + + // Hit the loop part + backend.set_event_idx(true); + backend + .handle_event( + 0, + EventSet::IN, + &[vring_request.clone(), vring_event.clone()], + 0, + ) + .unwrap(); + + // Hit the non-loop part + backend.set_event_idx(false); + backend + .handle_event( + 0, + EventSet::IN, + &[vring_request.clone(), vring_event.clone()], + 0, + ) + .unwrap(); + + // Hit the loop part + backend.set_event_idx(true); + backend + .handle_event( + 1, + EventSet::IN, + &[vring_request.clone(), vring_event.clone()], + 0, + ) + .unwrap(); + + // Hit the non-loop part + backend.set_event_idx(false); + backend + .handle_event(1, EventSet::IN, &[vring_request, vring_event], 0) + .unwrap(); + } +} From 5b0b8c3753203cababee4970045057096a2946d5 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Sat, 10 Jun 2023 18:02:53 +0200 Subject: [PATCH 137/189] scmi: Implement SCMI base protocol Implementation of the mandatory parts of the SCMI base protocol. This allows the daemon to communicate with the guest SCMI VIRTIO device, although not yet providing any useful functionality. Signed-off-by: Milan Zamazal --- Cargo.lock | 10 + crates/scmi/Cargo.toml | 1 + crates/scmi/src/scmi.rs | 476 +++++++++++++++++++++++++++++++++--- crates/scmi/src/vhu_scmi.rs | 123 ++++++++++ 4 files changed, 578 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3155b5f..7258ce1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -550,6 +550,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -1251,6 +1260,7 @@ dependencies = [ "assert_matches", "clap", "env_logger", + "itertools", "log", "thiserror", "vhost", diff --git a/crates/scmi/Cargo.toml b/crates/scmi/Cargo.toml index b5e0a42..be2929f 100644 --- a/crates/scmi/Cargo.toml +++ b/crates/scmi/Cargo.toml @@ -12,6 +12,7 @@ edition = "2021" [dependencies] clap = { version = "4.3", features = ["derive"] } env_logger = "0.10" +itertools = "0.10" log = "0.4" thiserror = "1.0" vhost = { version = "0.8", features = ["vhost-user-slave"] } diff --git a/crates/scmi/src/scmi.rs b/crates/scmi/src/scmi.rs index 9429e0c..d8fd96b 100644 --- a/crates/scmi/src/scmi.rs +++ b/crates/scmi/src/scmi.rs @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 +use std::collections::HashMap; + +use itertools::Itertools; use log::debug; pub type MessageHeader = u32; @@ -8,7 +11,6 @@ pub type MessageHeader = u32; // VirtIO SCMI specification talks about u8 SCMI values. // Let's stick with SCMI specification for implementation simplicity. #[derive(Clone, Debug, PartialEq)] -#[allow(dead_code)] enum MessageValue { Signed(i32), Unsigned(u32), @@ -65,8 +67,15 @@ impl From for Response { } } +impl From for Response { + fn from(value: MessageValue) -> Self { + Self { + values: vec![ReturnStatus::Success.as_value(), value], + } + } +} + impl From<&MessageValues> for Response { - #[allow(dead_code)] fn from(value: &MessageValues) -> Self { let mut response_values = vec![ReturnStatus::Success.as_value()]; response_values.extend_from_slice(value.as_slice()); @@ -126,34 +135,6 @@ impl ScmiResponse { } } -pub struct ScmiHandler {} - -impl ScmiHandler { - pub const fn new() -> Self { - Self {} - } - - pub fn handle(&mut self, request: ScmiRequest) -> ScmiResponse { - let response = match request.message_type { - // TODO: Implement a mechanism to invoke the proper protocol & - // message handling on command requests. - MessageType::Command => Response::from(ReturnStatus::NotSupported), - MessageType::Unsupported => Response::from(ReturnStatus::NotSupported), - }; - ScmiResponse::from(request.header, response) - } - - pub fn number_of_parameters(&self, _request: &ScmiRequest) -> Option { - // TODO: Implement. - Some(0) - } - - pub fn store_parameters(&self, _request: &mut ScmiRequest, _buffer: &[u8]) { - // TODO: Implement (depends on knowledge of the number of parameters). - } -} - -#[allow(dead_code)] pub struct ScmiRequest { header: MessageHeader, // 32-bit unsigned integer, split below: message_id: MessageId, // bits 7:0 @@ -161,7 +142,7 @@ pub struct ScmiRequest { protocol_id: ProtocolId, // bits 17:10 // token: u16, // bits 27:18 // bits 31:28 are reserved, must be 0 - _parameters: Option, // set later based on the number of parameters + parameters: Option, // set later based on the number of parameters } impl ScmiRequest { @@ -185,7 +166,256 @@ impl ScmiRequest { message_id, message_type, protocol_id, - _parameters: None, + parameters: None, + } + } + + fn get_unsigned(&self, parameter: usize) -> u32 { + match self.parameters.as_ref().expect("Missing parameters")[parameter] { + MessageValue::Unsigned(value) => value, + _ => panic!("Wrong parameter"), + } + } +} + +const BASE_PROTOCOL_ID: ProtocolId = 0x10; +const BASE_VERSION: MessageId = 0x0; +const BASE_PROTOCOL_ATTRIBUTES: MessageId = 0x1; +const BASE_MESSAGE_ATTRIBUTES: MessageId = 0x2; +const BASE_DISCOVER_VENDOR: MessageId = 0x3; +const BASE_DISCOVER_IMPLEMENTATION_VERSION: MessageId = 0x5; +const BASE_DISCOVER_LIST_PROTOCOLS: MessageId = 0x6; + +enum ParameterType { + _SignedInt32, + UnsignedInt32, +} +type ParameterSpecification = Vec; + +type HandlerFunction = fn(&ScmiHandler, &ScmiRequest) -> Response; +struct HandlerInfo { + name: String, + parameters: ParameterSpecification, + function: HandlerFunction, +} + +// HandlerMap layout is suboptimal but let's prefer simplicity for now. +struct HandlerMap(HashMap<(ProtocolId, MessageId), HandlerInfo>); + +impl HandlerMap { + fn new() -> Self { + let mut map = Self(HashMap::new()); + map.make_base_handlers(); + map + } + + fn keys(&self) -> std::collections::hash_map::Keys<(u8, u8), HandlerInfo> { + self.0.keys() + } + + fn get(&self, protocol_id: ProtocolId, message_id: MessageId) -> Option<&HandlerInfo> { + self.0.get(&(protocol_id, message_id)) + } + + fn bind( + &mut self, + protocol_id: ProtocolId, + message_id: MessageId, + name: &str, + parameters: ParameterSpecification, + function: HandlerFunction, + ) { + assert!( + self.get(protocol_id, message_id).is_none(), + "Multiple handlers defined for SCMI message {}/{}", + protocol_id, + message_id + ); + self.0.insert( + (protocol_id, message_id), + HandlerInfo { + name: name.to_string(), + parameters, + function, + }, + ); + } + + fn make_base_handlers(&mut self) { + self.bind( + BASE_PROTOCOL_ID, + BASE_VERSION, + "base/version", + vec![], + |_, _| -> Response { + // 32-bit unsigned integer + // major: upper 16 bits + // minor: lower 16 bits + Response::from(MessageValue::Unsigned(0x20000)) + }, + ); + self.bind( + BASE_PROTOCOL_ID, + BASE_PROTOCOL_ATTRIBUTES, + "base/protocol_attributes", + vec![], + |handler, _| -> Response { + // The base protocol doesn't count. + Response::from(MessageValue::Unsigned(handler.number_of_protocols() - 1)) + }, + ); + self.bind( + BASE_PROTOCOL_ID, + BASE_MESSAGE_ATTRIBUTES, + "base/message_attributes", + vec![ParameterType::UnsignedInt32], + ScmiHandler::message_attributes, + ); + self.bind( + BASE_PROTOCOL_ID, + BASE_DISCOVER_VENDOR, + "base/discover_vendor", + vec![], + |_, _| -> Response { Response::from(MessageValue::String("rust-vmm".to_string(), 16)) }, + ); + self.bind( + BASE_PROTOCOL_ID, + BASE_DISCOVER_IMPLEMENTATION_VERSION, + "base/discover_implementation_version", + vec![], + |_, _| -> Response { Response::from(MessageValue::Unsigned(0)) }, + ); + self.bind( + BASE_PROTOCOL_ID, + BASE_DISCOVER_LIST_PROTOCOLS, + "base/discover_list_protocols", + vec![ParameterType::UnsignedInt32], + ScmiHandler::discover_list_protocols, + ); + } +} + +pub struct ScmiHandler { + handlers: HandlerMap, +} + +impl ScmiHandler { + pub fn new() -> Self { + Self { + handlers: HandlerMap::new(), + } + } + + fn request_handler(&self, request: &ScmiRequest) -> Option<&HandlerInfo> { + self.handlers.get(request.protocol_id, request.message_id) + } + + pub fn handle(&mut self, request: ScmiRequest) -> ScmiResponse { + let response = match request.message_type { + MessageType::Command => match self.request_handler(&request) { + Some(info) => { + debug!( + "Calling handler for {}({:?})", + info.name, + request.parameters.as_ref().unwrap_or(&vec![]) + ); + (info.function)(self, &request) + } + _ => Response::from(ReturnStatus::NotSupported), + }, + MessageType::Unsupported => Response::from(ReturnStatus::NotSupported), + }; + ScmiResponse::from(request.header, response) + } + + pub fn number_of_parameters(&self, request: &ScmiRequest) -> Option { + self.request_handler(request).map(|info| { + info.parameters + .len() + .try_into() + .expect("Invalid parameter specification") + }) + } + + pub fn store_parameters(&self, request: &mut ScmiRequest, buffer: &[u8]) { + let handler = &self + .request_handler(request) + .expect("Attempt to process an unsupported SCMI message"); + let n_parameters = handler.parameters.len(); + debug!( + "SCMI request {}/{} parameters length: {}, buffer length: {}", + request.message_id, + request.protocol_id, + n_parameters, + buffer.len() + ); + let value_size = 4; + assert!( + buffer.len() == n_parameters * value_size, + "Unexpected parameters buffer size: buffer={} parameters={}", + buffer.len(), + n_parameters + ); + let mut values: MessageValues = Vec::with_capacity(n_parameters); + for n in 0..n_parameters { + let slice: [u8; 4] = buffer[4 * n..4 * (n + 1)] + .try_into() + .expect("Insufficient data for parameters"); + let v = match handler.parameters[n] { + ParameterType::_SignedInt32 => MessageValue::Signed(i32::from_le_bytes(slice)), + ParameterType::UnsignedInt32 => MessageValue::Unsigned(u32::from_le_bytes(slice)), + }; + debug!("SCMI parameter {}: {:?}", n, v); + values.push(v); + } + request.parameters = Some(values); + } + + fn number_of_protocols(&self) -> u32 { + let n: usize = self.handlers.keys().unique_by(|k| k.0).count(); + n.try_into() + .expect("Impossibly large number of SCMI protocols") + } + + fn discover_list_protocols(&self, request: &ScmiRequest) -> Response { + // Base protocol is skipped + let skip: usize = request + .get_unsigned(0) + .try_into() + .expect("Extremely many protocols"); + let protocols: Vec = self + .handlers + .keys() + .filter(|(protocol_id, _)| *protocol_id != BASE_PROTOCOL_ID) + .map(|(protocol_id, _)| *protocol_id) + .unique() + .sorted() + .skip(skip) + .collect(); + let n_protocols = protocols.len(); + debug!("Number of listed protocols after {}: {}", skip, n_protocols); + let mut values: Vec = vec![MessageValue::Unsigned(n_protocols as u32)]; + if n_protocols > 0 { + let mut compressed: Vec = vec![0; 1 + (n_protocols - 1) / 4]; + for i in 0..n_protocols { + debug!("Adding protocol: {}", protocols[i]); + compressed[i % 4] |= u32::from(protocols[i]) << ((i % 4) * 8); + } + for item in compressed { + values.push(MessageValue::Unsigned(item)); + } + } + Response::from(&values) + } + + fn message_attributes(&self, request: &ScmiRequest) -> Response { + let message_id: Result = request.get_unsigned(0).try_into(); + if message_id.is_err() { + return Response::from(ReturnStatus::InvalidParameters); + } + match self.handlers.get(request.protocol_id, message_id.unwrap()) { + Some(_) => Response::from(MessageValue::Unsigned(0)), + None => Response::from(ReturnStatus::NotFound), } } } @@ -202,6 +432,16 @@ mod tests { assert_eq!(response.values[0], MessageValue::Signed(status as i32)); } + #[test] + fn test_response_from_value() { + let value = MessageValue::Unsigned(28); + let status = ReturnStatus::Success; + let response = Response::from(value.clone()); + assert_eq!(response.values.len(), 2); + assert_eq!(response.values[0], MessageValue::Signed(status as i32)); + assert_eq!(response.values[1], value); + } + #[test] fn test_response_from_values() { let status = ReturnStatus::Success; @@ -278,4 +518,176 @@ mod tests { assert_eq!(request.message_type, MessageType::Unsupported); assert_eq!(request.protocol_id, 0x40); } + + fn make_request(protocol_id: ProtocolId, message_id: MessageId) -> ScmiRequest { + let header: MessageHeader = u32::from(message_id) | (u32::from(protocol_id) << 10); + ScmiRequest::new(header) + } + + fn store_parameters( + handler: &ScmiHandler, + request: &mut ScmiRequest, + parameters: &[MessageValue], + ) { + let mut bytes: Vec = vec![]; + for p in parameters { + let value = match p { + MessageValue::Unsigned(n) => u32::to_le_bytes(*n), + MessageValue::Signed(n) => i32::to_le_bytes(*n), + _ => panic!("Unsupported parameter type"), + }; + bytes.append(&mut value.to_vec()); + } + handler.store_parameters(request, bytes.as_slice()); + } + + #[test] + fn test_handler_parameters() { + let handler = ScmiHandler::new(); + let mut request = make_request(BASE_PROTOCOL_ID, BASE_DISCOVER_LIST_PROTOCOLS); + assert_eq!(handler.number_of_parameters(&request), Some(1)); + + let value: u32 = 1234567890; + let parameters = [MessageValue::Unsigned(value)]; + store_parameters(&handler, &mut request, ¶meters); + assert_eq!(request.parameters, Some(parameters.to_vec())); + assert_eq!(request.get_unsigned(0), value); + } + + fn test_message( + protocol_id: ProtocolId, + message_id: MessageId, + parameters: Vec, + result_code: ReturnStatus, + result_values: Vec, + ) { + let mut handler = ScmiHandler::new(); + let mut request = make_request(protocol_id, message_id); + let header = request.header; + if !parameters.is_empty() { + let parameter_slice = parameters.as_slice(); + store_parameters(&handler, &mut request, parameter_slice); + } + let response = handler.handle(request); + assert_eq!(response.header, header); + let mut bytes: Vec = vec![]; + bytes.append(&mut header.to_le_bytes().to_vec()); + bytes.append(&mut (result_code as i32).to_le_bytes().to_vec()); + for value in result_values { + let mut value_vec = match value { + MessageValue::Unsigned(n) => n.to_le_bytes().to_vec(), + MessageValue::Signed(n) => n.to_le_bytes().to_vec(), + MessageValue::String(s, size) => { + let mut v = s.as_bytes().to_vec(); + let v_len = v.len(); + assert!( + v_len < size, + "String longer than specified: {v_len} >= {size}" + ); + v.resize(size, b'\0'); + v + } + }; + bytes.append(&mut value_vec); + } + assert_eq!(response.ret_bytes, bytes.as_slice()); + } + + #[test] + fn test_base_version() { + let values = vec![MessageValue::Unsigned(0x20000)]; + test_message( + BASE_PROTOCOL_ID, + BASE_VERSION, + vec![], + ReturnStatus::Success, + values, + ); + } + + #[test] + fn test_base_protocol_attributes() { + let result = vec![MessageValue::Unsigned(0)]; + test_message( + BASE_PROTOCOL_ID, + BASE_PROTOCOL_ATTRIBUTES, + vec![], + ReturnStatus::Success, + result, + ); + } + + #[test] + fn test_base_protocol_message_attributes_supported() { + let parameters = vec![MessageValue::Unsigned(u32::from(BASE_DISCOVER_VENDOR))]; + let result = vec![MessageValue::Unsigned(0)]; + test_message( + BASE_PROTOCOL_ID, + BASE_MESSAGE_ATTRIBUTES, + parameters, + ReturnStatus::Success, + result, + ); + } + + #[test] + fn test_base_protocol_message_attributes_unsupported() { + let parameters = vec![MessageValue::Unsigned(0x4)]; + test_message( + BASE_PROTOCOL_ID, + BASE_MESSAGE_ATTRIBUTES, + parameters, + ReturnStatus::NotFound, + vec![], + ); + } + + #[test] + fn test_base_protocol_message_attributes_invalid() { + let parameters = vec![MessageValue::Unsigned(0x100)]; + test_message( + BASE_PROTOCOL_ID, + BASE_MESSAGE_ATTRIBUTES, + parameters, + ReturnStatus::InvalidParameters, + vec![], + ); + } + + #[test] + fn test_base_discover_vendor() { + let result = vec![MessageValue::String(String::from("rust-vmm"), 16)]; + test_message( + BASE_PROTOCOL_ID, + BASE_DISCOVER_VENDOR, + vec![], + ReturnStatus::Success, + result, + ); + } + + #[test] + fn test_base_discover_implementation_version() { + let values = vec![MessageValue::Unsigned(0)]; + test_message( + BASE_PROTOCOL_ID, + BASE_DISCOVER_IMPLEMENTATION_VERSION, + vec![], + ReturnStatus::Success, + values, + ); + } + + #[test] + fn test_base_discover_list_protocols() { + let parameters = vec![MessageValue::Unsigned(0)]; + let result = vec![MessageValue::Unsigned(0)]; + test_message( + BASE_PROTOCOL_ID, + BASE_DISCOVER_LIST_PROTOCOLS, + parameters, + ReturnStatus::Success, + result, + ); + } } diff --git a/crates/scmi/src/vhu_scmi.rs b/crates/scmi/src/vhu_scmi.rs index 0128666..e3f37b5 100644 --- a/crates/scmi/src/vhu_scmi.rs +++ b/crates/scmi/src/vhu_scmi.rs @@ -412,6 +412,54 @@ mod tests { use super::*; + fn scmi_header(message_id: u8, protocol_id: u8) -> u32 { + u32::from(message_id) | u32::from(protocol_id) << 10 + } + + fn build_cmd_desc_chain( + protocol_id: u8, + message_id: u8, + parameters: Vec, + ) -> ScmiDescriptorChain { + let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); + let vq = MockSplitQueue::new(mem, 16); + let mut next_addr = vq.desc_table().total_size() + 0x100; + let mut index = 0; + let request_size: u32 = (4 + parameters.len() * 4) as u32; + + // Descriptor for the SCMI request + let desc_request = + Descriptor::new(next_addr, request_size, VRING_DESC_F_NEXT as u16, index + 1); + let mut bytes: Vec = vec![]; + bytes.append(&mut scmi_header(message_id, protocol_id).to_le_bytes().to_vec()); + for p in parameters { + bytes.append(&mut p.to_le_bytes().to_vec()); + } + mem.write_slice(bytes.as_slice(), desc_request.addr()) + .unwrap(); + vq.desc_table().store(index, desc_request).unwrap(); + next_addr += u64::from(desc_request.len()); + index += 1; + + // Descriptor for the SCMI response + let desc_response = Descriptor::new(next_addr, 0x100, VRING_DESC_F_WRITE as u16, 0); + vq.desc_table().store(index, desc_response).unwrap(); + + // Put the descriptor index 0 in the first available ring position. + mem.write_obj(0u16, vq.avail_addr().unchecked_add(4)) + .unwrap(); + // Set `avail_idx` to 1. + mem.write_obj(1u16, vq.avail_addr().unchecked_add(2)) + .unwrap(); + // Create descriptor chain from pre-filled memory. + vq.create_queue::() + .unwrap() + .iter(GuestMemoryAtomic::new(mem.clone()).memory()) + .unwrap() + .next() + .unwrap() + } + fn build_event_desc_chain() -> ScmiDescriptorChain { let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); let vq = MockSplitQueue::new(mem, 16); @@ -476,6 +524,63 @@ mod tests { .unwrap() } + fn validate_desc_chains( + desc_chains: &[ScmiDescriptorChain], + chain_index: usize, + protocol_id: u8, + message_id: u8, + status: i32, + data: Vec, + ) { + let desc_chain = &desc_chains[chain_index]; + let descriptors: Vec<_> = desc_chain.clone().collect(); + let mut response = vec![0; descriptors[1].len() as usize]; + + desc_chain + .memory() + .read(&mut response, descriptors[1].addr()) + .unwrap(); + + let mut result: Vec = scmi_header(message_id, protocol_id).to_le_bytes().to_vec(); + result.append(&mut status.to_le_bytes().to_vec()); + for d in &data { + result.append(&mut d.to_le_bytes().to_vec()); + } + assert_eq!(response[0..result.len()], result); + } + + #[test] + fn test_process_requests() { + let mut backend = VuScmiBackend::new().unwrap(); + let mem = GuestMemoryAtomic::new( + GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), + ); + let vring = VringRwLock::new(mem, 0x1000).unwrap(); + + // Descriptor chain size zero, shouldn't fail + backend + .process_requests(Vec::::new(), &vring) + .unwrap(); + + // Valid single SCMI request: base protocol version + let desc_chains = vec![build_cmd_desc_chain(0x10, 0x0, vec![])]; + backend + .process_requests(desc_chains.clone(), &vring) + .unwrap(); + validate_desc_chains(&desc_chains, 0, 0x10, 0x0, 0, vec![0x20000]); + + // Valid multi SCMI request: base protocol version + implementation version + let desc_chains = vec![ + build_cmd_desc_chain(0x10, 0x0, vec![]), + build_cmd_desc_chain(0x10, 0x5, vec![]), + ]; + backend + .process_requests(desc_chains.clone(), &vring) + .unwrap(); + validate_desc_chains(&desc_chains, 0, 0x10, 0x0, 0, vec![0x20000]); + validate_desc_chains(&desc_chains, 1, 0x10, 0x5, 0, vec![0]); + } + #[test] fn test_process_requests_failure() { let mut backend = VuScmiBackend::new().unwrap(); @@ -566,6 +671,24 @@ mod tests { VuScmiError::UnexpectedMinimumDescriptorSize(4, 2) ); + // Invalid request length (too small). + let desc_chain = build_cmd_desc_chain(0x10, 0x2, vec![]); + assert_eq!( + backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err(), + VuScmiError::UnexpectedDescriptorSize(8, 4) + ); + + // Invalid request length (too large). + let desc_chain = build_cmd_desc_chain(0x10, 0x0, vec![0]); + assert_eq!( + backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err(), + VuScmiError::UnexpectedDescriptorSize(4, 8) + ); + // Read only descriptors. let p = DescParameters { addr: None, From bc7faf7685725f3597893b6e0488e349aa7b274f Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Thu, 29 Jun 2023 18:16:50 +0200 Subject: [PATCH 138/189] scmi: Add sensor management protocol This patch implements the necessary parts of the SCMI sensor management protocol, required either by the SCMI standard or by Linux VIRTIO SCMI drivers to function correctly. A part of this implementation is a fake sensor device, which is useful for both unit testing here and a testing with a real guest OS. Signed-off-by: Milan Zamazal --- crates/scmi/README.md | 18 +- crates/scmi/src/devices.rs | 119 +++++++ crates/scmi/src/main.rs | 1 + crates/scmi/src/scmi.rs | 619 +++++++++++++++++++++++++++++++++++-- 4 files changed, 731 insertions(+), 26 deletions(-) create mode 100644 crates/scmi/src/devices.rs diff --git a/crates/scmi/README.md b/crates/scmi/README.md index b555e7b..49f1b70 100644 --- a/crates/scmi/README.md +++ b/crates/scmi/README.md @@ -8,14 +8,6 @@ It is tested with QEMU's `-device vhost-user-scmi-pci` but should work with any virtual machine monitor (VMM) that supports vhost-user. See the Examples section below. -The currently supported SCMI protocols are: - -- base protocol - -The currently supported SCMI connections on the host are: - -- none - ## Synopsis **vhost-device-scmi** [*OPTIONS*] @@ -56,6 +48,16 @@ use to communicate as well as share the guests memory over a memfd: -object memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on \ ... +## Supported SCMI protocols + +The currently supported SCMI protocols are: + +- base +- sensor management + +Basically only the mandatory and necessary parts of the protocols are +implemented. + ## License This project is licensed under either of diff --git a/crates/scmi/src/devices.rs b/crates/scmi/src/devices.rs new file mode 100644 index 0000000..174aa39 --- /dev/null +++ b/crates/scmi/src/devices.rs @@ -0,0 +1,119 @@ +// SPDX-FileCopyrightText: Red Hat, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use log::debug; + +use crate::scmi::{ + DeviceResult, MessageId, MessageValue, MessageValues, ProtocolId, ScmiDevice, ScmiDeviceError, + MAX_SIMPLE_STRING_LENGTH, SENSOR_AXIS_DESCRIPTION_GET, SENSOR_CONFIG_GET, SENSOR_CONFIG_SET, + SENSOR_CONTINUOUS_UPDATE_NOTIFY, SENSOR_DESCRIPTION_GET, SENSOR_PROTOCOL_ID, + SENSOR_READING_GET, SENSOR_UNIT_METERS_PER_SECOND_SQUARED, +}; + +pub struct FakeSensor { + enabled: bool, + value: u8, + name: String, +} + +impl FakeSensor { + const NUMBER_OF_AXES: u32 = 3; + + pub const fn new(name: String) -> Self { + Self { + enabled: false, + value: 0, + name, + } + } +} + +impl ScmiDevice for FakeSensor { + fn protocol(&self) -> ProtocolId { + SENSOR_PROTOCOL_ID + } + + fn handle(&mut self, message_id: MessageId, parameters: &[MessageValue]) -> DeviceResult { + match message_id { + SENSOR_DESCRIPTION_GET => { + // Continuous update required by Linux SCMI IIO driver + let low = 1 << 30; + let high = Self::NUMBER_OF_AXES << 16 | 1 << 8; + let name = self.name.clone(); + let values: MessageValues = vec![ + // attributes low + MessageValue::Unsigned(low), + // attributes high + MessageValue::Unsigned(high), + // name, up to 16 bytes with final NULL (non-extended version) + MessageValue::String(name, MAX_SIMPLE_STRING_LENGTH), + ]; + Ok(values) + } + SENSOR_AXIS_DESCRIPTION_GET => { + let axis_desc_index = parameters[0].get_unsigned(); + if axis_desc_index >= Self::NUMBER_OF_AXES { + return Result::Err(ScmiDeviceError::InvalidParameters); + } + let mut values = vec![MessageValue::Unsigned( + Self::NUMBER_OF_AXES - axis_desc_index, + )]; + for i in axis_desc_index..Self::NUMBER_OF_AXES { + values.push(MessageValue::Unsigned(i)); // axis id + values.push(MessageValue::Unsigned(0)); // attributes low + + // The sensor type is "Meters per second squared", since this is the + // only, together with "Radians per second", what Google Linux IIO + // supports (accelerometers and gyroscopes only). + values.push(MessageValue::Unsigned( + SENSOR_UNIT_METERS_PER_SECOND_SQUARED, + )); // attributes high + + // Name in the recommended format, 16 bytes: + let axis = match i { + 0 => 'X', + 1 => 'Y', + 2 => 'Z', + _ => 'N', // shouldn't be reached currently + }; + values.push(MessageValue::String(format!("acc_{axis}").to_string(), 16)); + } + Ok(values) + } + SENSOR_CONFIG_GET => { + let config = u32::from(self.enabled); + Ok(vec![MessageValue::Unsigned(config)]) + } + SENSOR_CONFIG_SET => { + let config = parameters[0].get_unsigned(); + if config & 0xFFFFFFFE != 0 { + return Result::Err(ScmiDeviceError::UnsupportedRequest); + } + self.enabled = config != 0; + debug!("Sensor enabled: {}", self.enabled); + Ok(vec![]) + } + SENSOR_CONTINUOUS_UPDATE_NOTIFY => { + // Linux VIRTIO SCMI insists on this. + // We can accept it and ignore it, the sensor will be still working. + Ok(vec![]) + } + SENSOR_READING_GET => { + if !self.enabled { + return Result::Err(ScmiDeviceError::NotEnabled); + } + let value = self.value; + self.value = self.value.overflowing_add(1).0; + let mut result = vec![]; + for i in 0..3 { + result.push(MessageValue::Unsigned(u32::from(value) + 100 * i)); + result.push(MessageValue::Unsigned(0)); + result.push(MessageValue::Unsigned(0)); + result.push(MessageValue::Unsigned(0)); + } + Ok(result) + } + _ => Result::Err(ScmiDeviceError::UnsupportedRequest), + } + } +} diff --git a/crates/scmi/src/main.rs b/crates/scmi/src/main.rs index 02b8e20..2cf8906 100644 --- a/crates/scmi/src/main.rs +++ b/crates/scmi/src/main.rs @@ -4,6 +4,7 @@ mod scmi; mod vhu_scmi; +mod devices; use std::process::exit; use std::sync::{Arc, RwLock}; diff --git a/crates/scmi/src/scmi.rs b/crates/scmi/src/scmi.rs index d8fd96b..e168654 100644 --- a/crates/scmi/src/scmi.rs +++ b/crates/scmi/src/scmi.rs @@ -1,22 +1,40 @@ // SPDX-FileCopyrightText: Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::collections::HashMap; +use std::{ + cmp::min, + collections::HashMap, + sync::{Arc, Mutex}, +}; use itertools::Itertools; -use log::debug; +use log::{debug, error, info}; +use thiserror::Error as ThisError; pub type MessageHeader = u32; + +pub const MAX_SIMPLE_STRING_LENGTH: usize = 16; // incl. NULL terminator + // SCMI specification talks about Le32 parameter and return values. // VirtIO SCMI specification talks about u8 SCMI values. // Let's stick with SCMI specification for implementation simplicity. -#[derive(Clone, Debug, PartialEq)] -enum MessageValue { +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum MessageValue { Signed(i32), Unsigned(u32), String(String, usize), // string, expected characters } -type MessageValues = Vec; + +impl MessageValue { + pub(crate) fn get_unsigned(&self) -> u32 { + match self { + Self::Unsigned(value) => *value, + _ => panic!("Wrong parameter"), + } + } +} + +pub type MessageValues = Vec; #[derive(Debug, PartialEq)] enum MessageType { @@ -24,8 +42,8 @@ enum MessageType { Command, // 0 Unsupported, // anything else } -type MessageId = u8; -type ProtocolId = u8; +pub type MessageId = u8; +pub type ProtocolId = u8; type NParameters = u8; #[derive(Clone, Copy)] @@ -171,10 +189,11 @@ impl ScmiRequest { } fn get_unsigned(&self, parameter: usize) -> u32 { - match self.parameters.as_ref().expect("Missing parameters")[parameter] { - MessageValue::Unsigned(value) => value, - _ => panic!("Wrong parameter"), - } + self.parameters.as_ref().expect("Missing parameters")[parameter].get_unsigned() + } + + fn get_usize(&self, parameter: usize) -> usize { + self.get_unsigned(parameter) as usize } } @@ -186,6 +205,19 @@ const BASE_DISCOVER_VENDOR: MessageId = 0x3; const BASE_DISCOVER_IMPLEMENTATION_VERSION: MessageId = 0x5; const BASE_DISCOVER_LIST_PROTOCOLS: MessageId = 0x6; +pub const SENSOR_PROTOCOL_ID: ProtocolId = 0x15; +const SENSOR_VERSION: MessageId = 0x0; +const SENSOR_ATTRIBUTES: MessageId = 0x1; +const SENSOR_MESSAGE_ATTRIBUTES: MessageId = 0x2; +pub const SENSOR_DESCRIPTION_GET: MessageId = 0x3; +pub const SENSOR_READING_GET: MessageId = 0x6; +pub const SENSOR_AXIS_DESCRIPTION_GET: MessageId = 0x7; +pub const SENSOR_CONFIG_GET: MessageId = 0x9; +pub const SENSOR_CONFIG_SET: MessageId = 0xA; +pub const SENSOR_CONTINUOUS_UPDATE_NOTIFY: MessageId = 0xB; + +pub const SENSOR_UNIT_METERS_PER_SECOND_SQUARED: u32 = 89; + enum ParameterType { _SignedInt32, UnsignedInt32, @@ -206,6 +238,7 @@ impl HandlerMap { fn new() -> Self { let mut map = Self(HashMap::new()); map.make_base_handlers(); + map.make_sensor_handlers(); map } @@ -293,16 +326,227 @@ impl HandlerMap { ScmiHandler::discover_list_protocols, ); } + + fn make_sensor_handlers(&mut self) { + self.bind( + SENSOR_PROTOCOL_ID, + SENSOR_VERSION, + "sensor/version", + vec![], + |_, _| -> Response { + // 32-bit unsigned integer + // major: upper 16 bits + // minor: lower 16 bits + Response::from(MessageValue::Unsigned(0x30000)) + }, + ); + + self.bind( + SENSOR_PROTOCOL_ID, + SENSOR_ATTRIBUTES, + "sensor/attributes", + vec![], + |handler: &ScmiHandler, _| -> Response { + let n_sensors = u32::from(handler.devices.number_of_devices(SENSOR_PROTOCOL_ID)); + let values: MessageValues = vec![ + MessageValue::Unsigned(n_sensors), // # of sensors, no async commands + MessageValue::Unsigned(0), // lower shared memory address -- not supported + MessageValue::Unsigned(0), // higer shared memory address -- not supported + MessageValue::Unsigned(0), // length of shared memory -- not supported + ]; + Response::from(&values) + }, + ); + + self.bind( + SENSOR_PROTOCOL_ID, + SENSOR_MESSAGE_ATTRIBUTES, + "sensor/message_attributes", + vec![ParameterType::UnsignedInt32], + ScmiHandler::message_attributes, + ); + + self.bind( + SENSOR_PROTOCOL_ID, + SENSOR_DESCRIPTION_GET, + "sensor/description_get", + vec![ParameterType::UnsignedInt32], + |handler: &ScmiHandler, request: &ScmiRequest| -> Response { + let first_index = request.get_usize(0); + let n_sensors = handler.devices.number_of_devices(SENSOR_PROTOCOL_ID) as usize; + if first_index >= n_sensors { + return Response::from(ReturnStatus::InvalidParameters); + } + // Let's use something reasonable to fit into the available VIRTIO buffers: + let max_sensors_to_return = 256; + let sensors_to_return = min(n_sensors - first_index, max_sensors_to_return); + let last_non_returned_sensor = first_index + sensors_to_return; + let remaining_sensors = if n_sensors > last_non_returned_sensor { + n_sensors - last_non_returned_sensor + } else { + 0 + }; + let mut values = vec![MessageValue::Unsigned( + sensors_to_return as u32 | (remaining_sensors as u32) << 16, + )]; + for index in first_index..last_non_returned_sensor { + values.push(MessageValue::Unsigned(index as u32)); + let result = handler.handle_device( + index, + SENSOR_PROTOCOL_ID, + SENSOR_DESCRIPTION_GET, + &[], + ); + if result.is_err() { + return handler.device_response(result, index); + } + let mut sensor_values = result.unwrap(); + values.append(&mut sensor_values); + } + Response::from(&values) + }, + ); + + self.bind( + SENSOR_PROTOCOL_ID, + SENSOR_READING_GET, + "sensor/reading_get", + vec![ParameterType::UnsignedInt32, ParameterType::UnsignedInt32], + |handler: &ScmiHandler, request: &ScmiRequest| -> Response { + // Check flags + if request.get_unsigned(1) != 0 { + // Asynchronous reporting not supported + return Response::from(ReturnStatus::NotSupported); + } + handler.handle_device_response(request, &[]) + }, + ); + + self.bind( + SENSOR_PROTOCOL_ID, + SENSOR_AXIS_DESCRIPTION_GET, + "sensor/axis_description_get", + vec![ParameterType::UnsignedInt32, ParameterType::UnsignedInt32], + |handler: &ScmiHandler, request: &ScmiRequest| -> Response { + handler.handle_device_response(request, &[1]) + }, + ); + + self.bind( + SENSOR_PROTOCOL_ID, + SENSOR_CONFIG_GET, + "sensor/config_get", + vec![ParameterType::UnsignedInt32], + |handler: &ScmiHandler, request: &ScmiRequest| -> Response { + handler.handle_device_response(request, &[]) + }, + ); + + self.bind( + SENSOR_PROTOCOL_ID, + SENSOR_CONFIG_SET, + "sensor/config_set", + vec![ParameterType::UnsignedInt32, ParameterType::UnsignedInt32], + |handler: &ScmiHandler, request: &ScmiRequest| -> Response { + handler.handle_device_response(request, &[1]) + }, + ); + + // Linux VIRTIO SCMI seems to insist on presence of this: + self.bind( + SENSOR_PROTOCOL_ID, + SENSOR_CONTINUOUS_UPDATE_NOTIFY, + "sensor/continuous_update_notify", + vec![ParameterType::UnsignedInt32, ParameterType::UnsignedInt32], + |handler: &ScmiHandler, request: &ScmiRequest| -> Response { + handler.handle_device_response(request, &[1]) + }, + ); + } } +#[derive(Debug, PartialEq, Eq, ThisError)] +pub enum ScmiDeviceError { + #[error("Invalid parameters")] + InvalidParameters, + #[error("No such device")] + NoSuchDevice, + #[error("Device not enabled")] + NotEnabled, + #[error("Unsupported request")] + UnsupportedRequest, +} + +pub trait ScmiDevice: Send { + fn protocol(&self) -> ProtocolId; + fn handle( + &mut self, + message_id: MessageId, + parameters: &[MessageValue], + ) -> Result; +} + +type DeviceList = Vec>; + +struct DeviceMap(Arc>>); + +impl DeviceMap { + fn new() -> Self { + Self(Arc::new(Mutex::new(HashMap::new()))) + } + + // This is the maximum number of the remaining sensors + // SENSOR_DESCRIPTION_GET supports -- the upper 16 bits of the response. + const MAX_NUMBER_OF_PROTOCOL_DEVICES: usize = 0xFFFF; + + fn insert(&mut self, device: Box) { + let mut device_map = self.0.lock().unwrap(); + let devices = device_map.entry(device.protocol()).or_default(); + if devices.len() >= Self::MAX_NUMBER_OF_PROTOCOL_DEVICES { + panic!( + "Too many devices defined for protocol {}", + device.protocol() + ); + } + devices.push(device); + } + + fn number_of_devices(&self, protocol_id: ProtocolId) -> u16 { + match self.0.lock().unwrap().get(&protocol_id) { + Some(devices) => devices.len() as u16, + None => 0, + } + } + + fn handle( + &self, + device_index: usize, + protocol_id: ProtocolId, + message_id: MessageId, + parameters: &[MessageValue], + ) -> Result { + match self.0.lock().unwrap().get_mut(&protocol_id) { + Some(devices) => match devices.get_mut(device_index) { + Some(device) => device.handle(message_id, parameters), + None => Result::Err(ScmiDeviceError::NoSuchDevice), + }, + None => Result::Err(ScmiDeviceError::NoSuchDevice), + } + } +} + +pub type DeviceResult = Result; + pub struct ScmiHandler { handlers: HandlerMap, + devices: DeviceMap, } impl ScmiHandler { pub fn new() -> Self { Self { handlers: HandlerMap::new(), + devices: DeviceMap::new(), } } @@ -377,6 +621,56 @@ impl ScmiHandler { .expect("Impossibly large number of SCMI protocols") } + pub fn register_device(&mut self, device: Box) { + self.devices.insert(device); + } + + fn handle_device( + &self, + device_index: usize, + protocol_id: ProtocolId, + message_id: MessageId, + parameters: &[MessageValue], + ) -> DeviceResult { + self.devices + .handle(device_index, protocol_id, message_id, parameters) + } + + fn device_response(&self, result: DeviceResult, device_index: usize) -> Response { + match result { + Ok(values) => Response::from(&values), + Err(error) => match error { + ScmiDeviceError::NoSuchDevice + | ScmiDeviceError::NotEnabled + | ScmiDeviceError::InvalidParameters => { + info!("Invalid device access: {}, {}", device_index, error); + Response::from(ReturnStatus::InvalidParameters) + } + ScmiDeviceError::UnsupportedRequest => { + info!("Unsupported request for {}", device_index); + Response::from(ReturnStatus::NotSupported) + } + }, + } + } + + fn handle_device_response(&self, request: &ScmiRequest, parameters: &[usize]) -> Response { + let device_index = request.get_usize(0); + let protocol_id = request.protocol_id; + let message_id = request.message_id; + let parameter_values: Vec = parameters + .iter() + .map(|i| MessageValue::Unsigned(request.get_unsigned(*i))) + .collect(); + let result = self.handle_device( + device_index, + protocol_id, + message_id, + parameter_values.as_slice(), + ); + self.device_response(result, device_index) + } + fn discover_list_protocols(&self, request: &ScmiRequest) -> Response { // Base protocol is skipped let skip: usize = request @@ -422,6 +716,8 @@ impl ScmiHandler { #[cfg(test)] mod tests { + use crate::devices; + use super::*; #[test] @@ -448,7 +744,7 @@ mod tests { let values = vec![ MessageValue::Signed(-2), MessageValue::Unsigned(8), - MessageValue::String("foo".to_owned(), 16), + MessageValue::String("foo".to_owned(), MAX_SIMPLE_STRING_LENGTH), ]; let len = values.len() + 1; let response = Response::from(&values); @@ -463,7 +759,7 @@ mod tests { let values = vec![ MessageValue::Signed(-2), MessageValue::Unsigned(800_000_000), - MessageValue::String("foo".to_owned(), 16), + MessageValue::String("foo".to_owned(), MAX_SIMPLE_STRING_LENGTH), ]; let response = Response::from(&values); ScmiResponse::from(header, response) @@ -554,6 +850,22 @@ mod tests { assert_eq!(request.get_unsigned(0), value); } + #[test] + fn test_unsupported_parameters() { + let handler = ScmiHandler::new(); + let request = make_request(BASE_PROTOCOL_ID, 0x4); + assert_eq!(handler.number_of_parameters(&request), None); + } + + fn make_handler() -> ScmiHandler { + let mut handler = ScmiHandler::new(); + for i in 0..2 { + let fake_sensor = Box::new(devices::FakeSensor::new(format!("fake{i}"))); + handler.register_device(fake_sensor); + } + handler + } + fn test_message( protocol_id: ProtocolId, message_id: MessageId, @@ -561,13 +873,32 @@ mod tests { result_code: ReturnStatus, result_values: Vec, ) { - let mut handler = ScmiHandler::new(); + let mut handler = make_handler(); + test_message_with_handler( + protocol_id, + message_id, + parameters, + result_code, + result_values, + &mut handler, + ); + } + + fn test_message_with_handler( + protocol_id: ProtocolId, + message_id: MessageId, + parameters: Vec, + result_code: ReturnStatus, + result_values: Vec, + handler: &mut ScmiHandler, + ) { let mut request = make_request(protocol_id, message_id); let header = request.header; if !parameters.is_empty() { let parameter_slice = parameters.as_slice(); - store_parameters(&handler, &mut request, parameter_slice); + store_parameters(handler, &mut request, parameter_slice); } + let response = handler.handle(request); assert_eq!(response.header, header); let mut bytes: Vec = vec![]; @@ -607,7 +938,7 @@ mod tests { #[test] fn test_base_protocol_attributes() { - let result = vec![MessageValue::Unsigned(0)]; + let result = vec![MessageValue::Unsigned(1)]; test_message( BASE_PROTOCOL_ID, BASE_PROTOCOL_ATTRIBUTES, @@ -656,7 +987,10 @@ mod tests { #[test] fn test_base_discover_vendor() { - let result = vec![MessageValue::String(String::from("rust-vmm"), 16)]; + let result = vec![MessageValue::String( + "rust-vmm".to_owned(), + MAX_SIMPLE_STRING_LENGTH, + )]; test_message( BASE_PROTOCOL_ID, BASE_DISCOVER_VENDOR, @@ -681,7 +1015,7 @@ mod tests { #[test] fn test_base_discover_list_protocols() { let parameters = vec![MessageValue::Unsigned(0)]; - let result = vec![MessageValue::Unsigned(0)]; + let result = vec![MessageValue::Unsigned(1), MessageValue::Unsigned(21)]; test_message( BASE_PROTOCOL_ID, BASE_DISCOVER_LIST_PROTOCOLS, @@ -690,4 +1024,253 @@ mod tests { result, ); } + + #[test] + fn test_sensor_version() { + let values = vec![MessageValue::Unsigned(0x30000)]; + test_message( + SENSOR_PROTOCOL_ID, + SENSOR_VERSION, + vec![], + ReturnStatus::Success, + values, + ); + } + + #[test] + fn test_sensor_attributes() { + let result = vec![ + MessageValue::Unsigned(2), + MessageValue::Unsigned(0), + MessageValue::Unsigned(0), + MessageValue::Unsigned(0), + ]; + test_message( + SENSOR_PROTOCOL_ID, + SENSOR_ATTRIBUTES, + vec![], + ReturnStatus::Success, + result, + ); + } + + #[test] + fn test_sensor_message_attributes_supported() { + let parameters = vec![MessageValue::Unsigned(u32::from(SENSOR_DESCRIPTION_GET))]; + let result = vec![MessageValue::Unsigned(0)]; + test_message( + SENSOR_PROTOCOL_ID, + SENSOR_MESSAGE_ATTRIBUTES, + parameters, + ReturnStatus::Success, + result, + ); + } + + #[test] + fn test_sensor_message_attributes_unsupported() { + let parameters = vec![MessageValue::Unsigned(0x5)]; + test_message( + SENSOR_PROTOCOL_ID, + SENSOR_MESSAGE_ATTRIBUTES, + parameters, + ReturnStatus::NotFound, + vec![], + ); + } + + #[test] + fn test_sensor_protocol_message_attributes_invalid() { + let parameters = vec![MessageValue::Unsigned(0x100)]; + test_message( + SENSOR_PROTOCOL_ID, + SENSOR_MESSAGE_ATTRIBUTES, + parameters, + ReturnStatus::InvalidParameters, + vec![], + ); + } + + fn check_sensor_description(sensor_index: u32) { + let n_sensors = 2; + let parameters = vec![MessageValue::Unsigned(sensor_index)]; + let mut result = vec![MessageValue::Unsigned(n_sensors - sensor_index)]; + for i in sensor_index..n_sensors { + let mut description = vec![ + MessageValue::Unsigned(i), + MessageValue::Unsigned(1 << 30), + MessageValue::Unsigned(3 << 16 | 1 << 8), + MessageValue::String(format!("fake{i}"), MAX_SIMPLE_STRING_LENGTH), + ]; + result.append(&mut description); + } + test_message( + SENSOR_PROTOCOL_ID, + SENSOR_DESCRIPTION_GET, + parameters, + ReturnStatus::Success, + result, + ); + } + + #[test] + fn test_sensor_description_get() { + check_sensor_description(0); + check_sensor_description(1); + } + + #[test] + fn test_sensor_description_get_invalid() { + let parameters = vec![MessageValue::Unsigned(2)]; + test_message( + SENSOR_PROTOCOL_ID, + SENSOR_DESCRIPTION_GET, + parameters, + ReturnStatus::InvalidParameters, + vec![], + ); + } + + fn check_sensor_axis_description(axis_index: u32) { + let n_axes = 3; + let parameters = vec![ + MessageValue::Unsigned(0), + MessageValue::Unsigned(axis_index), + ]; + let mut result = vec![MessageValue::Unsigned(n_axes - axis_index)]; + for i in axis_index..n_axes { + let name = format!("acc_{}", char::from_u32('X' as u32 + i).unwrap()).to_string(); + let mut description = vec![ + MessageValue::Unsigned(i), + MessageValue::Unsigned(0), + MessageValue::Unsigned(SENSOR_UNIT_METERS_PER_SECOND_SQUARED), + MessageValue::String(name, MAX_SIMPLE_STRING_LENGTH), + ]; + result.append(&mut description); + } + test_message( + SENSOR_PROTOCOL_ID, + SENSOR_AXIS_DESCRIPTION_GET, + parameters, + ReturnStatus::Success, + result, + ); + } + + #[test] + fn test_sensor_axis_description_get() { + check_sensor_axis_description(0); + check_sensor_axis_description(1); + check_sensor_axis_description(2); + } + + #[test] + fn test_sensor_axis_description_get_invalid() { + let parameters = vec![MessageValue::Unsigned(0), MessageValue::Unsigned(3)]; + test_message( + SENSOR_PROTOCOL_ID, + SENSOR_AXIS_DESCRIPTION_GET, + parameters, + ReturnStatus::InvalidParameters, + vec![], + ); + } + + fn check_enabled(sensor: u32, enabled: bool, handler: &mut ScmiHandler) { + let enabled_flag = u32::from(enabled); + let parameters = vec![MessageValue::Unsigned(sensor)]; + let result = vec![MessageValue::Unsigned(enabled_flag)]; + test_message_with_handler( + SENSOR_PROTOCOL_ID, + SENSOR_CONFIG_GET, + parameters, + ReturnStatus::Success, + result, + handler, + ); + } + + #[test] + fn test_sensor_config_get() { + let mut handler = make_handler(); + check_enabled(0, false, &mut handler); + } + + fn enable_sensor(sensor: u32, enable: bool, handler: &mut ScmiHandler) { + let enable_flag = u32::from(enable); + let parameters = vec![ + MessageValue::Unsigned(sensor), + MessageValue::Unsigned(enable_flag), + ]; + let result = vec![]; + test_message_with_handler( + SENSOR_PROTOCOL_ID, + SENSOR_CONFIG_SET, + parameters, + ReturnStatus::Success, + result, + handler, + ); + } + + #[test] + fn test_sensor_config_set() { + let mut handler = make_handler(); + enable_sensor(0, true, &mut handler); + check_enabled(0, true, &mut handler); + check_enabled(1, false, &mut handler); + enable_sensor(1, true, &mut handler); + check_enabled(1, true, &mut handler); + enable_sensor(0, true, &mut handler); + check_enabled(0, true, &mut handler); + enable_sensor(0, false, &mut handler); + check_enabled(0, false, &mut handler); + } + + #[test] + fn test_sensor_config_set_invalid() { + let parameters = vec![MessageValue::Unsigned(0), MessageValue::Unsigned(3)]; + test_message( + SENSOR_PROTOCOL_ID, + SENSOR_CONFIG_SET, + parameters, + ReturnStatus::NotSupported, + vec![], + ); + } + + #[test] + fn test_sensor_reading_get() { + let mut handler = make_handler(); + for sensor in 0..2 { + enable_sensor(sensor, true, &mut handler); + } + for iteration in 0..2 { + for sensor in 0..2 { + let parameters = vec![MessageValue::Unsigned(sensor), MessageValue::Unsigned(0)]; + let result = vec![ + MessageValue::Unsigned(iteration), + MessageValue::Unsigned(0), + MessageValue::Unsigned(0), + MessageValue::Unsigned(0), + MessageValue::Unsigned(iteration + 100), + MessageValue::Unsigned(0), + MessageValue::Unsigned(0), + MessageValue::Unsigned(0), + MessageValue::Unsigned(iteration + 200), + MessageValue::Unsigned(0), + MessageValue::Unsigned(0), + MessageValue::Unsigned(0), + ]; + test_message_with_handler( + SENSOR_PROTOCOL_ID, + SENSOR_READING_GET, + parameters, + ReturnStatus::Success, + result, + &mut handler, + ); + } + } + } } From 3d1cc5b37000fda723e378846fb7c23fb2d0315a Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Fri, 30 Jun 2023 18:00:28 +0200 Subject: [PATCH 139/189] scmi: Add a command line option for defining devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code already contains support for creating devices that can serve as SCMI-accessible sensors and a sample fake devices. But to actually use the device, the code must be modified. This patch adds a command line option to define the devices on start. The format of the option value is in the QEMU style: DEVICE,PROPERTY=VALUE,… For example: --device fake,name=fake1 fake,name=fake2 Signed-off-by: Milan Zamazal --- crates/scmi/README.md | 9 +++- crates/scmi/src/devices.rs | 45 ++++++++++++++---- crates/scmi/src/main.rs | 91 +++++++++++++++++++++++++++++++------ crates/scmi/src/scmi.rs | 9 +++- crates/scmi/src/vhu_scmi.rs | 46 ++++++++++++++++--- 5 files changed, 167 insertions(+), 33 deletions(-) diff --git a/crates/scmi/README.md b/crates/scmi/README.md index 49f1b70..8c1f9c4 100644 --- a/crates/scmi/README.md +++ b/crates/scmi/README.md @@ -24,6 +24,13 @@ the Examples section below. Location of the vhost-user Unix domain sockets. +.. option:: -d, --device=SPEC + + SCMI device specification in the format `ID,PROPERTY=VALUE,...`. + Can be used multiple times for multiple exposed devices. + If no device is specified then no device will be provided to the + guest OS but VirtIO SCMI will be still available there. + You can set `RUST_LOG` environment variable to `debug` to get maximum messages on the standard error output. @@ -33,7 +40,7 @@ The daemon should be started first: :: - host# vhost-device-scmi --socket-path=scmi.sock + host# vhost-device-scmi --socket-path=scmi.sock --device fake,name=foo The QEMU invocation needs to create a chardev socket the device can use to communicate as well as share the guests memory over a memfd: diff --git a/crates/scmi/src/devices.rs b/crates/scmi/src/devices.rs index 174aa39..39dd67a 100644 --- a/crates/scmi/src/devices.rs +++ b/crates/scmi/src/devices.rs @@ -1,15 +1,29 @@ // SPDX-FileCopyrightText: Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 +use std::collections::HashMap; + use log::debug; -use crate::scmi::{ - DeviceResult, MessageId, MessageValue, MessageValues, ProtocolId, ScmiDevice, ScmiDeviceError, - MAX_SIMPLE_STRING_LENGTH, SENSOR_AXIS_DESCRIPTION_GET, SENSOR_CONFIG_GET, SENSOR_CONFIG_SET, - SENSOR_CONTINUOUS_UPDATE_NOTIFY, SENSOR_DESCRIPTION_GET, SENSOR_PROTOCOL_ID, - SENSOR_READING_GET, SENSOR_UNIT_METERS_PER_SECOND_SQUARED, +use crate::{ + scmi::{ + DeviceResult, MessageId, MessageValue, MessageValues, ProtocolId, ScmiDevice, + ScmiDeviceError, MAX_SIMPLE_STRING_LENGTH, SENSOR_AXIS_DESCRIPTION_GET, SENSOR_CONFIG_GET, + SENSOR_CONFIG_SET, SENSOR_CONTINUOUS_UPDATE_NOTIFY, SENSOR_DESCRIPTION_GET, + SENSOR_PROTOCOL_ID, SENSOR_READING_GET, SENSOR_UNIT_METERS_PER_SECOND_SQUARED, + }, + DeviceProperties, }; +type DeviceSpecification = fn() -> Box; +type NameDeviceMapping = HashMap; + +pub fn available_devices() -> NameDeviceMapping { + let mut devices: NameDeviceMapping = HashMap::new(); + devices.insert(String::from("fake"), FakeSensor::new); + devices +} + pub struct FakeSensor { enabled: bool, value: u8, @@ -19,16 +33,31 @@ pub struct FakeSensor { impl FakeSensor { const NUMBER_OF_AXES: u32 = 3; - pub const fn new(name: String) -> Self { - Self { + #[allow(clippy::new_ret_no_self)] + pub fn new() -> Box { + let name = String::from("fake"); + let sensor = Self { enabled: false, value: 0, name, - } + }; + Box::new(sensor) } } impl ScmiDevice for FakeSensor { + fn configure(&mut self, properties: &DeviceProperties) -> Result<(), String> { + for (k, v) in properties { + if k == "name" { + // TODO: Check for duplicate names + self.name = String::from(v); + } else { + return Result::Err(format!("Invalid device option: {k}")); + } + } + Ok(()) + } + fn protocol(&self) -> ProtocolId { SENSOR_PROTOCOL_ID } diff --git a/crates/scmi/src/main.rs b/crates/scmi/src/main.rs index 2cf8906..c9e176e 100644 --- a/crates/scmi/src/main.rs +++ b/crates/scmi/src/main.rs @@ -2,48 +2,79 @@ // SPDX-License-Identifier: Apache-2.0 // Based on implementation of other devices here, Copyright by Linaro Ltd. +mod devices; mod scmi; mod vhu_scmi; -mod devices; -use std::process::exit; -use std::sync::{Arc, RwLock}; +use std::{ + process::exit, + sync::{Arc, RwLock}, +}; use clap::Parser; +use itertools::Itertools; use log::{debug, error, info, warn}; use vhost::vhost_user; use vhost::vhost_user::Listener; use vhost_user_backend::VhostUserDaemon; -use vhu_scmi::{VuScmiBackend, VuScmiError}; +use vhu_scmi::VuScmiBackend; use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; -type Result = std::result::Result; +type Result = std::result::Result; #[derive(Parser)] struct ScmiArgs { // Location of vhost-user Unix domain socket. #[clap(short, long, help = "vhost-user socket to use")] socket_path: String, + // Specification of SCMI devices to create. + #[clap(short, long)] + #[arg(num_args(1..))] + device: Vec, } -struct VuScmiConfig { +// [(NAME, [(PROPERTY, VALUE), ...]), ...] +type DeviceDescription = Vec<(String, DeviceProperties)>; +type DeviceProperties = Vec<(String, String)>; + +pub struct VuScmiConfig { socket_path: String, + devices: DeviceDescription, } impl TryFrom for VuScmiConfig { - type Error = VuScmiError; + type Error = String; fn try_from(cmd_args: ScmiArgs) -> Result { let socket_path = cmd_args.socket_path.trim().to_string(); - Ok(Self { socket_path }) + let device_iterator = cmd_args.device.iter(); + let mut devices: DeviceDescription = vec![]; + for d in device_iterator { + let mut split = d.split(','); + let name = split.next().unwrap().to_owned(); + let mut properties: DeviceProperties = vec![]; + for s in split { + if let Some((key, value)) = s.split('=').collect_tuple() { + properties.push((key.to_owned(), value.to_owned())); + } else { + return Result::Err(format!("Invalid device {name} property format: {s}")); + } + } + devices.push((name, properties)); + } + Ok(Self { + socket_path, + devices, + }) } } fn start_backend(config: VuScmiConfig) -> Result<()> { loop { debug!("Starting backend"); - let backend = Arc::new(RwLock::new(VuScmiBackend::new().unwrap())); + // TODO: Print a nice error message on backend configuration failure. + let backend = Arc::new(RwLock::new(VuScmiBackend::new(&config).unwrap())); let listener = Listener::new(config.socket_path.clone(), true).unwrap(); let mut daemon = VhostUserDaemon::new( "vhost-device-scmi".to_owned(), @@ -78,9 +109,17 @@ fn start_backend(config: VuScmiConfig) -> Result<()> { fn main() { env_logger::init(); - if let Err(error) = start_backend(VuScmiConfig::try_from(ScmiArgs::parse()).unwrap()) { - error!("{error}"); - exit(1); + match VuScmiConfig::try_from(ScmiArgs::parse()) { + Ok(config) => { + if let Err(error) = start_backend(config) { + error!("{error}"); + exit(1); + } + } + Err(message) => { + println!("{message}"); + // TODO: print help + } } } @@ -91,9 +130,31 @@ mod tests { #[test] fn test_command_line() { let path = "/foo/scmi.sock".to_owned(); - let command_line = format!("-s {path}"); - let args: ScmiArgs = Parser::parse_from(["", &command_line]); - let config: VuScmiConfig = args.try_into().unwrap(); + let params_string = format!( + "binary \ + --device dummy \ + -s {path} \ + --device fake,name=foo,prop=value \ + -d fake,name=bar" + ); + let params: Vec<&str> = params_string.split_whitespace().collect(); + let args: ScmiArgs = Parser::parse_from(params); + let config = VuScmiConfig::try_from(args).unwrap(); assert_eq!(config.socket_path, path); + let devices = vec![ + ("dummy".to_owned(), vec![]), + ( + "fake".to_owned(), + vec![ + ("name".to_owned(), "foo".to_owned()), + ("prop".to_owned(), "value".to_owned()), + ], + ), + ( + "fake".to_owned(), + vec![("name".to_owned(), "bar".to_owned())], + ), + ]; + assert_eq!(config.devices, devices); } } diff --git a/crates/scmi/src/scmi.rs b/crates/scmi/src/scmi.rs index e168654..d4b982b 100644 --- a/crates/scmi/src/scmi.rs +++ b/crates/scmi/src/scmi.rs @@ -11,6 +11,8 @@ use itertools::Itertools; use log::{debug, error, info}; use thiserror::Error as ThisError; +use crate::DeviceProperties; + pub type MessageHeader = u32; pub const MAX_SIMPLE_STRING_LENGTH: usize = 16; // incl. NULL terminator @@ -478,6 +480,7 @@ pub enum ScmiDeviceError { } pub trait ScmiDevice: Send { + fn configure(&mut self, properties: &DeviceProperties) -> Result<(), String>; fn protocol(&self) -> ProtocolId; fn handle( &mut self, @@ -716,7 +719,7 @@ impl ScmiHandler { #[cfg(test)] mod tests { - use crate::devices; + use crate::devices::FakeSensor; use super::*; @@ -860,7 +863,9 @@ mod tests { fn make_handler() -> ScmiHandler { let mut handler = ScmiHandler::new(); for i in 0..2 { - let fake_sensor = Box::new(devices::FakeSensor::new(format!("fake{i}"))); + let properties = vec![("name".to_owned(), format!("fake{i}"))]; + let mut fake_sensor = FakeSensor::new(); + fake_sensor.configure(&properties).unwrap(); handler.register_device(fake_sensor); } handler diff --git a/crates/scmi/src/vhu_scmi.rs b/crates/scmi/src/vhu_scmi.rs index e3f37b5..2049383 100644 --- a/crates/scmi/src/vhu_scmi.rs +++ b/crates/scmi/src/vhu_scmi.rs @@ -20,7 +20,10 @@ use vm_memory::{ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; +use crate::devices::available_devices; +use crate::scmi::ScmiDevice; use crate::scmi::{MessageHeader, ScmiHandler, ScmiRequest}; +use crate::VuScmiConfig; // QUEUE_SIZE must be apparently at least 1024 for MMIO. // There is probably a maximum size per descriptor defined in the kernel. @@ -40,6 +43,8 @@ pub enum VuScmiError { DescriptorReadFailed, #[error("Descriptor write failed")] DescriptorWriteFailed, + #[error("Error when configuring device {0}: {1}")] + DeviceConfigurationError(String, String), #[error("Failed to create new EventFd")] EventFdFailed, #[error("Failed to handle event, didn't match EPOLLIN")] @@ -60,6 +65,8 @@ pub enum VuScmiError { UnexpectedReadableDescriptor(usize), #[error("Received unexpected write only descriptor at index {0}")] UnexpectedWriteOnlyDescriptor(usize), + #[error("Unknown device requested: {0}")] + UnknownDeviceRequested(String), } impl From for io::Error { @@ -88,14 +95,31 @@ pub struct VuScmiBackend { } impl VuScmiBackend { - pub fn new() -> Result { + pub fn new(config: &VuScmiConfig) -> Result { + let mut handler = ScmiHandler::new(); + let device_mapping = available_devices(); + for (name, properties) in config.devices.iter() { + match device_mapping.get(name) { + Some(constructor) => { + let mut device: Box = constructor(); + if let Err(message) = device.configure(properties) { + return Result::Err(VuScmiError::DeviceConfigurationError( + name.clone(), + message, + )); + } + handler.register_device(device); + } + None => return Result::Err(VuScmiError::UnknownDeviceRequested(name.clone())), + }; + } Ok(Self { event_idx: false, exit_event: EventFd::new(EFD_NONBLOCK).map_err(|_| VuScmiError::EventFdFailed)?, mem: None, event_vring: None, event_descriptors: vec![], - scmi_handler: ScmiHandler::new(), + scmi_handler: handler, }) } @@ -549,9 +573,17 @@ mod tests { assert_eq!(response[0..result.len()], result); } + fn make_backend() -> VuScmiBackend { + let config = VuScmiConfig { + socket_path: "/foo/scmi.sock".to_owned(), + devices: vec![], + }; + VuScmiBackend::new(&config).unwrap() + } + #[test] fn test_process_requests() { - let mut backend = VuScmiBackend::new().unwrap(); + let mut backend = make_backend(); let mem = GuestMemoryAtomic::new( GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), ); @@ -583,7 +615,7 @@ mod tests { #[test] fn test_process_requests_failure() { - let mut backend = VuScmiBackend::new().unwrap(); + let mut backend = make_backend(); let mem = GuestMemoryAtomic::new( GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), ); @@ -749,7 +781,7 @@ mod tests { #[test] fn test_event_requests() { - let mut backend = VuScmiBackend::new().unwrap(); + let mut backend = make_backend(); let mem = GuestMemoryAtomic::new( GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), ); @@ -778,7 +810,7 @@ mod tests { #[test] fn test_event_requests_failure() { - let mut backend = VuScmiBackend::new().unwrap(); + let mut backend = make_backend(); let mem = GuestMemoryAtomic::new( GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(), ); @@ -815,7 +847,7 @@ mod tests { #[test] fn test_backend() { - let mut backend = VuScmiBackend::new().unwrap(); + let mut backend = make_backend(); assert_eq!(backend.num_queues(), NUM_QUEUES); assert_eq!(backend.max_queue_size(), QUEUE_SIZE); From e0fdfa4c383d79f5b107f823951a8dfc4a39a339 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Fri, 21 Jul 2023 20:43:58 +0200 Subject: [PATCH 140/189] scmi: Refactor devices.rs for sensor code reuse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Different sensors will have similar handling. Let’s extract generic parts from FakeSensor implementation into a reusable code, within the limits of Rust. Signed-off-by: Milan Zamazal --- crates/scmi/src/devices.rs | 271 +++++++++++++++++++++++++------------ 1 file changed, 185 insertions(+), 86 deletions(-) diff --git a/crates/scmi/src/devices.rs b/crates/scmi/src/devices.rs index 39dd67a..be86507 100644 --- a/crates/scmi/src/devices.rs +++ b/crates/scmi/src/devices.rs @@ -20,107 +20,141 @@ type NameDeviceMapping = HashMap; pub fn available_devices() -> NameDeviceMapping { let mut devices: NameDeviceMapping = HashMap::new(); - devices.insert(String::from("fake"), FakeSensor::new); + devices.insert("fake".to_owned(), FakeSensor::new); devices } -pub struct FakeSensor { - enabled: bool, - value: u8, +// Common sensor infrastructure + +pub struct Sensor { name: String, + enabled: bool, } -impl FakeSensor { - const NUMBER_OF_AXES: u32 = 3; - - #[allow(clippy::new_ret_no_self)] - pub fn new() -> Box { - let name = String::from("fake"); - let sensor = Self { +impl Sensor { + const fn new(default_name: String) -> Self { + Self { + name: default_name, enabled: false, - value: 0, - name, - }; - Box::new(sensor) + } } } -impl ScmiDevice for FakeSensor { - fn configure(&mut self, properties: &DeviceProperties) -> Result<(), String> { - for (k, v) in properties { - if k == "name" { - // TODO: Check for duplicate names - self.name = String::from(v); - } else { - return Result::Err(format!("Invalid device option: {k}")); - } - } - Ok(()) - } +trait SensorT: Send { + fn sensor(&self) -> &Sensor; + fn sensor_mut(&mut self) -> &mut Sensor; fn protocol(&self) -> ProtocolId { SENSOR_PROTOCOL_ID } + fn invalid_property(&self, name: &String) -> Result<(), String> { + Result::Err(format!("Invalid device option: {name}")) + } + + fn process_property(&mut self, name: &String, _value: &str) -> Result<(), String> { + self.invalid_property(name) + } + + fn configure(&mut self, properties: &DeviceProperties) -> Result<(), String> { + for (name, value) in properties { + if name == "name" { + // TODO: Check for duplicate names + self.sensor_mut().name = String::from(value); + } else { + self.process_property(name, value)?; + } + } + Ok(()) + } + + fn number_of_axes(&self) -> u32 { + 1 + } + + fn description_get(&self) -> DeviceResult { + // Continuous update required by Linux SCMI IIO driver + let low = 1 << 30; + let high = self.number_of_axes() << 16 | 1 << 8; + let name = self.sensor().name.clone(); + let values: MessageValues = vec![ + // attributes low + MessageValue::Unsigned(low), + // attributes high + MessageValue::Unsigned(high), + // name, up to 16 bytes with final NULL (non-extended version) + MessageValue::String(name, MAX_SIMPLE_STRING_LENGTH), + ]; + Ok(values) + } + + fn axis_unit(&self) -> u32; + + fn axis_name_prefix(&self) -> String { + "axis".to_owned() + } + + fn axis_name_suffix(&self, axis: u32) -> char { + match axis { + 0 => 'X', + 1 => 'Y', + 2 => 'Z', + _ => 'N', // shouldn't be reached currently + } + } + + fn axis_description(&self, axis: u32) -> Vec { + let mut values = vec![]; + values.push(MessageValue::Unsigned(axis)); // axis id + values.push(MessageValue::Unsigned(0)); // attributes low + values.push(MessageValue::Unsigned(self.axis_unit())); // attributes high + + // Name in the recommended format, 16 bytes: + let prefix = self.axis_name_prefix(); + let suffix = self.axis_name_suffix(axis); + values.push(MessageValue::String( + format!("{prefix}_{suffix}"), + MAX_SIMPLE_STRING_LENGTH, + )); + values + } + + fn config_get(&self) -> DeviceResult { + let config = u32::from(self.sensor().enabled); + Ok(vec![MessageValue::Unsigned(config)]) + } + + fn config_set(&mut self, config: u32) -> DeviceResult { + if config & 0xFFFFFFFE != 0 { + return Result::Err(ScmiDeviceError::UnsupportedRequest); + } + self.sensor_mut().enabled = config != 0; + debug!("Sensor enabled: {}", self.sensor().enabled); + Ok(vec![]) + } + + fn reading_get(&mut self) -> DeviceResult; + fn handle(&mut self, message_id: MessageId, parameters: &[MessageValue]) -> DeviceResult { match message_id { - SENSOR_DESCRIPTION_GET => { - // Continuous update required by Linux SCMI IIO driver - let low = 1 << 30; - let high = Self::NUMBER_OF_AXES << 16 | 1 << 8; - let name = self.name.clone(); - let values: MessageValues = vec![ - // attributes low - MessageValue::Unsigned(low), - // attributes high - MessageValue::Unsigned(high), - // name, up to 16 bytes with final NULL (non-extended version) - MessageValue::String(name, MAX_SIMPLE_STRING_LENGTH), - ]; - Ok(values) - } + SENSOR_DESCRIPTION_GET => self.description_get(), SENSOR_AXIS_DESCRIPTION_GET => { + let n_sensor_axes = self.number_of_axes(); let axis_desc_index = parameters[0].get_unsigned(); - if axis_desc_index >= Self::NUMBER_OF_AXES { + if axis_desc_index >= n_sensor_axes { return Result::Err(ScmiDeviceError::InvalidParameters); } - let mut values = vec![MessageValue::Unsigned( - Self::NUMBER_OF_AXES - axis_desc_index, - )]; - for i in axis_desc_index..Self::NUMBER_OF_AXES { - values.push(MessageValue::Unsigned(i)); // axis id - values.push(MessageValue::Unsigned(0)); // attributes low - - // The sensor type is "Meters per second squared", since this is the - // only, together with "Radians per second", what Google Linux IIO - // supports (accelerometers and gyroscopes only). - values.push(MessageValue::Unsigned( - SENSOR_UNIT_METERS_PER_SECOND_SQUARED, - )); // attributes high - - // Name in the recommended format, 16 bytes: - let axis = match i { - 0 => 'X', - 1 => 'Y', - 2 => 'Z', - _ => 'N', // shouldn't be reached currently - }; - values.push(MessageValue::String(format!("acc_{axis}").to_string(), 16)); + let mut values = vec![MessageValue::Unsigned(n_sensor_axes - axis_desc_index)]; + for i in axis_desc_index..n_sensor_axes { + let mut description = self.axis_description(i); + values.append(&mut description); } Ok(values) } - SENSOR_CONFIG_GET => { - let config = u32::from(self.enabled); - Ok(vec![MessageValue::Unsigned(config)]) - } + SENSOR_CONFIG_GET => self.config_get(), SENSOR_CONFIG_SET => { let config = parameters[0].get_unsigned(); - if config & 0xFFFFFFFE != 0 { - return Result::Err(ScmiDeviceError::UnsupportedRequest); - } - self.enabled = config != 0; - debug!("Sensor enabled: {}", self.enabled); - Ok(vec![]) + self.config_set(config) } SENSOR_CONTINUOUS_UPDATE_NOTIFY => { // Linux VIRTIO SCMI insists on this. @@ -128,21 +162,86 @@ impl ScmiDevice for FakeSensor { Ok(vec![]) } SENSOR_READING_GET => { - if !self.enabled { + if !self.sensor().enabled { return Result::Err(ScmiDeviceError::NotEnabled); } - let value = self.value; - self.value = self.value.overflowing_add(1).0; - let mut result = vec![]; - for i in 0..3 { - result.push(MessageValue::Unsigned(u32::from(value) + 100 * i)); - result.push(MessageValue::Unsigned(0)); - result.push(MessageValue::Unsigned(0)); - result.push(MessageValue::Unsigned(0)); - } - Ok(result) + self.reading_get() } _ => Result::Err(ScmiDeviceError::UnsupportedRequest), } } } + +// It's possible to impl ScmiDevice for SensorT but it is not very useful +// because it doesn't allow to pass SensorT as ScmiDevice directly. +// Hence this wrapper. +struct SensorDevice(Box); + +impl ScmiDevice for SensorDevice { + fn configure(&mut self, properties: &DeviceProperties) -> Result<(), String> { + self.0.configure(properties) + } + + fn protocol(&self) -> ProtocolId { + self.0.protocol() + } + + fn handle(&mut self, message_id: MessageId, parameters: &[MessageValue]) -> DeviceResult { + self.0.handle(message_id, parameters) + } +} + +// Particular sensor implementations + +pub struct FakeSensor { + sensor: Sensor, + value: u8, +} + +impl SensorT for FakeSensor { + // TODO: Define a macro for this boilerplate? + fn sensor(&self) -> &Sensor { + &self.sensor + } + fn sensor_mut(&mut self) -> &mut Sensor { + &mut self.sensor + } + + fn number_of_axes(&self) -> u32 { + 3 + } + + fn axis_unit(&self) -> u32 { + // The sensor type is "Meters per second squared", since this is the + // only, together with "Radians per second", what Google Linux IIO + // supports (accelerometers and gyroscopes only). + SENSOR_UNIT_METERS_PER_SECOND_SQUARED + } + + fn axis_name_prefix(&self) -> String { + "acc".to_owned() + } + + fn reading_get(&mut self) -> DeviceResult { + let value = self.value; + self.value = self.value.overflowing_add(1).0; + let mut result = vec![]; + for i in 0..3 { + result.push(MessageValue::Unsigned(u32::from(value) + 100 * i)); + result.push(MessageValue::Unsigned(0)); + result.push(MessageValue::Unsigned(0)); + result.push(MessageValue::Unsigned(0)); + } + Ok(result) + } +} + +impl FakeSensor { + #[allow(clippy::new_ret_no_self)] + pub fn new() -> Box { + let sensor = Sensor::new("fake".to_owned()); + let fake_sensor = Self { sensor, value: 0 }; + let sensor_device = SensorDevice(Box::new(fake_sensor)); + Box::new(sensor_device) + } +} From 4014a6366f4815e5b96a384dc1515e4004253d29 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Wed, 26 Jul 2023 14:43:03 +0200 Subject: [PATCH 141/189] scmi: Kernel instructions and changes for IIO added MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Followup patches will allow connecting vhost-user-scmi to industrial I/O devices. On host without IIO devices, it’s possible to use emulated devices for testing. This patch documents how to use them and also provides a slightly customized IIO dummy kernel module. Signed-off-by: Milan Zamazal --- crates/scmi/README.md | 8 + crates/scmi/kernel/iio-dummy/.gitignore | 7 + crates/scmi/kernel/iio-dummy/Makefile | 19 + crates/scmi/kernel/iio-dummy/README.md | 184 +++++ .../scmi/kernel/iio-dummy/iio-dummy-fix.patch | 55 ++ .../kernel/iio-dummy/iio_modified_dummy.c | 693 ++++++++++++++++++ .../kernel/iio-dummy/iio_modified_dummy.h | 68 ++ 7 files changed, 1034 insertions(+) create mode 100644 crates/scmi/kernel/iio-dummy/.gitignore create mode 100644 crates/scmi/kernel/iio-dummy/Makefile create mode 100644 crates/scmi/kernel/iio-dummy/README.md create mode 100644 crates/scmi/kernel/iio-dummy/iio-dummy-fix.patch create mode 100644 crates/scmi/kernel/iio-dummy/iio_modified_dummy.c create mode 100644 crates/scmi/kernel/iio-dummy/iio_modified_dummy.h diff --git a/crates/scmi/README.md b/crates/scmi/README.md index 8c1f9c4..b7e3dcd 100644 --- a/crates/scmi/README.md +++ b/crates/scmi/README.md @@ -65,9 +65,17 @@ The currently supported SCMI protocols are: Basically only the mandatory and necessary parts of the protocols are implemented. +## Kernel support for testing + +`kernel` subdirectory contains +[instructions](kernel/iio-dummy/README.md) how to create emulated +industrial I/O devices for testing. + ## License This project is licensed under either of - [Apache License](http://www.apache.org/licenses/LICENSE-2.0), Version 2.0 - [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause) + +unless specified in particular files otherwise. diff --git a/crates/scmi/kernel/iio-dummy/.gitignore b/crates/scmi/kernel/iio-dummy/.gitignore new file mode 100644 index 0000000..a2337af --- /dev/null +++ b/crates/scmi/kernel/iio-dummy/.gitignore @@ -0,0 +1,7 @@ +*.cmd +*.ko +*.mod +*.mod.[co] +*.o +Module.symvers +modules.order diff --git a/crates/scmi/kernel/iio-dummy/Makefile b/crates/scmi/kernel/iio-dummy/Makefile new file mode 100644 index 0000000..f602b91 --- /dev/null +++ b/crates/scmi/kernel/iio-dummy/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the IIO Dummy Driver +# +# Modified by Milan Zamazal in 2023 for out of +# tree compilation. +# + +obj-m += iio_modified_dummy.o + +on_nixos = $(wildcard /etc/NIXOS) +ifeq ($(on_nixos), /etc/NIXOS) +nix_prefix = $(shell nix-build -E '(import {}).linux.dev' --no-out-link) +endif + +all: + make -C $(nix_prefix)/lib/modules/$(shell uname -r)/build M=$(PWD) modules +clean: + make -C $(nix_prefix)/lib/modules/$(shell uname -r)/build M=$(PWD) clean diff --git a/crates/scmi/kernel/iio-dummy/README.md b/crates/scmi/kernel/iio-dummy/README.md new file mode 100644 index 0000000..ed87101 --- /dev/null +++ b/crates/scmi/kernel/iio-dummy/README.md @@ -0,0 +1,184 @@ +# Using emulated industrial I/O devices + +This is a modified version of the Linux [industrial I/O dummy +driver](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/iio/dummy). +Both the original driver and this modification can provide emulated +industrial I/O devices for testing vhost-device-scmi. + +## Modifications in this module + +If the stock industrial I/O dummy driver is enough for you, use it +(but you may still want to read the instructions below). + +Otherwise, this alternative is provided with the following changes: + +- Simplified Makefile for out of tree compilation. +- The accelerometer has three axes instead of just one. + +Of course, you can modified it further for your liking if needed. + +## How to create emulated industrial I/O devices + +Make sure your kernel supports software industrial I/O devices and +industrial I/O with configfs. You can check this by running `modprobe +industrialio_sw_device && modprobe industrialio_configfs`. If any of +the modules is not present, follow the [instructions for recompiling +kernel](#recompiling-kernel-with-industrial-io) below. + +Make sure you have the right kernel version. Since Linux 5.19, the +dummy industrial I/O driver is broken. This will be probably fixed in +Linux 6.6. + +If you have a broken kernel version, apply the +[fix](./iio-dummy-fix.patch) and compile and install the modified +kernel. + +If you want to use the modified module from here, compile it. In +order to do this, you must have kernel development environment +installed, for example: + +- Fedora or derivatives: `dnf install kernel-devel kernel-modules make` +- Debian or derivatives: `apt install linux-headers-$(uname -r) make` +- NixOS: `nix-shell '' -A linux.dev` + +Then you can compile the module, simply running `make` should work on +most distributions. + +Insert a dummy industrial I/O kernel module. Either the stock one: + +``` +# modprobe iio-dummy +``` + +or the modified one from here: + +``` +# modprobe industrialio +# modprobe industrialio_configfs +# modprobe industrialio_sw_device +# insmod ./iio-dummy-modified.ko +``` + +Find out where configfs is mounted: `mount | grep configfs`. It's +typically `/sys/kernel/config`. If configfs is not mounted, mount it +somewhere: `mount -t configfs none MOUNTPOINT`. + +Now you can create emulated industrial I/O devices with the stock driver: + +``` +# mkdir /sys/kernel/config/iio/devices/dummy/my-device +``` + +And/or with the modified driver from here: + +``` +# mkdir /sys/kernel/config/iio/devices/dummy-modified/my-device +``` + +If everything is OK then you can find the device in +`/sys/bus/iio/devices/`. + +## Recompiling kernel with industrial I/O + +Making a custom kernel is different on each GNU/Linux distribution. +The corresponding documentation can be found for example here: + +- Fedora: [https://fedoraproject.org/wiki/Building_a_custom_kernel](https://fedoraproject.org/wiki/Building_a_custom_kernel) +- CentOS Stream: [https://wiki.centos.org/HowTos/BuildingKernelModules](https://wiki.centos.org/HowTos/BuildingKernelModules) + (looks more useful for Fedora builds than CentOS) +- Debian: [https://kernel-team.pages.debian.net/kernel-handbook/ch-common-tasks.html#s-common-official](https://kernel-team.pages.debian.net/kernel-handbook/ch-common-tasks.html#s-common-official) +- NixOS: [https://nixos.wiki/wiki/Linux_kernel](https://nixos.wiki/wiki/Linux_kernel) + +Here are instructions for Fedora, similar steps can be used for other +distributions, with distribution specifics as described in the links +above. This is not necessarily the most official or the best way to +do it but it's a way that *actually works* for me. + +Note on CentOS Stream 9: The kernel there doesn't contain the needed +modules. Recompiling the kernel on CentOS Stream may be challenging +due to missing build dependencies. If it doesn't work for you, you +can try to use Fedora kernel and modules on CentOS Stream, including +the dummy module compiled on Fedora. + +### Install kernel sources + +``` +# dnf install 'dnf-command(download)' +$ dnf download --source kernel +$ rpm -i kernel-*.src.rpm +# dnf builddep ~/rpmbuild/SPECS/kernel.spec +``` + +### Change kernel configuration + +Not needed for current Fedora but may be needed for e.g. CentOS Stream. + +``` +# dnf install kernel-devel kernel-modules make rpm-build python3-devel ncurses-devel +$ rpmbuild -bp ~/rpmbuild/SPECS/kernel.spec +$ cd ~/rpmbuild/BUILD/kernel-*/linux-*/ +$ cp configs/kernel-VERSION-YOURARCH.config .config +$ make nconfig +``` + +Configuration options that must be enabled: + +- Device Drivers -> Industrial I/O Support -> Enable IIO configuration via configfs +- Device Drivers -> Industrial I/O Support -> Enable software IIO device support + +Optionally (you can use the alternative driver from here instead): + +- Device Drivers -> Industrial I/O Support -> IIO dummy drive -> An example driver with no hardware requirements + +Then copy `.config` back to its original file and don't forget to add +the original architecture specification line there. + +### Apply the kernel fix + +If the kernel fix from here is needed, copy it to the sources: + +``` +cp .../iio-dummy-fix.patch ~/rpmbuild/SOURCES/ +``` + +Edit `~/rpmbuild/SPECS/kernel.spec`: + +- Uncomment: `%define buildid .local`. + +- Add the patch file before: `Patch999999: linux-kernel-test.patch`. + +- Add the patch file before: `ApplyOptionalPatch linux-kernel-test.patch`. + +### Build the kernel + +You can use different options, if you don't need anything extra then +the following builds the most important rpm's: + +``` +$ rpmbuild -bb --with baseonly --without debug --without debuginfo ~/rpmbuild/SPECS/kernel.spec +``` + +## Adding industrial I/O dummy module to your kernel + +If all you need is to add a missing stock I/O dummy module, you can +try to compile just the module. Switch to kernel sources and run: + +``` +$ make oldconfig +$ make prepare +$ make modules_prepare +$ make M=drivers/iio/dummy +``` + +And insert the module: + +``` +# modprobe industrialio +# modprobe industrialio_configfs +# modprobe industrialio_sw_device +# insmod ./drivers/iio/dummy/iio-dummy.ko +``` + +If this fails, inspect `dmesg` output and try to figure out what's +wrong. If this fails too, rebuild the whole kernel with the given +module enabled. diff --git a/crates/scmi/kernel/iio-dummy/iio-dummy-fix.patch b/crates/scmi/kernel/iio-dummy/iio-dummy-fix.patch new file mode 100644 index 0000000..d74b7d5 --- /dev/null +++ b/crates/scmi/kernel/iio-dummy/iio-dummy-fix.patch @@ -0,0 +1,55 @@ +Commit 813665564b3d ("iio: core: Convert to use firmware node handle +instead of OF node") switched the kind of nodes to use for label +retrieval in device registration. Probably an unwanted change in that +commit was that if the device has no parent then NULL pointer is +accessed. This is what happens in the stock IIO dummy driver when a +new entry is created in configfs: + + # mkdir /sys/kernel/config/iio/devices/dummy/foo + BUG: kernel NULL pointer dereference, address: ... + ... + Call Trace: + __iio_device_register + iio_dummy_probe + +Since there seems to be no reason to make a parent device of an IIO +dummy device mandatory, let’s prevent the invalid memory access in +__iio_device_register when the parent device is NULL. With this +change, the IIO dummy driver works fine with configfs. + +Fixes: 813665564b3d ("iio: core: Convert to use firmware node handle instead of OF node") +Reviewed-by: Andy Shevchenko +Signed-off-by: Milan Zamazal +--- + drivers/iio/industrialio-core.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c +index c117f50d0cf3..adcba832e6fa 100644 +--- a/drivers/iio/industrialio-core.c ++++ b/drivers/iio/industrialio-core.c +@@ -1888,7 +1888,7 @@ static const struct iio_buffer_setup_ops noop_ring_setup_ops; + int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) + { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); +- struct fwnode_handle *fwnode; ++ struct fwnode_handle *fwnode = NULL; + int ret; + + if (!indio_dev->info) +@@ -1899,7 +1899,8 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) + /* If the calling driver did not initialize firmware node, do it here */ + if (dev_fwnode(&indio_dev->dev)) + fwnode = dev_fwnode(&indio_dev->dev); +- else ++ /* The default dummy IIO device has no parent */ ++ else if (indio_dev->dev.parent) + fwnode = dev_fwnode(indio_dev->dev.parent); + device_set_node(&indio_dev->dev, fwnode); + +-- + +2.40.1 + + + diff --git a/crates/scmi/kernel/iio-dummy/iio_modified_dummy.c b/crates/scmi/kernel/iio-dummy/iio_modified_dummy.c new file mode 100644 index 0000000..7504489 --- /dev/null +++ b/crates/scmi/kernel/iio-dummy/iio_modified_dummy.c @@ -0,0 +1,693 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2011 Jonathan Cameron + * + * A reference industrial I/O driver to illustrate the functionality available. + * + * There are numerous real drivers to illustrate the finer points. + * The purpose of this driver is to provide a driver with far more comments + * and explanatory notes than any 'real' driver would have. + * Anyone starting out writing an IIO driver should first make sure they + * understand all of this driver except those bits specifically marked + * as being present to allow us to 'fake' the presence of hardware. + * + * Changes by Milan Zamazal 2023, for testing + * with vhost-device-scmi: + * + * - Dropped conditional parts. + * - Use 3 axes in the accelerometer device. + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "iio_modified_dummy.h" + +static const struct config_item_type iio_dummy_type = { + .ct_owner = THIS_MODULE, +}; + +/** + * struct iio_dummy_accel_calibscale - realworld to register mapping + * @val: first value in read_raw - here integer part. + * @val2: second value in read_raw etc - here micro part. + * @regval: register value - magic device specific numbers. + */ +struct iio_dummy_accel_calibscale { + int val; + int val2; + int regval; /* what would be written to hardware */ +}; + +static const struct iio_dummy_accel_calibscale dummy_scales[] = { + { 0, 100, 0x8 }, /* 0.000100 */ + { 0, 133, 0x7 }, /* 0.000133 */ + { 733, 13, 0x9 }, /* 733.000013 */ +}; + +/* + * iio_dummy_channels - Description of available channels + * + * This array of structures tells the IIO core about what the device + * actually provides for a given channel. + */ +static const struct iio_chan_spec iio_dummy_channels[] = { + /* indexed ADC channel in_voltage0_raw etc */ + { + .type = IIO_VOLTAGE, + /* Channel has a numeric index of 0 */ + .indexed = 1, + .channel = 0, + /* What other information is available? */ + .info_mask_separate = + /* + * in_voltage0_raw + * Raw (unscaled no bias removal etc) measurement + * from the device. + */ + BIT(IIO_CHAN_INFO_RAW) | + /* + * in_voltage0_offset + * Offset for userspace to apply prior to scale + * when converting to standard units (microvolts) + */ + BIT(IIO_CHAN_INFO_OFFSET) | + /* + * in_voltage0_scale + * Multipler for userspace to apply post offset + * when converting to standard units (microvolts) + */ + BIT(IIO_CHAN_INFO_SCALE), + /* + * sampling_frequency + * The frequency in Hz at which the channels are sampled + */ + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), + /* The ordering of elements in the buffer via an enum */ + .scan_index = DUMMY_INDEX_VOLTAGE_0, + .scan_type = { /* Description of storage in buffer */ + .sign = 'u', /* unsigned */ + .realbits = 13, /* 13 bits */ + .storagebits = 16, /* 16 bits used for storage */ + .shift = 0, /* zero shift */ + }, + }, + /* Differential ADC channel in_voltage1-voltage2_raw etc*/ + { + .type = IIO_VOLTAGE, + .differential = 1, + /* + * Indexing for differential channels uses channel + * for the positive part, channel2 for the negative. + */ + .indexed = 1, + .channel = 1, + .channel2 = 2, + /* + * in_voltage1-voltage2_raw + * Raw (unscaled no bias removal etc) measurement + * from the device. + */ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + /* + * in_voltage-voltage_scale + * Shared version of scale - shared by differential + * input channels of type IIO_VOLTAGE. + */ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + /* + * sampling_frequency + * The frequency in Hz at which the channels are sampled + */ + .scan_index = DUMMY_INDEX_DIFFVOLTAGE_1M2, + .scan_type = { /* Description of storage in buffer */ + .sign = 's', /* signed */ + .realbits = 12, /* 12 bits */ + .storagebits = 16, /* 16 bits used for storage */ + .shift = 0, /* zero shift */ + }, + }, + /* Differential ADC channel in_voltage3-voltage4_raw etc*/ + { + .type = IIO_VOLTAGE, + .differential = 1, + .indexed = 1, + .channel = 3, + .channel2 = 4, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_index = DUMMY_INDEX_DIFFVOLTAGE_3M4, + .scan_type = { + .sign = 's', + .realbits = 11, + .storagebits = 16, + .shift = 0, + }, + }, + /* + * 'modified' (i.e. axis specified) acceleration channel + * in_accel_[xyz]_raw + */ + { + .type = IIO_ACCEL, + .modified = 1, + /* Channel 2 is use for modifiers */ + .channel2 = IIO_MOD_X, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + /* + * Internal bias and gain correction values. Applied + * by the hardware or driver prior to userspace + * seeing the readings. Typically part of hardware + * calibration. + */ + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_index = DUMMY_INDEX_ACCEL_X, + .scan_type = { /* Description of storage in buffer */ + .sign = 's', /* signed */ + .realbits = 16, /* 16 bits */ + .storagebits = 16, /* 16 bits used for storage */ + .shift = 0, /* zero shift */ + }, + }, + { + .type = IIO_ACCEL, + .modified = 1, + /* Channel 2 is use for modifiers */ + .channel2 = IIO_MOD_Y, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_index = DUMMY_INDEX_ACCEL_Y, + .scan_type = { /* Description of storage in buffer */ + .sign = 's', /* signed */ + .realbits = 16, /* 16 bits */ + .storagebits = 16, /* 16 bits used for storage */ + .shift = 0, /* zero shift */ + }, + }, + { + .type = IIO_ACCEL, + .modified = 1, + /* Channel 2 is use for modifiers */ + .channel2 = IIO_MOD_Z, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_index = DUMMY_INDEX_ACCEL_Z, + .scan_type = { /* Description of storage in buffer */ + .sign = 's', /* signed */ + .realbits = 16, /* 16 bits */ + .storagebits = 16, /* 16 bits used for storage */ + .shift = 0, /* zero shift */ + }, + }, + /* + * Convenience macro for timestamps. 4 is the index in + * the buffer. + */ + IIO_CHAN_SOFT_TIMESTAMP(4), + /* DAC channel out_voltage0_raw */ + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .scan_index = -1, /* No buffer support */ + .output = 1, + .indexed = 1, + .channel = 0, + }, + { + .type = IIO_STEPS, + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_CALIBHEIGHT), + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = -1, /* No buffer support */ + }, + { + .type = IIO_ACTIVITY, + .modified = 1, + .channel2 = IIO_MOD_RUNNING, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = -1, /* No buffer support */ + }, + { + .type = IIO_ACTIVITY, + .modified = 1, + .channel2 = IIO_MOD_WALKING, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = -1, /* No buffer support */ + }, +}; + +/** + * iio_dummy_read_raw() - data read function. + * @indio_dev: the struct iio_dev associated with this device instance + * @chan: the channel whose data is to be read + * @val: first element of returned value (typically INT) + * @val2: second element of returned value (typically MICRO) + * @mask: what we actually want to read as per the info_mask_* + * in iio_chan_spec. + */ +static int iio_dummy_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct iio_dummy_state *st = iio_priv(indio_dev); + int ret = -EINVAL; + + mutex_lock(&st->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: /* magic value - channel value read */ + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output) { + /* Set integer part to cached value */ + *val = st->dac_val; + ret = IIO_VAL_INT; + } else if (chan->differential) { + if (chan->channel == 1) + *val = st->differential_adc_val[0]; + else + *val = st->differential_adc_val[1]; + ret = IIO_VAL_INT; + } else { + *val = st->single_ended_adc_val; + ret = IIO_VAL_INT; + } + break; + case IIO_ACCEL: + switch(chan->scan_index) { + case DUMMY_INDEX_ACCEL_X: + *val = st->accel_val[0]; + break; + case DUMMY_INDEX_ACCEL_Y: + *val = st->accel_val[1]; + break; + case DUMMY_INDEX_ACCEL_Z: + *val = st->accel_val[2]; + break; + default: + *val = 0; + } + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_STEPS: + *val = st->steps; + ret = IIO_VAL_INT; + break; + case IIO_ACTIVITY: + switch (chan->channel2) { + case IIO_MOD_RUNNING: + *val = st->activity_running; + ret = IIO_VAL_INT; + break; + case IIO_MOD_WALKING: + *val = st->activity_walking; + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + default: + break; + } + break; + case IIO_CHAN_INFO_OFFSET: + /* only single ended adc -> 7 */ + *val = 7; + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + switch (chan->differential) { + case 0: + /* only single ended adc -> 0.001333 */ + *val = 0; + *val2 = 1333; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case 1: + /* all differential adc -> 0.000001344 */ + *val = 0; + *val2 = 1344; + ret = IIO_VAL_INT_PLUS_NANO; + } + break; + default: + break; + } + break; + case IIO_CHAN_INFO_CALIBBIAS: + /* only the acceleration axis - read from cache */ + *val = st->accel_calibbias; + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_CALIBSCALE: + *val = st->accel_calibscale->val; + *val2 = st->accel_calibscale->val2; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = 3; + *val2 = 33; + ret = IIO_VAL_INT_PLUS_NANO; + break; + case IIO_CHAN_INFO_ENABLE: + switch (chan->type) { + case IIO_STEPS: + *val = st->steps_enabled; + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + case IIO_CHAN_INFO_CALIBHEIGHT: + switch (chan->type) { + case IIO_STEPS: + *val = st->height; + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + + default: + break; + } + mutex_unlock(&st->lock); + return ret; +} + +/** + * iio_dummy_write_raw() - data write function. + * @indio_dev: the struct iio_dev associated with this device instance + * @chan: the channel whose data is to be written + * @val: first element of value to set (typically INT) + * @val2: second element of value to set (typically MICRO) + * @mask: what we actually want to write as per the info_mask_* + * in iio_chan_spec. + * + * Note that all raw writes are assumed IIO_VAL_INT and info mask elements + * are assumed to be IIO_INT_PLUS_MICRO unless the callback write_raw_get_fmt + * in struct iio_info is provided by the driver. + */ +static int iio_dummy_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + int i; + int ret = 0; + struct iio_dummy_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output == 0) + return -EINVAL; + + /* Locking not required as writing single value */ + mutex_lock(&st->lock); + st->dac_val = val; + mutex_unlock(&st->lock); + return 0; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_STEPS: + mutex_lock(&st->lock); + st->steps = val; + mutex_unlock(&st->lock); + return 0; + case IIO_ACTIVITY: + if (val < 0) + val = 0; + if (val > 100) + val = 100; + switch (chan->channel2) { + case IIO_MOD_RUNNING: + st->activity_running = val; + return 0; + case IIO_MOD_WALKING: + st->activity_walking = val; + return 0; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBSCALE: + mutex_lock(&st->lock); + /* Compare against table - hard matching here */ + for (i = 0; i < ARRAY_SIZE(dummy_scales); i++) + if (val == dummy_scales[i].val && + val2 == dummy_scales[i].val2) + break; + if (i == ARRAY_SIZE(dummy_scales)) + ret = -EINVAL; + else + st->accel_calibscale = &dummy_scales[i]; + mutex_unlock(&st->lock); + return ret; + case IIO_CHAN_INFO_CALIBBIAS: + mutex_lock(&st->lock); + st->accel_calibbias = val; + mutex_unlock(&st->lock); + return 0; + case IIO_CHAN_INFO_ENABLE: + switch (chan->type) { + case IIO_STEPS: + mutex_lock(&st->lock); + st->steps_enabled = val; + mutex_unlock(&st->lock); + return 0; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBHEIGHT: + switch (chan->type) { + case IIO_STEPS: + st->height = val; + return 0; + default: + return -EINVAL; + } + + default: + return -EINVAL; + } +} + +/* + * Device type specific information. + */ +static const struct iio_info iio_dummy_info = { + .read_raw = &iio_dummy_read_raw, + .write_raw = &iio_dummy_write_raw, +}; + +/** + * iio_dummy_init_device() - device instance specific init + * @indio_dev: the iio device structure + * + * Most drivers have one of these to set up default values, + * reset the device to known state etc. + */ +static int iio_dummy_init_device(struct iio_dev *indio_dev) +{ + struct iio_dummy_state *st = iio_priv(indio_dev); + + st->dac_val = 0; + st->single_ended_adc_val = 73; + st->differential_adc_val[0] = 33; + st->differential_adc_val[1] = -34; + st->accel_val[0] = 34; + st->accel_val[1] = 37; + st->accel_val[2] = 40; + st->accel_calibbias = -7; + st->accel_calibscale = &dummy_scales[0]; + st->steps = 47; + st->activity_running = 98; + st->activity_walking = 4; + + return 0; +} + +/** + * iio_dummy_probe() - device instance probe + * @name: name of this instance. + * + * Arguments are bus type specific. + * I2C: iio_dummy_probe(struct i2c_client *client, + * const struct i2c_device_id *id) + * SPI: iio_dummy_probe(struct spi_device *spi) + */ +static struct iio_sw_device *iio_dummy_probe(const char *name) +{ + int ret; + struct iio_dev *indio_dev; + struct iio_dummy_state *st; + struct iio_sw_device *swd; + struct device *parent; + + /* + * With hardware: Set the parent device. + * parent = &spi->dev; + * parent = &client->dev; + */ + + swd = kzalloc(sizeof(*swd), GFP_KERNEL); + if (!swd) + return ERR_PTR(-ENOMEM); + + /* + * Allocate an IIO device. + * + * This structure contains all generic state + * information about the device instance. + * It also has a region (accessed by iio_priv() + * for chip specific state information. + */ + indio_dev = iio_device_alloc(parent, sizeof(*st)); + if (!indio_dev) { + ret = -ENOMEM; + goto error_free_swd; + } + + st = iio_priv(indio_dev); + mutex_init(&st->lock); + + iio_dummy_init_device(indio_dev); + + /* + * Make the iio_dev struct available to remove function. + * Bus equivalents + * i2c_set_clientdata(client, indio_dev); + * spi_set_drvdata(spi, indio_dev); + */ + swd->device = indio_dev; + + /* + * Set the device name. + * + * This is typically a part number and obtained from the module + * id table. + * e.g. for i2c and spi: + * indio_dev->name = id->name; + * indio_dev->name = spi_get_device_id(spi)->name; + */ + indio_dev->name = kstrdup(name, GFP_KERNEL); + if (!indio_dev->name) { + ret = -ENOMEM; + goto error_free_device; + } + + /* Provide description of available channels */ + indio_dev->channels = iio_dummy_channels; + indio_dev->num_channels = ARRAY_SIZE(iio_dummy_channels); + + /* + * Provide device type specific interface functions and + * constant data. + */ + indio_dev->info = &iio_dummy_info; + + /* Specify that device provides sysfs type interfaces */ + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto error_free_name; + + iio_swd_group_init_type_name(swd, name, &iio_dummy_type); + + return swd; +error_free_name: + kfree(indio_dev->name); +error_free_device: + iio_device_free(indio_dev); +error_free_swd: + kfree(swd); + return ERR_PTR(ret); +} + +/** + * iio_dummy_remove() - device instance removal function + * @swd: pointer to software IIO device abstraction + * + * Parameters follow those of iio_dummy_probe for buses. + */ +static int iio_dummy_remove(struct iio_sw_device *swd) +{ + /* + * Get a pointer to the device instance iio_dev structure + * from the bus subsystem. E.g. + * struct iio_dev *indio_dev = i2c_get_clientdata(client); + * struct iio_dev *indio_dev = spi_get_drvdata(spi); + */ + struct iio_dev *indio_dev = swd->device; + + /* Unregister the device */ + iio_device_unregister(indio_dev); + + /* Free all structures */ + kfree(indio_dev->name); + iio_device_free(indio_dev); + + return 0; +} + +/* + * module_iio_sw_device_driver() - device driver registration + * + * Varies depending on bus type of the device. As there is no device + * here, call probe directly. For information on device registration + * i2c: + * Documentation/i2c/writing-clients.rst + * spi: + * Documentation/spi/spi-summary.rst + */ +static const struct iio_sw_device_ops iio_dummy_device_ops = { + .probe = iio_dummy_probe, + .remove = iio_dummy_remove, +}; + +static struct iio_sw_device_type iio_dummy_device = { + .name = "dummy-modified", + .owner = THIS_MODULE, + .ops = &iio_dummy_device_ops, +}; + +module_iio_sw_device_driver(iio_dummy_device); + +MODULE_AUTHOR("Jonathan Cameron "); +MODULE_DESCRIPTION("IIO dummy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/crates/scmi/kernel/iio-dummy/iio_modified_dummy.h b/crates/scmi/kernel/iio-dummy/iio_modified_dummy.h new file mode 100644 index 0000000..9b0b8b1 --- /dev/null +++ b/crates/scmi/kernel/iio-dummy/iio_modified_dummy.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/** + * Copyright (c) 2011 Jonathan Cameron + * + * Join together the various functionality of iio_modified_dummy driver + * + * Changes by Milan Zamazal 2023, for testing + * with vhost-device-scmi: + * + * - Dropped conditional parts. + * - Use 3 axes in the accelerometer device. + */ + +#ifndef _IIO_MODIFIED_DUMMY_H_ +#define _IIO_MODIFIED_DUMMY_H_ +#include + +struct iio_dummy_accel_calibscale; +struct iio_dummy_regs; + +/** + * struct iio_dummy_state - device instance specific state. + * @dac_val: cache for dac value + * @single_ended_adc_val: cache for single ended adc value + * @differential_adc_val: cache for differential adc value + * @accel_val: cache for acceleration value + * @accel_calibbias: cache for acceleration calibbias + * @accel_calibscale: cache for acceleration calibscale + * @lock: lock to ensure state is consistent + * @event_irq: irq number for event line (faked) + * @event_val: cache for event threshold value + * @event_en: cache of whether event is enabled + */ +struct iio_dummy_state { + int dac_val; + int single_ended_adc_val; + int differential_adc_val[2]; + int accel_val[3]; + int accel_calibbias; + int activity_running; + int activity_walking; + const struct iio_dummy_accel_calibscale *accel_calibscale; + struct mutex lock; + struct iio_dummy_regs *regs; + int steps_enabled; + int steps; + int height; +}; + +/** + * enum iio_modified_dummy_scan_elements - scan index enum + * @DUMMY_INDEX_VOLTAGE_0: the single ended voltage channel + * @DUMMY_INDEX_DIFFVOLTAGE_1M2: first differential channel + * @DUMMY_INDEX_DIFFVOLTAGE_3M4: second differential channel + * @DUMMY_INDEX_ACCELX: acceleration channel + * + * Enum provides convenient numbering for the scan index. + */ +enum iio_modified_dummy_scan_elements { + DUMMY_INDEX_VOLTAGE_0, + DUMMY_INDEX_DIFFVOLTAGE_1M2, + DUMMY_INDEX_DIFFVOLTAGE_3M4, + DUMMY_INDEX_ACCEL_X, + DUMMY_INDEX_ACCEL_Y, + DUMMY_INDEX_ACCEL_Z, +}; + +#endif /* _IIO_MODIFIED_DUMMY_H_ */ From 73c536df2e425f74ade8a953d17b4232d2030950 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Mon, 31 Jul 2023 10:17:06 +0200 Subject: [PATCH 142/189] scmi: Provide help for devices Writing `--device help' on the command line will list all the available devices and their parameters. Signed-off-by: Milan Zamazal --- crates/scmi/README.md | 1 + crates/scmi/src/devices.rs | 97 ++++++++++++++++++++++++++++++++++++- crates/scmi/src/main.rs | 6 ++- crates/scmi/src/scmi.rs | 3 ++ crates/scmi/src/vhu_scmi.rs | 4 ++ 5 files changed, 108 insertions(+), 3 deletions(-) diff --git a/crates/scmi/README.md b/crates/scmi/README.md index b7e3dcd..1e347d7 100644 --- a/crates/scmi/README.md +++ b/crates/scmi/README.md @@ -30,6 +30,7 @@ the Examples section below. Can be used multiple times for multiple exposed devices. If no device is specified then no device will be provided to the guest OS but VirtIO SCMI will be still available there. + Use `help` as the device ID to list help on all the available devices. You can set `RUST_LOG` environment variable to `debug` to get maximum messages on the standard error output. diff --git a/crates/scmi/src/devices.rs b/crates/scmi/src/devices.rs index be86507..50b8d14 100644 --- a/crates/scmi/src/devices.rs +++ b/crates/scmi/src/devices.rs @@ -1,9 +1,10 @@ // SPDX-FileCopyrightText: Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::collections::HashMap; - use log::debug; +use std::collections::HashMap; +use std::fmt::Write; +use std::process::exit; use crate::{ scmi::{ @@ -15,6 +16,10 @@ use crate::{ DeviceProperties, }; +enum ExitCodes { + Help = 1, +} + type DeviceSpecification = fn() -> Box; type NameDeviceMapping = HashMap; @@ -24,6 +29,40 @@ pub fn available_devices() -> NameDeviceMapping { devices } +fn devices_help() -> String { + let mut help = String::new(); + writeln!(help, "Available devices:").unwrap(); + for (name, constructor) in available_devices().iter() { + let device = constructor(); + let short_help = device.short_help(); + let long_help = device.long_help(); + let parameters_help = device.parameters_help(); + writeln!(help, "\n- {name}: {short_help}").unwrap(); + for line in long_help.lines() { + writeln!(help, " {line}").unwrap(); + } + if !parameters_help.is_empty() { + writeln!(help, " Parameters:").unwrap(); + for parameter in parameters_help { + writeln!(help, " - {parameter}").unwrap(); + } + } + } + writeln!(help, "\nDevice specification example:").unwrap(); + writeln!( + help, + "--device iio,path=/sys/bus/iio/devices/iio:device0,channel=in_accel" + ) + .unwrap(); + help +} + +pub(crate) fn print_devices_help() { + let help = devices_help(); + println!("{}", help); + exit(ExitCodes::Help as i32); +} + // Common sensor infrastructure pub struct Sensor { @@ -44,6 +83,18 @@ trait SensorT: Send { fn sensor(&self) -> &Sensor; fn sensor_mut(&mut self) -> &mut Sensor; + fn short_help(&self) -> String { + "sensor".to_owned() + } + + fn long_help(&self) -> String { + "".to_owned() + } + + fn parameters_help(&self) -> Vec { + vec!["name: an optional name of the sensor, max. 15 characters".to_owned()] + } + fn protocol(&self) -> ProtocolId { SENSOR_PROTOCOL_ID } @@ -182,6 +233,18 @@ impl ScmiDevice for SensorDevice { self.0.configure(properties) } + fn short_help(&self) -> String { + self.0.short_help() + } + + fn long_help(&self) -> String { + self.0.long_help() + } + + fn parameters_help(&self) -> Vec { + self.0.parameters_help() + } + fn protocol(&self) -> ProtocolId { self.0.protocol() } @@ -207,6 +270,14 @@ impl SensorT for FakeSensor { &mut self.sensor } + fn short_help(&self) -> String { + "fake accelerometer".to_owned() + } + + fn long_help(&self) -> String { + "A simple 3-axes sensor providing fake pre-defined values.".to_owned() + } + fn number_of_axes(&self) -> u32 { 3 } @@ -245,3 +316,25 @@ impl FakeSensor { Box::new(sensor_device) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_help() { + let help = devices_help(); + assert!( + help.contains("Available devices:\n"), + "global label missing" + ); + assert!(help.contains("fake:"), "sensor name missing"); + assert!( + help.contains("fake accelerometer"), + "short description missing" + ); + assert!(help.contains("3-axes sensor"), "long description missing"); + assert!(help.contains("Parameters:\n"), "parameter label missing"); + assert!(help.contains("- name:"), "parameter `name' missing"); + } +} diff --git a/crates/scmi/src/main.rs b/crates/scmi/src/main.rs index c9e176e..bc91842 100644 --- a/crates/scmi/src/main.rs +++ b/crates/scmi/src/main.rs @@ -29,7 +29,11 @@ struct ScmiArgs { #[clap(short, long, help = "vhost-user socket to use")] socket_path: String, // Specification of SCMI devices to create. - #[clap(short, long)] + #[clap( + short, + long, + help = "Devices to expose (use `help' device for more info)" + )] #[arg(num_args(1..))] device: Vec, } diff --git a/crates/scmi/src/scmi.rs b/crates/scmi/src/scmi.rs index d4b982b..87534dc 100644 --- a/crates/scmi/src/scmi.rs +++ b/crates/scmi/src/scmi.rs @@ -480,6 +480,9 @@ pub enum ScmiDeviceError { } pub trait ScmiDevice: Send { + fn short_help(&self) -> String; + fn long_help(&self) -> String; + fn parameters_help(&self) -> Vec; fn configure(&mut self, properties: &DeviceProperties) -> Result<(), String>; fn protocol(&self) -> ProtocolId; fn handle( diff --git a/crates/scmi/src/vhu_scmi.rs b/crates/scmi/src/vhu_scmi.rs index 2049383..47fc1e3 100644 --- a/crates/scmi/src/vhu_scmi.rs +++ b/crates/scmi/src/vhu_scmi.rs @@ -21,6 +21,7 @@ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; use crate::devices::available_devices; +use crate::devices::print_devices_help; use crate::scmi::ScmiDevice; use crate::scmi::{MessageHeader, ScmiHandler, ScmiRequest}; use crate::VuScmiConfig; @@ -99,6 +100,9 @@ impl VuScmiBackend { let mut handler = ScmiHandler::new(); let device_mapping = available_devices(); for (name, properties) in config.devices.iter() { + if name == "help" { + print_devices_help(); + } match device_mapping.get(name) { Some(constructor) => { let mut device: Box = constructor(); From ca8f181bcdc22917a8bf7e81bbeddca82d32dbfb Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Mon, 31 Jul 2023 16:17:10 +0200 Subject: [PATCH 143/189] scmi: Refactor device specification and creation Making the device configuration polymorphic requires the device struct to exist before the device parameters are checked and assigned to the struct fields. Which means wrapping the struct fields by Option unnecessarily or introducing other data confusion. Let's extract the device configuration from traits to plain functions in order to keep the device struct's unencumbered. Signed-off-by: Milan Zamazal --- crates/scmi/src/devices.rs | 254 +++++++++++++++++++++++++----------- crates/scmi/src/main.rs | 18 ++- crates/scmi/src/scmi.rs | 13 +- crates/scmi/src/vhu_scmi.rs | 21 ++- 4 files changed, 199 insertions(+), 107 deletions(-) diff --git a/crates/scmi/src/devices.rs b/crates/scmi/src/devices.rs index 50b8d14..3ec35da 100644 --- a/crates/scmi/src/devices.rs +++ b/crates/scmi/src/devices.rs @@ -1,42 +1,147 @@ // SPDX-FileCopyrightText: Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 -use log::debug; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::fmt::Write; use std::process::exit; -use crate::{ - scmi::{ - DeviceResult, MessageId, MessageValue, MessageValues, ProtocolId, ScmiDevice, - ScmiDeviceError, MAX_SIMPLE_STRING_LENGTH, SENSOR_AXIS_DESCRIPTION_GET, SENSOR_CONFIG_GET, - SENSOR_CONFIG_SET, SENSOR_CONTINUOUS_UPDATE_NOTIFY, SENSOR_DESCRIPTION_GET, - SENSOR_PROTOCOL_ID, SENSOR_READING_GET, SENSOR_UNIT_METERS_PER_SECOND_SQUARED, - }, - DeviceProperties, +use itertools::Itertools; +use log::debug; +use thiserror::Error as ThisError; + +use crate::scmi::{ + DeviceResult, MessageId, MessageValue, MessageValues, ProtocolId, ScmiDevice, ScmiDeviceError, + MAX_SIMPLE_STRING_LENGTH, SENSOR_AXIS_DESCRIPTION_GET, SENSOR_CONFIG_GET, SENSOR_CONFIG_SET, + SENSOR_CONTINUOUS_UPDATE_NOTIFY, SENSOR_DESCRIPTION_GET, SENSOR_PROTOCOL_ID, + SENSOR_READING_GET, SENSOR_UNIT_METERS_PER_SECOND_SQUARED, }; enum ExitCodes { Help = 1, } -type DeviceSpecification = fn() -> Box; -type NameDeviceMapping = HashMap; +#[derive(Debug, PartialEq, Eq, ThisError)] +pub enum DeviceError { + #[error("Invalid device parameter: {0}")] + InvalidProperty(String), + #[error("Missing device parameters: {}", .0.join(", "))] + MissingDeviceProperties(Vec), + #[error("Unexpected device parameters: {}", .0.join(", "))] + UnexpectedDeviceProperties(Vec), +} + +// [(NAME, [(PROPERTY, VALUE), ...]), ...] +pub type DeviceDescription = Vec<(String, DeviceProperties)>; +type PropertyPairs = Vec<(String, String)>; + +#[derive(Debug, Eq, PartialEq, Hash)] +pub struct DeviceProperties(PropertyPairs); + +impl DeviceProperties { + pub(crate) fn new(properties: PropertyPairs) -> Self { + Self(properties) + } + + fn get(&self, name: &str) -> Option<&str> { + self.0 + .iter() + .find(|(n, _)| n == name) + .map(|(_, v)| v.as_str()) + } + + fn names(&self) -> HashSet<&str> { + self.0.iter().map(|(n, _)| -> &str { n.as_str() }).collect() + } + + fn extra<'a>(&'a self, allowed: &[&'a str]) -> HashSet<&str> { + let allowed_set: HashSet<&str> = HashSet::from_iter(allowed.iter().copied()); + self.names().difference(&allowed_set).copied().collect() + } + + fn missing<'a>(&'a self, required: &[&'a str]) -> HashSet<&str> { + let required_set: HashSet<&str> = HashSet::from_iter(required.iter().copied()); + required_set.difference(&self.names()).copied().collect() + } + + fn check(&self, required: &[&str], optional: &[&str]) -> Result<(), DeviceError> { + let missing = self.missing(required); + if !missing.is_empty() { + return Err(DeviceError::MissingDeviceProperties( + missing + .iter() + .sorted() + .map(|s| (*s).to_owned()) + .collect::>(), + )); + } + let mut all_allowed = Vec::from(required); + all_allowed.extend(optional.iter()); + let extra = self.extra(&all_allowed); + if !extra.is_empty() { + return Err(DeviceError::UnexpectedDeviceProperties( + extra + .iter() + .sorted() + .map(|s| (*s).to_owned()) + .collect::>(), + )); + } + Ok(()) + } +} + +type MaybeDevice = Result, DeviceError>; +type DeviceConstructor = fn(&DeviceProperties) -> MaybeDevice; + +pub struct DeviceSpecification { + pub(crate) constructor: DeviceConstructor, + short_help: String, + long_help: String, + parameters_help: Vec, +} + +impl DeviceSpecification { + fn new( + constructor: DeviceConstructor, + short_help: &str, + long_help: &str, + parameters_help: &[&str], + ) -> Self { + Self { + constructor, + short_help: short_help.to_owned(), + long_help: long_help.to_owned(), + parameters_help: parameters_help + .iter() + .map(|s| String::from(*s)) + .collect::>(), + } + } +} + +type NameDeviceMapping = HashMap<&'static str, DeviceSpecification>; pub fn available_devices() -> NameDeviceMapping { let mut devices: NameDeviceMapping = HashMap::new(); - devices.insert("fake".to_owned(), FakeSensor::new); + devices.insert( + "fake", + DeviceSpecification::new( + FakeSensor::new, + "fake accelerometer", + "A simple 3-axes sensor providing fake pre-defined values.", + &["name: an optional name of the sensor, max. 15 characters"], + ), + ); devices } fn devices_help() -> String { let mut help = String::new(); writeln!(help, "Available devices:").unwrap(); - for (name, constructor) in available_devices().iter() { - let device = constructor(); - let short_help = device.short_help(); - let long_help = device.long_help(); - let parameters_help = device.parameters_help(); + for (name, specification) in available_devices().iter() { + let short_help = &specification.short_help; + let long_help = &specification.long_help; + let parameters_help = &specification.parameters_help; writeln!(help, "\n- {name}: {short_help}").unwrap(); for line in long_help.lines() { writeln!(help, " {line}").unwrap(); @@ -57,7 +162,7 @@ fn devices_help() -> String { help } -pub(crate) fn print_devices_help() { +pub fn print_devices_help() { let help = devices_help(); println!("{}", help); exit(ExitCodes::Help as i32); @@ -71,9 +176,10 @@ pub struct Sensor { } impl Sensor { - const fn new(default_name: String) -> Self { + fn new(properties: &DeviceProperties, default_name: &str) -> Self { + let name = properties.get("name").unwrap_or(default_name); Self { - name: default_name, + name: name.to_owned(), enabled: false, } } @@ -83,42 +189,18 @@ trait SensorT: Send { fn sensor(&self) -> &Sensor; fn sensor_mut(&mut self) -> &mut Sensor; - fn short_help(&self) -> String { - "sensor".to_owned() - } - - fn long_help(&self) -> String { - "".to_owned() - } - - fn parameters_help(&self) -> Vec { - vec!["name: an optional name of the sensor, max. 15 characters".to_owned()] - } - fn protocol(&self) -> ProtocolId { SENSOR_PROTOCOL_ID } - fn invalid_property(&self, name: &String) -> Result<(), String> { - Result::Err(format!("Invalid device option: {name}")) + fn invalid_property(&self, name: &str) -> Result<(), DeviceError> { + Result::Err(DeviceError::InvalidProperty(name.to_owned())) } - fn process_property(&mut self, name: &String, _value: &str) -> Result<(), String> { + fn process_property(&mut self, name: &str, _value: &str) -> Result<(), DeviceError> { self.invalid_property(name) } - fn configure(&mut self, properties: &DeviceProperties) -> Result<(), String> { - for (name, value) in properties { - if name == "name" { - // TODO: Check for duplicate names - self.sensor_mut().name = String::from(value); - } else { - self.process_property(name, value)?; - } - } - Ok(()) - } - fn number_of_axes(&self) -> u32 { 1 } @@ -229,22 +311,6 @@ trait SensorT: Send { struct SensorDevice(Box); impl ScmiDevice for SensorDevice { - fn configure(&mut self, properties: &DeviceProperties) -> Result<(), String> { - self.0.configure(properties) - } - - fn short_help(&self) -> String { - self.0.short_help() - } - - fn long_help(&self) -> String { - self.0.long_help() - } - - fn parameters_help(&self) -> Vec { - self.0.parameters_help() - } - fn protocol(&self) -> ProtocolId { self.0.protocol() } @@ -270,14 +336,6 @@ impl SensorT for FakeSensor { &mut self.sensor } - fn short_help(&self) -> String { - "fake accelerometer".to_owned() - } - - fn long_help(&self) -> String { - "A simple 3-axes sensor providing fake pre-defined values.".to_owned() - } - fn number_of_axes(&self) -> u32 { 3 } @@ -309,16 +367,19 @@ impl SensorT for FakeSensor { impl FakeSensor { #[allow(clippy::new_ret_no_self)] - pub fn new() -> Box { - let sensor = Sensor::new("fake".to_owned()); + pub fn new(properties: &DeviceProperties) -> MaybeDevice { + properties.check(&[], &["name"])?; + let sensor = Sensor::new(properties, "fake"); let fake_sensor = Self { sensor, value: 0 }; let sensor_device = SensorDevice(Box::new(fake_sensor)); - Box::new(sensor_device) + Ok(Box::new(sensor_device)) } } #[cfg(test)] mod tests { + use std::assert_eq; + use super::*; #[test] @@ -337,4 +398,47 @@ mod tests { assert!(help.contains("Parameters:\n"), "parameter label missing"); assert!(help.contains("- name:"), "parameter `name' missing"); } + + fn device_properties() -> DeviceProperties { + DeviceProperties::new(vec![ + ("foo".to_owned(), "val1".to_owned()), + ("def".to_owned(), "val2".to_owned()), + ("bar".to_owned(), "val3".to_owned()), + ]) + } + + #[test] + fn test_device_properties() { + let properties = device_properties(); + assert_eq!(properties.get("bar"), Some("val3")); + assert_eq!(properties.get("baz"), None); + assert_eq!(properties.names(), HashSet::from(["foo", "def", "bar"])); + let expected = ["abc", "def", "ghi"]; + let missing = properties.missing(&expected); + assert_eq!(missing, HashSet::from(["abc", "ghi"])); + let extra = properties.extra(&expected); + assert_eq!(extra, HashSet::from(["foo", "bar"])); + } + + #[test] + fn test_check_device_properties() { + let properties = device_properties(); + let result = properties.check(&["abc", "def", "ghi"], &["foo", "baz"]); + assert_eq!( + result, + Err(DeviceError::MissingDeviceProperties(vec![ + "abc".to_owned(), + "ghi".to_owned() + ])) + ); + let result = properties.check(&["def"], &["foo", "baz"]); + assert_eq!( + result, + Err(DeviceError::UnexpectedDeviceProperties(vec![ + "bar".to_owned() + ])) + ); + let result = properties.check(&["def"], &["foo", "bar"]); + assert_eq!(result, Ok(())); + } } diff --git a/crates/scmi/src/main.rs b/crates/scmi/src/main.rs index bc91842..0471983 100644 --- a/crates/scmi/src/main.rs +++ b/crates/scmi/src/main.rs @@ -6,6 +6,8 @@ mod devices; mod scmi; mod vhu_scmi; +use devices::{DeviceDescription, DeviceProperties}; + use std::{ process::exit, sync::{Arc, RwLock}, @@ -38,10 +40,6 @@ struct ScmiArgs { device: Vec, } -// [(NAME, [(PROPERTY, VALUE), ...]), ...] -type DeviceDescription = Vec<(String, DeviceProperties)>; -type DeviceProperties = Vec<(String, String)>; - pub struct VuScmiConfig { socket_path: String, devices: DeviceDescription, @@ -57,7 +55,7 @@ impl TryFrom for VuScmiConfig { for d in device_iterator { let mut split = d.split(','); let name = split.next().unwrap().to_owned(); - let mut properties: DeviceProperties = vec![]; + let mut properties = vec![]; for s in split { if let Some((key, value)) = s.split('=').collect_tuple() { properties.push((key.to_owned(), value.to_owned())); @@ -65,7 +63,7 @@ impl TryFrom for VuScmiConfig { return Result::Err(format!("Invalid device {name} property format: {s}")); } } - devices.push((name, properties)); + devices.push((name, DeviceProperties::new(properties))); } Ok(Self { socket_path, @@ -146,17 +144,17 @@ mod tests { let config = VuScmiConfig::try_from(args).unwrap(); assert_eq!(config.socket_path, path); let devices = vec![ - ("dummy".to_owned(), vec![]), + ("dummy".to_owned(), DeviceProperties::new(vec![])), ( "fake".to_owned(), - vec![ + DeviceProperties::new(vec![ ("name".to_owned(), "foo".to_owned()), ("prop".to_owned(), "value".to_owned()), - ], + ]), ), ( "fake".to_owned(), - vec![("name".to_owned(), "bar".to_owned())], + DeviceProperties::new(vec![("name".to_owned(), "bar".to_owned())]), ), ]; assert_eq!(config.devices, devices); diff --git a/crates/scmi/src/scmi.rs b/crates/scmi/src/scmi.rs index 87534dc..ae7c5a0 100644 --- a/crates/scmi/src/scmi.rs +++ b/crates/scmi/src/scmi.rs @@ -11,8 +11,6 @@ use itertools::Itertools; use log::{debug, error, info}; use thiserror::Error as ThisError; -use crate::DeviceProperties; - pub type MessageHeader = u32; pub const MAX_SIMPLE_STRING_LENGTH: usize = 16; // incl. NULL terminator @@ -480,10 +478,6 @@ pub enum ScmiDeviceError { } pub trait ScmiDevice: Send { - fn short_help(&self) -> String; - fn long_help(&self) -> String; - fn parameters_help(&self) -> Vec; - fn configure(&mut self, properties: &DeviceProperties) -> Result<(), String>; fn protocol(&self) -> ProtocolId; fn handle( &mut self, @@ -722,7 +716,7 @@ impl ScmiHandler { #[cfg(test)] mod tests { - use crate::devices::FakeSensor; + use crate::devices::{DeviceProperties, FakeSensor}; use super::*; @@ -866,9 +860,8 @@ mod tests { fn make_handler() -> ScmiHandler { let mut handler = ScmiHandler::new(); for i in 0..2 { - let properties = vec![("name".to_owned(), format!("fake{i}"))]; - let mut fake_sensor = FakeSensor::new(); - fake_sensor.configure(&properties).unwrap(); + let properties = DeviceProperties::new(vec![("name".to_owned(), format!("fake{i}"))]); + let fake_sensor = FakeSensor::new(&properties).unwrap(); handler.register_device(fake_sensor); } handler diff --git a/crates/scmi/src/vhu_scmi.rs b/crates/scmi/src/vhu_scmi.rs index 47fc1e3..7821c3d 100644 --- a/crates/scmi/src/vhu_scmi.rs +++ b/crates/scmi/src/vhu_scmi.rs @@ -20,9 +20,7 @@ use vm_memory::{ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; -use crate::devices::available_devices; -use crate::devices::print_devices_help; -use crate::scmi::ScmiDevice; +use crate::devices::{available_devices, print_devices_help, DeviceError}; use crate::scmi::{MessageHeader, ScmiHandler, ScmiRequest}; use crate::VuScmiConfig; @@ -45,7 +43,7 @@ pub enum VuScmiError { #[error("Descriptor write failed")] DescriptorWriteFailed, #[error("Error when configuring device {0}: {1}")] - DeviceConfigurationError(String, String), + DeviceConfigurationError(String, DeviceError), #[error("Failed to create new EventFd")] EventFdFailed, #[error("Failed to handle event, didn't match EPOLLIN")] @@ -103,17 +101,16 @@ impl VuScmiBackend { if name == "help" { print_devices_help(); } - match device_mapping.get(name) { - Some(constructor) => { - let mut device: Box = constructor(); - if let Err(message) = device.configure(properties) { + match device_mapping.get(name.as_str()) { + Some(specification) => match (specification.constructor)(properties) { + Ok(device) => handler.register_device(device), + Err(error) => { return Result::Err(VuScmiError::DeviceConfigurationError( name.clone(), - message, - )); + error, + )) } - handler.register_device(device); - } + }, None => return Result::Err(VuScmiError::UnknownDeviceRequested(name.clone())), }; } From 58ddb26d5b514d312f752709e53638e991593119 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Wed, 2 Aug 2023 19:16:52 +0200 Subject: [PATCH 144/189] scmi: Use separate modules for device implementations For a better readability once we start adding more devices. Signed-off-by: Milan Zamazal --- .../src/{devices.rs => devices/common.rs} | 76 +++---------------- crates/scmi/src/devices/fake.rs | 62 +++++++++++++++ crates/scmi/src/devices/mod.rs | 5 ++ crates/scmi/src/main.rs | 2 +- crates/scmi/src/scmi.rs | 2 +- crates/scmi/src/vhu_scmi.rs | 4 +- 6 files changed, 82 insertions(+), 69 deletions(-) rename crates/scmi/src/{devices.rs => devices/common.rs} (85%) create mode 100644 crates/scmi/src/devices/fake.rs create mode 100644 crates/scmi/src/devices/mod.rs diff --git a/crates/scmi/src/devices.rs b/crates/scmi/src/devices/common.rs similarity index 85% rename from crates/scmi/src/devices.rs rename to crates/scmi/src/devices/common.rs index 3ec35da..b93fa85 100644 --- a/crates/scmi/src/devices.rs +++ b/crates/scmi/src/devices/common.rs @@ -13,9 +13,11 @@ use crate::scmi::{ DeviceResult, MessageId, MessageValue, MessageValues, ProtocolId, ScmiDevice, ScmiDeviceError, MAX_SIMPLE_STRING_LENGTH, SENSOR_AXIS_DESCRIPTION_GET, SENSOR_CONFIG_GET, SENSOR_CONFIG_SET, SENSOR_CONTINUOUS_UPDATE_NOTIFY, SENSOR_DESCRIPTION_GET, SENSOR_PROTOCOL_ID, - SENSOR_READING_GET, SENSOR_UNIT_METERS_PER_SECOND_SQUARED, + SENSOR_READING_GET, }; +use super::fake; + enum ExitCodes { Help = 1, } @@ -42,7 +44,7 @@ impl DeviceProperties { Self(properties) } - fn get(&self, name: &str) -> Option<&str> { + pub(crate) fn get(&self, name: &str) -> Option<&str> { self.0 .iter() .find(|(n, _)| n == name) @@ -63,7 +65,7 @@ impl DeviceProperties { required_set.difference(&self.names()).copied().collect() } - fn check(&self, required: &[&str], optional: &[&str]) -> Result<(), DeviceError> { + pub(crate) fn check(&self, required: &[&str], optional: &[&str]) -> Result<(), DeviceError> { let missing = self.missing(required); if !missing.is_empty() { return Err(DeviceError::MissingDeviceProperties( @@ -90,7 +92,7 @@ impl DeviceProperties { } } -type MaybeDevice = Result, DeviceError>; +pub type MaybeDevice = Result, DeviceError>; type DeviceConstructor = fn(&DeviceProperties) -> MaybeDevice; pub struct DeviceSpecification { @@ -126,7 +128,7 @@ pub fn available_devices() -> NameDeviceMapping { devices.insert( "fake", DeviceSpecification::new( - FakeSensor::new, + fake::FakeSensor::new, "fake accelerometer", "A simple 3-axes sensor providing fake pre-defined values.", &["name: an optional name of the sensor, max. 15 characters"], @@ -171,12 +173,12 @@ pub fn print_devices_help() { // Common sensor infrastructure pub struct Sensor { - name: String, + pub name: String, enabled: bool, } impl Sensor { - fn new(properties: &DeviceProperties, default_name: &str) -> Self { + pub fn new(properties: &DeviceProperties, default_name: &str) -> Self { let name = properties.get("name").unwrap_or(default_name); Self { name: name.to_owned(), @@ -185,7 +187,7 @@ impl Sensor { } } -trait SensorT: Send { +pub trait SensorT: Send { fn sensor(&self) -> &Sensor; fn sensor_mut(&mut self) -> &mut Sensor; @@ -308,7 +310,7 @@ trait SensorT: Send { // It's possible to impl ScmiDevice for SensorT but it is not very useful // because it doesn't allow to pass SensorT as ScmiDevice directly. // Hence this wrapper. -struct SensorDevice(Box); +pub struct SensorDevice(pub(crate) Box); impl ScmiDevice for SensorDevice { fn protocol(&self) -> ProtocolId { @@ -320,62 +322,6 @@ impl ScmiDevice for SensorDevice { } } -// Particular sensor implementations - -pub struct FakeSensor { - sensor: Sensor, - value: u8, -} - -impl SensorT for FakeSensor { - // TODO: Define a macro for this boilerplate? - fn sensor(&self) -> &Sensor { - &self.sensor - } - fn sensor_mut(&mut self) -> &mut Sensor { - &mut self.sensor - } - - fn number_of_axes(&self) -> u32 { - 3 - } - - fn axis_unit(&self) -> u32 { - // The sensor type is "Meters per second squared", since this is the - // only, together with "Radians per second", what Google Linux IIO - // supports (accelerometers and gyroscopes only). - SENSOR_UNIT_METERS_PER_SECOND_SQUARED - } - - fn axis_name_prefix(&self) -> String { - "acc".to_owned() - } - - fn reading_get(&mut self) -> DeviceResult { - let value = self.value; - self.value = self.value.overflowing_add(1).0; - let mut result = vec![]; - for i in 0..3 { - result.push(MessageValue::Unsigned(u32::from(value) + 100 * i)); - result.push(MessageValue::Unsigned(0)); - result.push(MessageValue::Unsigned(0)); - result.push(MessageValue::Unsigned(0)); - } - Ok(result) - } -} - -impl FakeSensor { - #[allow(clippy::new_ret_no_self)] - pub fn new(properties: &DeviceProperties) -> MaybeDevice { - properties.check(&[], &["name"])?; - let sensor = Sensor::new(properties, "fake"); - let fake_sensor = Self { sensor, value: 0 }; - let sensor_device = SensorDevice(Box::new(fake_sensor)); - Ok(Box::new(sensor_device)) - } -} - #[cfg(test)] mod tests { use std::assert_eq; diff --git a/crates/scmi/src/devices/fake.rs b/crates/scmi/src/devices/fake.rs new file mode 100644 index 0000000..cc47d63 --- /dev/null +++ b/crates/scmi/src/devices/fake.rs @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: Red Hat, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Fake sensor + +use crate::scmi::{DeviceResult, MessageValue, SENSOR_UNIT_METERS_PER_SECOND_SQUARED}; + +use super::common::{DeviceProperties, MaybeDevice, Sensor, SensorDevice, SensorT}; + +pub struct FakeSensor { + sensor: Sensor, + value: u8, +} + +impl SensorT for FakeSensor { + // TODO: Define a macro for this boilerplate? + fn sensor(&self) -> &Sensor { + &self.sensor + } + fn sensor_mut(&mut self) -> &mut Sensor { + &mut self.sensor + } + + fn number_of_axes(&self) -> u32 { + 3 + } + + fn axis_unit(&self) -> u32 { + // The sensor type is "Meters per second squared", since this is the + // only, together with "Radians per second", what Google Linux IIO + // supports (accelerometers and gyroscopes only). + SENSOR_UNIT_METERS_PER_SECOND_SQUARED + } + + fn axis_name_prefix(&self) -> String { + "acc".to_owned() + } + + fn reading_get(&mut self) -> DeviceResult { + let value = self.value; + self.value = self.value.overflowing_add(1).0; + let mut result = vec![]; + for i in 0..3 { + result.push(MessageValue::Unsigned(u32::from(value) + 100 * i)); + result.push(MessageValue::Unsigned(0)); + result.push(MessageValue::Unsigned(0)); + result.push(MessageValue::Unsigned(0)); + } + Ok(result) + } +} + +impl FakeSensor { + #[allow(clippy::new_ret_no_self)] + pub fn new(properties: &DeviceProperties) -> MaybeDevice { + properties.check(&[], &["name"])?; + let sensor = Sensor::new(properties, "fake"); + let fake_sensor = Self { sensor, value: 0 }; + let sensor_device = SensorDevice(Box::new(fake_sensor)); + Ok(Box::new(sensor_device)) + } +} diff --git a/crates/scmi/src/devices/mod.rs b/crates/scmi/src/devices/mod.rs new file mode 100644 index 0000000..a118d1b --- /dev/null +++ b/crates/scmi/src/devices/mod.rs @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: Red Hat, Inc. +// SPDX-License-Identifier: Apache-2.0 + +pub mod common; +pub mod fake; diff --git a/crates/scmi/src/main.rs b/crates/scmi/src/main.rs index 0471983..b61281b 100644 --- a/crates/scmi/src/main.rs +++ b/crates/scmi/src/main.rs @@ -6,7 +6,7 @@ mod devices; mod scmi; mod vhu_scmi; -use devices::{DeviceDescription, DeviceProperties}; +use devices::common::{DeviceDescription, DeviceProperties}; use std::{ process::exit, diff --git a/crates/scmi/src/scmi.rs b/crates/scmi/src/scmi.rs index ae7c5a0..616e4cd 100644 --- a/crates/scmi/src/scmi.rs +++ b/crates/scmi/src/scmi.rs @@ -716,7 +716,7 @@ impl ScmiHandler { #[cfg(test)] mod tests { - use crate::devices::{DeviceProperties, FakeSensor}; + use crate::devices::{common::DeviceProperties, fake::FakeSensor}; use super::*; diff --git a/crates/scmi/src/vhu_scmi.rs b/crates/scmi/src/vhu_scmi.rs index 7821c3d..b993fe7 100644 --- a/crates/scmi/src/vhu_scmi.rs +++ b/crates/scmi/src/vhu_scmi.rs @@ -20,7 +20,7 @@ use vm_memory::{ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; -use crate::devices::{available_devices, print_devices_help, DeviceError}; +use crate::devices::common::{available_devices, print_devices_help, DeviceError}; use crate::scmi::{MessageHeader, ScmiHandler, ScmiRequest}; use crate::VuScmiConfig; @@ -108,7 +108,7 @@ impl VuScmiBackend { return Result::Err(VuScmiError::DeviceConfigurationError( name.clone(), error, - )) + )); } }, None => return Result::Err(VuScmiError::UnknownDeviceRequested(name.clone())), From cbb0449d433a36f0d22045f9324bc214bb8639d2 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Sat, 5 Aug 2023 17:38:31 +0200 Subject: [PATCH 145/189] scmi: Refactor unit handling Make unit handling better SCMI compliant. Let's distinguish between scalar sensors and axis sensors and report the units in the appropriate SCMI commands. Also, let's change the unit type to u8 to correspond to the number of unit bits in SCMI. Signed-off-by: Milan Zamazal --- crates/scmi/src/devices/common.rs | 29 ++++++++++++++++++++++------- crates/scmi/src/devices/fake.rs | 6 +++--- crates/scmi/src/scmi.rs | 12 +++++++++--- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/crates/scmi/src/devices/common.rs b/crates/scmi/src/devices/common.rs index b93fa85..ac3a80a 100644 --- a/crates/scmi/src/devices/common.rs +++ b/crates/scmi/src/devices/common.rs @@ -10,9 +10,9 @@ use log::debug; use thiserror::Error as ThisError; use crate::scmi::{ - DeviceResult, MessageId, MessageValue, MessageValues, ProtocolId, ScmiDevice, ScmiDeviceError, - MAX_SIMPLE_STRING_LENGTH, SENSOR_AXIS_DESCRIPTION_GET, SENSOR_CONFIG_GET, SENSOR_CONFIG_SET, - SENSOR_CONTINUOUS_UPDATE_NOTIFY, SENSOR_DESCRIPTION_GET, SENSOR_PROTOCOL_ID, + self, DeviceResult, MessageId, MessageValue, MessageValues, ProtocolId, ScmiDevice, + ScmiDeviceError, MAX_SIMPLE_STRING_LENGTH, SENSOR_AXIS_DESCRIPTION_GET, SENSOR_CONFIG_GET, + SENSOR_CONFIG_SET, SENSOR_CONTINUOUS_UPDATE_NOTIFY, SENSOR_DESCRIPTION_GET, SENSOR_PROTOCOL_ID, SENSOR_READING_GET, }; @@ -204,13 +204,22 @@ pub trait SensorT: Send { } fn number_of_axes(&self) -> u32 { - 1 + 0 + } + + fn format_unit(&self, axis: u32) -> u32 { + (self.unit_exponent(axis) as u32 & 0x1F) << 11 | u32::from(self.unit()) } fn description_get(&self) -> DeviceResult { // Continuous update required by Linux SCMI IIO driver let low = 1 << 30; - let high = self.number_of_axes() << 16 | 1 << 8; + let n_axes = self.number_of_axes(); + let high = if n_axes > 0 { + n_axes << 16 | 1 << 8 + } else { + self.format_unit(0) + }; let name = self.sensor().name.clone(); let values: MessageValues = vec![ // attributes low @@ -223,7 +232,13 @@ pub trait SensorT: Send { Ok(values) } - fn axis_unit(&self) -> u32; + fn unit(&self) -> u8 { + scmi::SENSOR_UNIT_UNSPECIFIED + } + + fn unit_exponent(&self, _axis: u32) -> i8 { + 0 + } fn axis_name_prefix(&self) -> String { "axis".to_owned() @@ -242,7 +257,7 @@ pub trait SensorT: Send { let mut values = vec![]; values.push(MessageValue::Unsigned(axis)); // axis id values.push(MessageValue::Unsigned(0)); // attributes low - values.push(MessageValue::Unsigned(self.axis_unit())); // attributes high + values.push(MessageValue::Unsigned(self.format_unit(axis))); // attributes high // Name in the recommended format, 16 bytes: let prefix = self.axis_name_prefix(); diff --git a/crates/scmi/src/devices/fake.rs b/crates/scmi/src/devices/fake.rs index cc47d63..5cde468 100644 --- a/crates/scmi/src/devices/fake.rs +++ b/crates/scmi/src/devices/fake.rs @@ -3,7 +3,7 @@ // Fake sensor -use crate::scmi::{DeviceResult, MessageValue, SENSOR_UNIT_METERS_PER_SECOND_SQUARED}; +use crate::scmi::{self, DeviceResult, MessageValue}; use super::common::{DeviceProperties, MaybeDevice, Sensor, SensorDevice, SensorT}; @@ -25,11 +25,11 @@ impl SensorT for FakeSensor { 3 } - fn axis_unit(&self) -> u32 { + fn unit(&self) -> u8 { // The sensor type is "Meters per second squared", since this is the // only, together with "Radians per second", what Google Linux IIO // supports (accelerometers and gyroscopes only). - SENSOR_UNIT_METERS_PER_SECOND_SQUARED + scmi::SENSOR_UNIT_METERS_PER_SECOND_SQUARED } fn axis_name_prefix(&self) -> String { diff --git a/crates/scmi/src/scmi.rs b/crates/scmi/src/scmi.rs index 616e4cd..82185b6 100644 --- a/crates/scmi/src/scmi.rs +++ b/crates/scmi/src/scmi.rs @@ -216,7 +216,8 @@ pub const SENSOR_CONFIG_GET: MessageId = 0x9; pub const SENSOR_CONFIG_SET: MessageId = 0xA; pub const SENSOR_CONTINUOUS_UPDATE_NOTIFY: MessageId = 0xB; -pub const SENSOR_UNIT_METERS_PER_SECOND_SQUARED: u32 = 89; +pub const SENSOR_UNIT_UNSPECIFIED: u8 = 1; +pub const SENSOR_UNIT_METERS_PER_SECOND_SQUARED: u8 = 89; enum ParameterType { _SignedInt32, @@ -309,7 +310,12 @@ impl HandlerMap { BASE_DISCOVER_VENDOR, "base/discover_vendor", vec![], - |_, _| -> Response { Response::from(MessageValue::String("rust-vmm".to_string(), 16)) }, + |_, _| -> Response { + Response::from(MessageValue::String( + "rust-vmm".to_string(), + MAX_SIMPLE_STRING_LENGTH, + )) + }, ); self.bind( BASE_PROTOCOL_ID, @@ -1144,7 +1150,7 @@ mod tests { let mut description = vec![ MessageValue::Unsigned(i), MessageValue::Unsigned(0), - MessageValue::Unsigned(SENSOR_UNIT_METERS_PER_SECOND_SQUARED), + MessageValue::Unsigned(u32::from(SENSOR_UNIT_METERS_PER_SECOND_SQUARED)), MessageValue::String(name, MAX_SIMPLE_STRING_LENGTH), ]; result.append(&mut description); From 789288c372f01feb7a80d78a6c80a41d4f3f0c5d Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Sat, 5 Aug 2023 17:42:17 +0200 Subject: [PATCH 146/189] scmi: Add sensor device initialization function Implementation accessing real sensors will need to set up the device instance. Signed-off-by: Milan Zamazal --- crates/scmi/src/devices/common.rs | 8 ++++++++ crates/scmi/src/scmi.rs | 3 +++ crates/scmi/src/vhu_scmi.rs | 10 +++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/scmi/src/devices/common.rs b/crates/scmi/src/devices/common.rs index ac3a80a..d5568c8 100644 --- a/crates/scmi/src/devices/common.rs +++ b/crates/scmi/src/devices/common.rs @@ -191,6 +191,10 @@ pub trait SensorT: Send { fn sensor(&self) -> &Sensor; fn sensor_mut(&mut self) -> &mut Sensor; + fn initialize(&mut self) -> Result<(), DeviceError> { + Ok(()) + } + fn protocol(&self) -> ProtocolId { SENSOR_PROTOCOL_ID } @@ -328,6 +332,10 @@ pub trait SensorT: Send { pub struct SensorDevice(pub(crate) Box); impl ScmiDevice for SensorDevice { + fn initialize(&mut self) -> Result<(), DeviceError> { + self.0.initialize() + } + fn protocol(&self) -> ProtocolId { self.0.protocol() } diff --git a/crates/scmi/src/scmi.rs b/crates/scmi/src/scmi.rs index 82185b6..87d6789 100644 --- a/crates/scmi/src/scmi.rs +++ b/crates/scmi/src/scmi.rs @@ -11,6 +11,8 @@ use itertools::Itertools; use log::{debug, error, info}; use thiserror::Error as ThisError; +use crate::devices::common::DeviceError; + pub type MessageHeader = u32; pub const MAX_SIMPLE_STRING_LENGTH: usize = 16; // incl. NULL terminator @@ -484,6 +486,7 @@ pub enum ScmiDeviceError { } pub trait ScmiDevice: Send { + fn initialize(&mut self) -> Result<(), DeviceError>; fn protocol(&self) -> ProtocolId; fn handle( &mut self, diff --git a/crates/scmi/src/vhu_scmi.rs b/crates/scmi/src/vhu_scmi.rs index b993fe7..3a25597 100644 --- a/crates/scmi/src/vhu_scmi.rs +++ b/crates/scmi/src/vhu_scmi.rs @@ -103,7 +103,15 @@ impl VuScmiBackend { } match device_mapping.get(name.as_str()) { Some(specification) => match (specification.constructor)(properties) { - Ok(device) => handler.register_device(device), + Ok(mut device) => { + if let Err(error) = device.initialize() { + return Result::Err(VuScmiError::DeviceConfigurationError( + name.clone(), + error, + )); + } + handler.register_device(device); + } Err(error) => { return Result::Err(VuScmiError::DeviceConfigurationError( name.clone(), From c1637e94b81b2f8ee69a44fc0031daecbd7d5287 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Mon, 7 Aug 2023 20:52:53 +0200 Subject: [PATCH 147/189] scmi: Use new_device for ScmiDevice constructors Instead of `new', which should be reserved for direct constructors. Signed-off-by: Milan Zamazal --- crates/scmi/src/devices/common.rs | 2 +- crates/scmi/src/devices/fake.rs | 3 +-- crates/scmi/src/scmi.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/scmi/src/devices/common.rs b/crates/scmi/src/devices/common.rs index d5568c8..c4db9ca 100644 --- a/crates/scmi/src/devices/common.rs +++ b/crates/scmi/src/devices/common.rs @@ -128,7 +128,7 @@ pub fn available_devices() -> NameDeviceMapping { devices.insert( "fake", DeviceSpecification::new( - fake::FakeSensor::new, + fake::FakeSensor::new_device, "fake accelerometer", "A simple 3-axes sensor providing fake pre-defined values.", &["name: an optional name of the sensor, max. 15 characters"], diff --git a/crates/scmi/src/devices/fake.rs b/crates/scmi/src/devices/fake.rs index 5cde468..bc760b8 100644 --- a/crates/scmi/src/devices/fake.rs +++ b/crates/scmi/src/devices/fake.rs @@ -51,8 +51,7 @@ impl SensorT for FakeSensor { } impl FakeSensor { - #[allow(clippy::new_ret_no_self)] - pub fn new(properties: &DeviceProperties) -> MaybeDevice { + pub fn new_device(properties: &DeviceProperties) -> MaybeDevice { properties.check(&[], &["name"])?; let sensor = Sensor::new(properties, "fake"); let fake_sensor = Self { sensor, value: 0 }; diff --git a/crates/scmi/src/scmi.rs b/crates/scmi/src/scmi.rs index 87d6789..2c525d3 100644 --- a/crates/scmi/src/scmi.rs +++ b/crates/scmi/src/scmi.rs @@ -870,7 +870,7 @@ mod tests { let mut handler = ScmiHandler::new(); for i in 0..2 { let properties = DeviceProperties::new(vec![("name".to_owned(), format!("fake{i}"))]); - let fake_sensor = FakeSensor::new(&properties).unwrap(); + let fake_sensor = FakeSensor::new_device(&properties).unwrap(); handler.register_device(fake_sensor); } handler From 458f168639605216843bf3fdc2bca9020d428b39 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Sat, 5 Aug 2023 17:46:16 +0200 Subject: [PATCH 148/189] scmi: Add support for industrial I/O devices Industrial I/O (IIO) devices are present in /sys/bus/iio/devices/ on Linux. This patch makes them accessible to the guests via the sensor SCMI protocol. The implementation no way covers all the possible IIO devices. It supports some basic stuff, other sensors can be added as needed. Signed-off-by: Milan Zamazal --- crates/scmi/README.md | 3 +- crates/scmi/src/devices/common.rs | 56 ++- crates/scmi/src/devices/iio.rs | 782 ++++++++++++++++++++++++++++++ crates/scmi/src/devices/mod.rs | 1 + crates/scmi/src/scmi.rs | 26 +- crates/scmi/src/vhu_scmi.rs | 158 +++--- 6 files changed, 932 insertions(+), 94 deletions(-) create mode 100644 crates/scmi/src/devices/iio.rs diff --git a/crates/scmi/README.md b/crates/scmi/README.md index 1e347d7..4c65023 100644 --- a/crates/scmi/README.md +++ b/crates/scmi/README.md @@ -26,7 +26,8 @@ the Examples section below. .. option:: -d, --device=SPEC - SCMI device specification in the format `ID,PROPERTY=VALUE,...`. + SCMI device specification in the format `ID,PROPERTY=VALUE,...`. + For example: `-d iio,path=/sys/bus/iio/devices/iio:device0,channel=in_accel`. Can be used multiple times for multiple exposed devices. If no device is specified then no device will be provided to the guest OS but VirtIO SCMI will be still available there. diff --git a/crates/scmi/src/devices/common.rs b/crates/scmi/src/devices/common.rs index c4db9ca..11d4b08 100644 --- a/crates/scmi/src/devices/common.rs +++ b/crates/scmi/src/devices/common.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use std::collections::{HashMap, HashSet}; +use std::ffi::OsString; use std::fmt::Write; use std::process::exit; @@ -16,16 +17,20 @@ use crate::scmi::{ SENSOR_READING_GET, }; -use super::fake; +use super::{fake, iio}; enum ExitCodes { Help = 1, } -#[derive(Debug, PartialEq, Eq, ThisError)] +#[derive(Debug, ThisError)] pub enum DeviceError { + #[error("{0}")] + GenericError(String), #[error("Invalid device parameter: {0}")] InvalidProperty(String), + #[error("I/O error on {0:?}: {1}")] + IOError(OsString, std::io::Error), #[error("Missing device parameters: {}", .0.join(", "))] MissingDeviceProperties(Vec), #[error("Unexpected device parameters: {}", .0.join(", "))] @@ -134,6 +139,19 @@ pub fn available_devices() -> NameDeviceMapping { &["name: an optional name of the sensor, max. 15 characters"], ), ); + devices.insert( + "iio", + DeviceSpecification::new( + iio::IIOSensor::new_device, + "industrial I/O sensor", + "", + &[ + "path: path to the device directory (e.g. /sys/bus/iio/devices/iio:device0)", + "channel: prefix of the device type (e.g. in_accel)", + "name: an optional name of the sensor, max. 15 characters", + ], + ), + ); devices } @@ -172,6 +190,7 @@ pub fn print_devices_help() { // Common sensor infrastructure +#[derive(Debug)] pub struct Sensor { pub name: String, enabled: bool, @@ -392,22 +411,21 @@ mod tests { #[test] fn test_check_device_properties() { let properties = device_properties(); - let result = properties.check(&["abc", "def", "ghi"], &["foo", "baz"]); - assert_eq!( - result, - Err(DeviceError::MissingDeviceProperties(vec![ - "abc".to_owned(), - "ghi".to_owned() - ])) - ); - let result = properties.check(&["def"], &["foo", "baz"]); - assert_eq!( - result, - Err(DeviceError::UnexpectedDeviceProperties(vec![ - "bar".to_owned() - ])) - ); - let result = properties.check(&["def"], &["foo", "bar"]); - assert_eq!(result, Ok(())); + match properties.check(&["abc", "def", "ghi"], &["foo", "baz"]) { + Err(DeviceError::MissingDeviceProperties(missing)) => { + assert_eq!(missing, vec!["abc".to_owned(), "ghi".to_owned()]) + } + other => panic!("Unexpected result: {:?}", other), + } + match properties.check(&["def"], &["foo", "baz"]) { + Err(DeviceError::UnexpectedDeviceProperties(unexpected)) => { + assert_eq!(unexpected, vec!["bar".to_owned()]) + } + other => panic!("Unexpected result: {:?}", other), + } + match properties.check(&["def"], &["foo", "bar"]) { + Ok(()) => (), + other => panic!("Unexpected result: {:?}", other), + } } } diff --git a/crates/scmi/src/devices/iio.rs b/crates/scmi/src/devices/iio.rs new file mode 100644 index 0000000..e5dcfe9 --- /dev/null +++ b/crates/scmi/src/devices/iio.rs @@ -0,0 +1,782 @@ +// SPDX-FileCopyrightText: Red Hat, Inc. +// SPDX-License-Identifier: Apache-2.0 + +// Industrial I/O sensors + +use std::cmp::{max, min}; +use std::ffi::{OsStr, OsString}; +use std::fs; +use std::io::ErrorKind; +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +use log::{debug, error, warn}; + +use crate::scmi::{self, DeviceResult, MessageValue, ScmiDeviceError, MAX_SIMPLE_STRING_LENGTH}; + +use super::common::{DeviceError, DeviceProperties, MaybeDevice, Sensor, SensorDevice, SensorT}; + +struct UnitMapping<'a> { + channel: &'a str, + unit: u8, + unit_exponent: i8, // max. 5 bits actually +} + +// Incomplete, just a sample. +// TODO: Make some macro(s) for this. +const UNIT_MAPPING: &[UnitMapping] = &[ + UnitMapping { + channel: "in_accel", + unit: scmi::SENSOR_UNIT_METERS_PER_SECOND_SQUARED, + unit_exponent: 0, + }, + UnitMapping { + channel: "in_angle", + unit: scmi::SENSOR_UNIT_RADIANS, + unit_exponent: 0, + }, + UnitMapping { + channel: "in_anglevel", + unit: scmi::SENSOR_UNIT_RADIANS_PER_SECOND, + unit_exponent: 0, + }, + UnitMapping { + channel: "in_concentration", + unit: scmi::SENSOR_UNIT_PERCENTAGE, + unit_exponent: 0, + }, + UnitMapping { + channel: "in_current", + unit: scmi::SENSOR_UNIT_AMPS, + unit_exponent: -3, + }, + UnitMapping { + channel: "in_capacitance", + unit: scmi::SENSOR_UNIT_FARADS, + unit_exponent: -9, + }, + UnitMapping { + channel: "in_distance", + unit: scmi::SENSOR_UNIT_METERS, + unit_exponent: 0, + }, + UnitMapping { + channel: "in_electricalconductivity", + unit: scmi::SENSOR_UNIT_SIEMENS, // per meter + unit_exponent: 0, + }, + UnitMapping { + channel: "in_energy", + unit: scmi::SENSOR_UNIT_JOULS, + unit_exponent: 0, + }, + UnitMapping { + channel: "in_gravity", + unit: scmi::SENSOR_UNIT_METERS_PER_SECOND_SQUARED, + unit_exponent: 0, + }, + UnitMapping { + channel: "in_humidityrelative", + unit: scmi::SENSOR_UNIT_PERCENTAGE, + unit_exponent: -3, + }, + UnitMapping { + channel: "in_illuminance", + unit: scmi::SENSOR_UNIT_LUX, + unit_exponent: 0, + }, + UnitMapping { + channel: "in_magn", + unit: scmi::SENSOR_UNIT_GAUSS, + unit_exponent: 0, + }, + UnitMapping { + channel: "in_ph", + unit: scmi::SENSOR_UNIT_UNSPECIFIED, // SCMI doesn't define pH + unit_exponent: -3, + }, + UnitMapping { + channel: "in_positionrelative", + unit: scmi::SENSOR_UNIT_PERCENTAGE, + unit_exponent: -3, + }, + UnitMapping { + channel: "in_power", + unit: scmi::SENSOR_UNIT_WATTS, + unit_exponent: -3, + }, + UnitMapping { + channel: "in_pressure", + unit: scmi::SENSOR_UNIT_PASCALS, + unit_exponent: 3, + }, + UnitMapping { + channel: "in_proximity", + unit: scmi::SENSOR_UNIT_METERS, + unit_exponent: 0, + }, + UnitMapping { + channel: "in_resistance", + unit: scmi::SENSOR_UNIT_OHMS, + unit_exponent: 0, + }, + UnitMapping { + channel: "in_temp", + unit: scmi::SENSOR_UNIT_DEGREES_C, + unit_exponent: -3, + }, + UnitMapping { + channel: "in_velocity_sqrt(x^2+y^2+z^2)", + unit: scmi::SENSOR_UNIT_METERS_PER_SECOND, + unit_exponent: -3, + }, + UnitMapping { + channel: "in_voltage", + unit: scmi::SENSOR_UNIT_VOLTS, + unit_exponent: -3, + }, +]; + +#[derive(PartialEq, Debug)] +struct Axis { + path: OsString, // without "_raw" suffix + unit_exponent: i8, + custom_exponent: i8, +} + +#[derive(Debug)] +pub(crate) struct IIOSensor { + sensor: Sensor, + // Full /sys path to the device directory + path: OsString, + // Prefix of the device type in the device directory, e.g. "in_accel" + channel: OsString, + // Whether the sensor is scalar or has one or more axes + scalar: bool, + // Paths to "_raw" files + axes: Vec, +} + +impl SensorT for IIOSensor { + // TODO: Define a macro for this boilerplate? + fn sensor(&self) -> &Sensor { + &self.sensor + } + fn sensor_mut(&mut self) -> &mut Sensor { + &mut self.sensor + } + + fn initialize(&mut self) -> Result<(), DeviceError> { + let mut axes: Vec = vec![]; + match fs::read_dir(&self.path) { + Ok(iter) => { + for dir_entry in iter { + match dir_entry { + Ok(entry) => self.register_iio_file(entry, &mut axes), + Err(error) => return Err(DeviceError::IOError(self.path.clone(), error)), + } + } + } + Err(error) => return Err(DeviceError::IOError(self.path.clone(), error)), + } + if axes.is_empty() { + return Err(DeviceError::GenericError(format!( + "No {:?} channel found in {:?}", + &self.channel, &self.path + ))); + } + axes.sort_by(|a1, a2| a1.path.cmp(&a2.path)); + self.axes = axes; + Ok(()) + } + + fn unit(&self) -> u8 { + UNIT_MAPPING + .iter() + .find(|mapping| mapping.channel == self.channel) + .map_or(scmi::SENSOR_UNIT_UNSPECIFIED, |mapping| mapping.unit) + } + + fn unit_exponent(&self, axis_index: u32) -> i8 { + let axis: &Axis = self.axes.get(axis_index as usize).unwrap(); + axis.unit_exponent + axis.custom_exponent + } + + fn number_of_axes(&self) -> u32 { + if self.scalar { + 0 + } else { + self.axes.len() as u32 + } + } + + fn axis_name_prefix(&self) -> String { + let channel = self.channel.to_str().unwrap(); + let in_prefix = "in_"; + let out_prefix = "out_"; + let name: &str = if channel.starts_with(in_prefix) { + channel.strip_prefix(in_prefix).unwrap() + } else if channel.starts_with(out_prefix) { + channel.strip_prefix(out_prefix).unwrap() + } else { + channel + }; + let len = min(name.len(), MAX_SIMPLE_STRING_LENGTH - 1); + String::from(&name[..len]) + } + + fn reading_get(&mut self) -> DeviceResult { + let mut result = vec![]; + for axis in &self.axes { + let value = self.read_axis(axis)?; + result.push(MessageValue::Unsigned((value & 0xFFFFFFFF) as u32)); + result.push(MessageValue::Unsigned((value >> 32) as u32)); + result.push(MessageValue::Unsigned(0)); + result.push(MessageValue::Unsigned(0)); + } + Ok(result) + } +} + +fn read_number_from_file(path: &Path) -> Result, ScmiDeviceError> { + match fs::read_to_string(path) { + Ok(string) => match string.trim().parse() { + Ok(value) => Ok(Some(value)), + _ => { + error!( + "Failed to parse IIO numeric value from {}: {string}", + path.display() + ); + Err(ScmiDeviceError::GenericError) + } + }, + Err(error) => match error.kind() { + ErrorKind::NotFound => { + let raw = path.ends_with("_raw"); + let format = || { + format!( + "IIO {} file {} not found", + if raw { "value" } else { "data" }, + path.display() + ) + }; + if raw { + error!("{}", format()); + Err(ScmiDeviceError::GenericError) + } else { + debug!("{}", format()); + Ok(None) + } + } + other_error => { + error!( + "Failed to read IIO data from {}: {}", + path.display(), + other_error + ); + Err(ScmiDeviceError::GenericError) + } + }, + } +} + +impl IIOSensor { + #[allow(clippy::new_ret_no_self)] + pub fn new(properties: &DeviceProperties) -> Result { + properties.check(&["path", "channel"], &["name"])?; + let sensor = Sensor::new(properties, "iio"); + Ok(IIOSensor { + sensor, + path: OsString::from(properties.get("path").unwrap()), + channel: OsString::from(properties.get("channel").unwrap()), + scalar: true, + axes: vec![], + }) + } + + pub fn new_device(properties: &DeviceProperties) -> MaybeDevice { + let iio_sensor = IIOSensor::new(properties)?; + let sensor_device = SensorDevice(Box::new(iio_sensor)); + Ok(Box::new(sensor_device)) + } + + fn set_sensor_name_from_file(&mut self, path: &PathBuf) { + match fs::read_to_string(path) { + Ok(name) => self.sensor_mut().name = name, + Err(error) => warn!( + "Error reading IIO device name from {}: {}", + path.display(), + error + ), + } + } + + fn custom_exponent(&self, path: &OsStr, unit_exponent: i8) -> i8 { + let mut custom_exponent: i8 = 0; + if let Ok(Some(scale)) = self.read_axis_scale(path) { + // Crash completely OK if *this* doesn't fit: + custom_exponent = scale.log10() as i8; + if scale < 1.0 { + // The logarithm is truncated towards zero, we need floor + custom_exponent -= 1; + } + // The SCMI exponent (unit_exponent + custom_exponent) can have max. 5 bits: + custom_exponent = min(15 - unit_exponent, custom_exponent); + custom_exponent = max(-16 - unit_exponent, custom_exponent); + debug!( + "Setting custom scaling coefficient for {:?}: {}", + &path, custom_exponent + ); + } + custom_exponent + } + + fn add_axis(&mut self, axes: &mut Vec, path: &OsStr) { + let unit_exponent = UNIT_MAPPING + .iter() + .find(|mapping| mapping.channel == self.channel) + .map_or(0, |mapping| mapping.unit_exponent); + // To get meaningful integer values, we must adjust exponent to + // the provided scale if any. + let custom_exponent = self.custom_exponent(path, unit_exponent); + axes.push(Axis { + path: OsString::from(path), + unit_exponent, + custom_exponent, + }); + } + + fn register_iio_file(&mut self, file: fs::DirEntry, axes: &mut Vec) { + let channel = self.channel.to_str().unwrap(); + let os_file_name = file.file_name(); + let file_name = os_file_name.to_str().unwrap_or_default(); + let raw_suffix = "_raw"; + if file_name == "name" { + self.set_sensor_name_from_file(&file.path()); + } else if file_name.starts_with(channel) && file_name.ends_with(raw_suffix) { + let infix = &file_name[channel.len()..file_name.len() - raw_suffix.len()]; + let infix_len = infix.len(); + if infix_len == 0 || (infix_len == 2 && infix.starts_with('_')) { + let raw_axis_path = Path::new(&self.path) + .join(Path::new(&file_name)) + .to_str() + .unwrap() + .to_string(); + let axis_path = raw_axis_path.strip_suffix(raw_suffix).unwrap(); + self.add_axis(axes, &OsString::from(axis_path)); + if infix_len > 0 { + self.scalar = false; + } + } + } + } + + fn read_axis_file( + &self, + path: &OsStr, + name: &str, + ) -> Result, ScmiDeviceError> { + for value_path in [ + Path::new(&(String::from(path.to_str().unwrap()) + "_" + name)), + &Path::new(&path).parent().unwrap().join(name), + ] + .iter() + { + let value: Option = read_number_from_file(value_path)?; + if value.is_some() { + return Ok(value); + } + } + Ok(None) + } + + fn read_axis_offset(&self, path: &OsStr) -> Result, ScmiDeviceError> { + self.read_axis_file(path, "offset") + } + + fn read_axis_scale(&self, path: &OsStr) -> Result, ScmiDeviceError> { + self.read_axis_file(path, "scale") + } + + fn read_axis(&self, axis: &Axis) -> Result { + let path_result = axis.path.clone().into_string(); + let mut value: i64 = + read_number_from_file(Path::new(&(path_result.unwrap() + "_raw")))?.unwrap(); + let offset: Option = self.read_axis_offset(&axis.path)?; + if let Some(offset_value) = offset { + match value.checked_add(offset_value) { + Some(new_value) => value = new_value, + None => { + error!( + "IIO offset overflow in {:?}: {} + {}", + &axis.path, + value, + offset.unwrap() + ); + return Err(ScmiDeviceError::GenericError); + } + } + } + let scale: Option = self.read_axis_scale(&axis.path)?; + if let Some(scale_value) = scale { + let exponent_scale = 10.0_f64.powi(i32::from(axis.custom_exponent)); + value = (value as f64 * (scale_value / exponent_scale)).round() as i64; + } + Ok(value) + } +} + +#[cfg(test)] +mod tests { + use crate::scmi::ScmiDevice; + + use super::*; + use std::{ + assert_eq, fs, + path::{Path, PathBuf}, + }; + + fn make_directory(prefix: &str) -> PathBuf { + for i in 1..100 { + let path = Path::new(".").join(format!("{prefix}{i}")); + if fs::create_dir(&path).is_ok() { + return path; + } + } + panic!("Couldn't create test directory"); + } + + struct IIODirectory { + path: PathBuf, + } + + impl IIODirectory { + fn new(files: &[(&str, &str)]) -> IIODirectory { + let path = make_directory("_test"); + let directory = IIODirectory { path }; + for (file, content) in files.iter() { + fs::write(&directory.path.join(file), content).unwrap(); + } + directory + } + } + + impl Drop for IIODirectory { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.path); + } + } + + fn directory_path(directory: &IIODirectory) -> String { + directory + .path + .clone() + .into_os_string() + .into_string() + .unwrap() + } + + fn device_properties(path: String, channel: String, name: Option) -> DeviceProperties { + let mut pairs = vec![("path".to_owned(), path), ("channel".to_owned(), channel)]; + if let Some(name) = name { + pairs.push(("name".to_owned(), name)); + } + DeviceProperties::new(pairs) + } + + fn make_iio_sensor_from_path(path: String, channel: String, name: Option) -> IIOSensor { + let properties = device_properties(path, channel, name); + IIOSensor::new(&properties).unwrap() + } + + fn make_iio_sensor( + directory: &IIODirectory, + channel: String, + name: Option, + ) -> IIOSensor { + let path = directory_path(directory); + make_iio_sensor_from_path(path, channel, name) + } + + fn make_scmi_sensor_from_path( + path: String, + channel: String, + name: Option, + ) -> MaybeDevice { + let properties = device_properties(path, channel, name); + IIOSensor::new_device(&properties) + } + + fn make_scmi_sensor( + directory: &IIODirectory, + channel: String, + name: Option, + ) -> Box { + let path = directory_path(directory); + make_scmi_sensor_from_path(path, channel, name).unwrap() + } + + #[test] + fn test_missing_property() { + let properties = DeviceProperties::new(vec![("path".to_owned(), ".".to_owned())]); + let result = IIOSensor::new(&properties); + match result { + Ok(_) => panic!("Should fail on a missing property"), + Err(DeviceError::MissingDeviceProperties(missing)) => { + assert_eq!(missing, vec!["channel".to_owned()]) + } + other => panic!("Unexpected result: {:?}", other), + } + } + + #[test] + fn test_extra_property() { + let properties = DeviceProperties::new(vec![ + ("path".to_owned(), ".".to_owned()), + ("name".to_owned(), "test".to_owned()), + ("channel".to_owned(), "in_accel".to_owned()), + ("foo".to_owned(), "something".to_owned()), + ("bar".to_owned(), "baz".to_owned()), + ]); + let result = IIOSensor::new(&properties); + match result { + Ok(_) => panic!("Should fail on an extra property"), + Err(DeviceError::UnexpectedDeviceProperties(extra)) => { + assert_eq!(extra, ["bar".to_owned(), "foo".to_owned()]) + } + other => panic!("Unexpected result: {:?}", other), + } + } + + #[test] + fn test_iio_init() { + let directory = IIODirectory::new(&[("foo", "bar"), ("in_accel_raw", "123")]); + let mut sensor = + make_scmi_sensor(&directory, "in_accel".to_owned(), Some("accel".to_owned())); + sensor.initialize().unwrap(); + } + + #[test] + fn test_iio_init_no_directory() { + let mut sensor = + make_scmi_sensor_from_path("non-existent".to_owned(), "".to_owned(), None).unwrap(); + match sensor.initialize() { + Ok(_) => panic!("Should fail on an inaccessible path"), + Err(DeviceError::IOError(path, std::io::Error { .. })) => { + assert_eq!(path, "non-existent") + } + other => panic!("Unexpected result: {:?}", other), + } + } + + #[test] + fn test_iio_init_no_channel() { + let directory = IIODirectory::new(&[("foo", "bar")]); + let mut sensor = make_scmi_sensor(&directory, "in_accel".to_owned(), None); + match sensor.initialize() { + Ok(_) => panic!("Should fail on an inaccessible channel"), + Err(DeviceError::GenericError(message)) => { + assert!( + message.starts_with("No \"in_accel\" channel found in \"./_test"), + "Unexpected error: {}", + message + ) + } + other => panic!("Unexpected result: {:?}", other), + } + } + + #[test] + fn test_sensor_name_from_fs() { + let directory = IIODirectory::new(&[("in_accel_raw", "123"), ("name", "foo")]); + let mut sensor = + make_iio_sensor(&directory, "in_accel".to_owned(), Some("accel".to_owned())); + sensor.initialize().unwrap(); + assert_eq!(sensor.sensor.name, "foo"); + } + + #[test] + fn test_sensor_name_from_params() { + let directory = IIODirectory::new(&[("in_accel_raw", "123")]); + let mut sensor = make_iio_sensor(&directory, "in_accel".to_owned(), Some("foo".to_owned())); + sensor.initialize().unwrap(); + assert_eq!(sensor.sensor.name, "foo"); + } + + #[test] + fn test_default_sensor_name() { + let directory = IIODirectory::new(&[("in_accel_raw", "123")]); + let mut sensor = make_iio_sensor(&directory, "in_accel".to_owned(), None); + sensor.initialize().unwrap(); + assert_eq!(sensor.sensor.name, "iio"); + } + + #[test] + fn test_units() { + let directory = IIODirectory::new(&[ + ("in_foo_raw", "123"), + ("in_accel_raw", "123"), + ("in_voltage_raw", "123"), + ]); + for (name, unit) in [ + ("foo", scmi::SENSOR_UNIT_UNSPECIFIED), + ("accel", scmi::SENSOR_UNIT_METERS_PER_SECOND_SQUARED), + ("voltage", scmi::SENSOR_UNIT_VOLTS), + ] + .iter() + { + let sensor = + make_iio_sensor(&directory, "in_".to_owned() + name, Some(name.to_string())); + assert_eq!(sensor.unit(), *unit); + } + } + + #[test] + fn test_unit_exponent() { + for (channel, scale, exponent) in [ + ("in_accel", 1.23, 0), + ("in_accel", 0.000123, -4), + ("in_accel", 123.0, 2), + ("in_voltage", 123.0, -1), + ] + .iter() + { + let raw_file = format!("{channel}_raw"); + let scale_file = format!("{channel}_scale"); + let directory = + IIODirectory::new(&[(&raw_file, "123"), (&scale_file, &scale.to_string())]); + let mut sensor = make_iio_sensor(&directory, channel.to_string(), None); + sensor.initialize().unwrap(); + assert_eq!(sensor.unit_exponent(0), *exponent); + } + } + + #[test] + fn test_unit_exponent_multiple_axes() { + let directory = IIODirectory::new(&[ + ("in_accel_x_raw", "123"), + ("in_accel_x_scale", "0.123"), + ("in_accel_y_raw", "123"), + ("in_accel_y_scale", "12.3"), + ]); + let mut sensor = make_iio_sensor(&directory, "in_accel".to_owned(), None); + sensor.initialize().unwrap(); + assert_eq!(sensor.unit_exponent(0), -1); + assert_eq!(sensor.unit_exponent(1), 1); + } + + #[test] + fn test_unit_exponent_single_scale() { + let directory = IIODirectory::new(&[("in_accel_raw", "123"), ("scale", "0.123")]); + let mut sensor = make_iio_sensor(&directory, "in_accel".to_owned(), None); + sensor.initialize().unwrap(); + assert_eq!(sensor.unit_exponent(0), -1); + } + + #[test] + fn test_number_of_axes_scalar() { + let directory = IIODirectory::new(&[("in_accel_raw", "123"), ("in_accel_scale", "123")]); + let mut sensor = make_iio_sensor(&directory, "in_accel".to_owned(), None); + sensor.initialize().unwrap(); + assert_eq!(sensor.number_of_axes(), 0); + } + + #[test] + fn test_number_of_axes_1() { + let directory = IIODirectory::new(&[("in_accel_x_raw", "123"), ("in_accel_scale", "123")]); + let mut sensor = make_iio_sensor(&directory, "in_accel".to_owned(), None); + sensor.initialize().unwrap(); + assert_eq!(sensor.number_of_axes(), 1); + } + + #[test] + fn test_number_of_axes_3() { + let directory = IIODirectory::new(&[ + ("in_accel_x_raw", "123"), + ("in_accel_y_raw", "123"), + ("in_accel_z_raw", "123"), + ("in_accel_x_scale", "123"), + ]); + let mut sensor = make_iio_sensor(&directory, "in_accel".to_owned(), None); + sensor.initialize().unwrap(); + assert_eq!(sensor.number_of_axes(), 3); + } + + #[test] + fn test_axis_name_prefix() { + for (channel, prefix) in [ + ("in_accel", "accel"), + ("out_voltage", "voltage"), + ("foo", "foo"), + ("name-longer-than-fifteen-characters", "name-longer-tha"), + ] + .iter() + { + let sensor = make_iio_sensor_from_path("".to_owned(), channel.to_string(), None); + assert_eq!(&sensor.axis_name_prefix(), prefix); + } + } + + #[test] + fn test_iio_reading_scalar() { + let directory = IIODirectory::new(&[ + ("in_voltage_raw", "9876543210"), + ("in_voltage_offset", "123"), + ("in_voltage_scale", "456"), + ]); + let mut sensor = make_iio_sensor(&directory, "in_voltage".to_owned(), None); + sensor.initialize().unwrap(); + let result = sensor.reading_get().unwrap(); + // (9876543210 + 123) * 456 = 4503703759848 + // custom exponent = 2 + // applied and rounded: 45037037598 = 0xA7C6AA81E + assert_eq!(result.len(), 4); + assert_eq!(result.get(0).unwrap(), &MessageValue::Unsigned(0x7C6AA81E)); + assert_eq!(result.get(1).unwrap(), &MessageValue::Unsigned(0xA)); + assert_eq!(result.get(2).unwrap(), &MessageValue::Unsigned(0)); + assert_eq!(result.get(3).unwrap(), &MessageValue::Unsigned(0)); + } + + #[test] + fn test_iio_reading_scalar_whitespace() { + let directory = IIODirectory::new(&[ + ("in_accel_raw", "10\n"), + ("in_accel_offset", "20\n"), + ("in_accel_scale", "0.3\n"), + ]); + let mut sensor = make_iio_sensor(&directory, "in_accel".to_owned(), None); + sensor.initialize().unwrap(); + let result = sensor.reading_get().unwrap(); + assert_eq!(result.len(), 4); + assert_eq!(result.get(0).unwrap(), &MessageValue::Unsigned(0x5A)); + assert_eq!(result.get(1).unwrap(), &MessageValue::Unsigned(0)); + assert_eq!(result.get(2).unwrap(), &MessageValue::Unsigned(0)); + assert_eq!(result.get(3).unwrap(), &MessageValue::Unsigned(0)); + } + + #[test] + fn test_iio_reading_axes() { + let directory = IIODirectory::new(&[ + ("in_accel_x_raw", "10"), + ("in_accel_x_offset", "1"), + ("in_accel_y_raw", "20"), + ("in_accel_y_offset", "10"), + ("in_accel_z_raw", "30"), + ("in_accel_z_offset", "20"), + ("in_accel_z_scale", "0.3"), + ("scale", "0.02"), + ]); + let mut sensor = make_iio_sensor(&directory, "in_accel".to_owned(), None); + sensor.initialize().unwrap(); + let result = sensor.reading_get().unwrap(); + assert_eq!(result.len(), 12); + assert_eq!(result.get(0).unwrap(), &MessageValue::Unsigned(22)); + assert_eq!(result.get(4).unwrap(), &MessageValue::Unsigned(60)); + assert_eq!(result.get(8).unwrap(), &MessageValue::Unsigned(150)); + for i in 0..12 { + if i % 4 != 0 { + assert_eq!(result.get(i).unwrap(), &MessageValue::Unsigned(0)); + } + } + } +} diff --git a/crates/scmi/src/devices/mod.rs b/crates/scmi/src/devices/mod.rs index a118d1b..a776667 100644 --- a/crates/scmi/src/devices/mod.rs +++ b/crates/scmi/src/devices/mod.rs @@ -3,3 +3,4 @@ pub mod common; pub mod fake; +pub mod iio; diff --git a/crates/scmi/src/scmi.rs b/crates/scmi/src/scmi.rs index 2c525d3..5bcca1e 100644 --- a/crates/scmi/src/scmi.rs +++ b/crates/scmi/src/scmi.rs @@ -8,7 +8,7 @@ use std::{ }; use itertools::Itertools; -use log::{debug, error, info}; +use log::{debug, error, info, warn}; use thiserror::Error as ThisError; use crate::devices::common::DeviceError; @@ -218,7 +218,25 @@ pub const SENSOR_CONFIG_GET: MessageId = 0x9; pub const SENSOR_CONFIG_SET: MessageId = 0xA; pub const SENSOR_CONTINUOUS_UPDATE_NOTIFY: MessageId = 0xB; +#[allow(dead_code)] +pub const SENSOR_UNIT_NONE: u8 = 0; pub const SENSOR_UNIT_UNSPECIFIED: u8 = 1; +pub const SENSOR_UNIT_DEGREES_C: u8 = 2; +pub const SENSOR_UNIT_VOLTS: u8 = 5; +pub const SENSOR_UNIT_AMPS: u8 = 6; +pub const SENSOR_UNIT_WATTS: u8 = 7; +pub const SENSOR_UNIT_JOULS: u8 = 8; +pub const SENSOR_UNIT_LUX: u8 = 13; +pub const SENSOR_UNIT_METERS: u8 = 31; +pub const SENSOR_UNIT_RADIANS: u8 = 36; +pub const SENSOR_UNIT_GAUSS: u8 = 45; +pub const SENSOR_UNIT_FARADS: u8 = 48; +pub const SENSOR_UNIT_OHMS: u8 = 49; +pub const SENSOR_UNIT_SIEMENS: u8 = 50; +pub const SENSOR_UNIT_PERCENTAGE: u8 = 65; +pub const SENSOR_UNIT_PASCALS: u8 = 66; +pub const SENSOR_UNIT_RADIANS_PER_SECOND: u8 = 87; +pub const SENSOR_UNIT_METERS_PER_SECOND: u8 = 90; pub const SENSOR_UNIT_METERS_PER_SECOND_SQUARED: u8 = 89; enum ParameterType { @@ -475,6 +493,8 @@ impl HandlerMap { #[derive(Debug, PartialEq, Eq, ThisError)] pub enum ScmiDeviceError { + #[error("Generic error")] + GenericError, #[error("Invalid parameters")] InvalidParameters, #[error("No such device")] @@ -659,6 +679,10 @@ impl ScmiHandler { info!("Unsupported request for {}", device_index); Response::from(ReturnStatus::NotSupported) } + ScmiDeviceError::GenericError => { + warn!("Device error in {}", device_index); + Response::from(ReturnStatus::GenericError) + } }, } } diff --git a/crates/scmi/src/vhu_scmi.rs b/crates/scmi/src/vhu_scmi.rs index 3a25597..45d9cd0 100644 --- a/crates/scmi/src/vhu_scmi.rs +++ b/crates/scmi/src/vhu_scmi.rs @@ -34,7 +34,7 @@ const EVENT_QUEUE: u16 = 1; const VIRTIO_SCMI_F_P2A_CHANNELS: u16 = 0; -#[derive(Debug, PartialEq, Eq, ThisError)] +#[derive(Debug, ThisError)] pub enum VuScmiError { #[error("Descriptor not found")] DescriptorNotFound, @@ -638,22 +638,24 @@ mod tests { // Have only one descriptor, expected two. let parameters = vec![&default]; let desc_chain = build_dummy_desc_chain(parameters); - assert_eq!( - backend - .process_requests(vec![desc_chain], &vring) - .unwrap_err(), - VuScmiError::UnexpectedDescriptorCount(1) - ); + match backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err() + { + VuScmiError::UnexpectedDescriptorCount(1) => (), + other => panic!("Unexpected result: {:?}", other), + } // Have three descriptors, expected two. let parameters = vec![&default, &default, &default]; let desc_chain = build_dummy_desc_chain(parameters); - assert_eq!( - backend - .process_requests(vec![desc_chain], &vring) - .unwrap_err(), - VuScmiError::UnexpectedDescriptorCount(3) - ); + match backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err() + { + VuScmiError::UnexpectedDescriptorCount(3) => (), + other => panic!("Unexpected result: {:?}", other), + } // Write only descriptors. let p = DescParameters { @@ -663,12 +665,13 @@ mod tests { }; let parameters = vec![&p, &p]; let desc_chain = build_dummy_desc_chain(parameters); - assert_eq!( - backend - .process_requests(vec![desc_chain], &vring) - .unwrap_err(), - VuScmiError::UnexpectedWriteOnlyDescriptor(0) - ); + match backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err() + { + VuScmiError::UnexpectedWriteOnlyDescriptor(0) => (), + other => panic!("Unexpected result: {:?}", other), + } // Invalid request address. let parameters = vec![ @@ -684,12 +687,13 @@ mod tests { }, ]; let desc_chain = build_dummy_desc_chain(parameters); - assert_eq!( - backend - .process_requests(vec![desc_chain], &vring) - .unwrap_err(), - VuScmiError::DescriptorReadFailed - ); + match backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err() + { + VuScmiError::DescriptorReadFailed => (), + other => panic!("Unexpected result: {:?}", other), + } // Invalid request length (very small). let parameters = vec![ @@ -705,30 +709,33 @@ mod tests { }, ]; let desc_chain = build_dummy_desc_chain(parameters); - assert_eq!( - backend - .process_requests(vec![desc_chain], &vring) - .unwrap_err(), - VuScmiError::UnexpectedMinimumDescriptorSize(4, 2) - ); + match backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err() + { + VuScmiError::UnexpectedMinimumDescriptorSize(4, 2) => (), + other => panic!("Unexpected result: {:?}", other), + } // Invalid request length (too small). let desc_chain = build_cmd_desc_chain(0x10, 0x2, vec![]); - assert_eq!( - backend - .process_requests(vec![desc_chain], &vring) - .unwrap_err(), - VuScmiError::UnexpectedDescriptorSize(8, 4) - ); + match backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err() + { + VuScmiError::UnexpectedDescriptorSize(8, 4) => (), + other => panic!("Unexpected result: {:?}", other), + } // Invalid request length (too large). let desc_chain = build_cmd_desc_chain(0x10, 0x0, vec![0]); - assert_eq!( - backend - .process_requests(vec![desc_chain], &vring) - .unwrap_err(), - VuScmiError::UnexpectedDescriptorSize(4, 8) - ); + match backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err() + { + VuScmiError::UnexpectedDescriptorSize(4, 8) => (), + other => panic!("Unexpected result: {:?}", other), + } // Read only descriptors. let p = DescParameters { @@ -738,12 +745,13 @@ mod tests { }; let parameters = vec![&p, &p]; let desc_chain = build_dummy_desc_chain(parameters); - assert_eq!( - backend - .process_requests(vec![desc_chain], &vring) - .unwrap_err(), - VuScmiError::UnexpectedReadableDescriptor(1) - ); + match backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err() + { + VuScmiError::UnexpectedReadableDescriptor(1) => (), + other => panic!("Unexpected result: {:?}", other), + } // Invalid response address. let parameters = vec![ @@ -759,12 +767,13 @@ mod tests { }, ]; let desc_chain = build_dummy_desc_chain(parameters); - assert_eq!( - backend - .process_requests(vec![desc_chain], &vring) - .unwrap_err(), - VuScmiError::DescriptorWriteFailed - ); + match backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err() + { + VuScmiError::DescriptorWriteFailed => (), + other => panic!("Unexpected result: {:?}", other), + } // Invalid response length. let parameters = vec![ @@ -780,12 +789,13 @@ mod tests { }, ]; let desc_chain = build_dummy_desc_chain(parameters); - assert_eq!( - backend - .process_requests(vec![desc_chain], &vring) - .unwrap_err(), - VuScmiError::InsufficientDescriptorSize(8, 6) - ); + match backend + .process_requests(vec![desc_chain], &vring) + .unwrap_err() + { + VuScmiError::InsufficientDescriptorSize(8, 6) => (), + other => panic!("Unexpected result: {:?}", other), + } } #[test] @@ -832,12 +842,13 @@ mod tests { len: 0, }; let desc_chain = build_dummy_desc_chain(vec![&p, &p]); - assert_eq!( - backend - .process_event_requests(vec![desc_chain], &vring) - .unwrap_err(), - VuScmiError::UnexpectedDescriptorCount(2) - ); + match backend + .process_event_requests(vec![desc_chain], &vring) + .unwrap_err() + { + VuScmiError::UnexpectedDescriptorCount(2) => (), + other => panic!("Unexpected result: {:?}", other), + } // Read only descriptor let p = DescParameters { @@ -846,12 +857,13 @@ mod tests { len: 0, }; let desc_chain = build_dummy_desc_chain(vec![&p]); - assert_eq!( - backend - .process_event_requests(vec![desc_chain], &vring) - .unwrap_err(), - VuScmiError::UnexpectedReadableDescriptor(0) - ); + match backend + .process_event_requests(vec![desc_chain], &vring) + .unwrap_err() + { + VuScmiError::UnexpectedReadableDescriptor(0) => (), + other => panic!("Unexpected result: {:?}", other), + } } #[test] From b84bf46d68dc7f4b933daa9df2df07ef7a82be92 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Tue, 8 Aug 2023 12:58:06 +0200 Subject: [PATCH 149/189] scmi: Add scale & offset to the accelerator in the kernel To be able to test scaling in guests running on hosts with the dummy IIO module. Signed-off-by: Milan Zamazal --- crates/scmi/kernel/iio-dummy/README.md | 1 + crates/scmi/kernel/iio-dummy/iio_modified_dummy.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/crates/scmi/kernel/iio-dummy/README.md b/crates/scmi/kernel/iio-dummy/README.md index ed87101..59e16f4 100644 --- a/crates/scmi/kernel/iio-dummy/README.md +++ b/crates/scmi/kernel/iio-dummy/README.md @@ -14,6 +14,7 @@ Otherwise, this alternative is provided with the following changes: - Simplified Makefile for out of tree compilation. - The accelerometer has three axes instead of just one. +- The Y axis of the accelerometer has offset and scale. Of course, you can modified it further for your liking if needed. diff --git a/crates/scmi/kernel/iio-dummy/iio_modified_dummy.c b/crates/scmi/kernel/iio-dummy/iio_modified_dummy.c index 7504489..dd5e594 100644 --- a/crates/scmi/kernel/iio-dummy/iio_modified_dummy.c +++ b/crates/scmi/kernel/iio-dummy/iio_modified_dummy.c @@ -16,6 +16,7 @@ * * - Dropped conditional parts. * - Use 3 axes in the accelerometer device. + * - Define offset and scale for some of the accelerometer axes. */ #include #include @@ -184,6 +185,9 @@ static const struct iio_chan_spec iio_dummy_channels[] = { /* Channel 2 is use for modifiers */ .channel2 = IIO_MOD_Y, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_CALIBBIAS), .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), @@ -352,6 +356,15 @@ static int iio_dummy_read_raw(struct iio_dev *indio_dev, *val2 = 1344; ret = IIO_VAL_INT_PLUS_NANO; } + break; + case IIO_ACCEL: + switch(chan->scan_index) { + case DUMMY_INDEX_ACCEL_Y: + *val = 0; + *val2 = 1344; + break; + } + ret = IIO_VAL_INT_PLUS_MICRO; break; default: break; From 847b3f44a6ccec291d59ad2c40c9971550bce866 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Wed, 9 Aug 2023 18:50:11 +0200 Subject: [PATCH 150/189] scmi: Add source code docstrings Signed-off-by: Milan Zamazal --- crates/scmi/README.md | 3 + crates/scmi/src/devices/common.rs | 136 ++++++++++++++++++++++++++++++ crates/scmi/src/devices/fake.rs | 8 +- crates/scmi/src/devices/iio.rs | 67 ++++++++++++--- crates/scmi/src/devices/mod.rs | 7 ++ crates/scmi/src/main.rs | 30 +++++++ crates/scmi/src/scmi.rs | 70 +++++++++++++++ crates/scmi/src/vhu_scmi.rs | 15 ++-- 8 files changed, 318 insertions(+), 18 deletions(-) diff --git a/crates/scmi/README.md b/crates/scmi/README.md index 4c65023..5b97275 100644 --- a/crates/scmi/README.md +++ b/crates/scmi/README.md @@ -67,6 +67,9 @@ The currently supported SCMI protocols are: Basically only the mandatory and necessary parts of the protocols are implemented. +See source code (`scmi` crate) documentation for details and how to +add more protocols, host device bindings or other functionality. + ## Kernel support for testing `kernel` subdirectory contains diff --git a/crates/scmi/src/devices/common.rs b/crates/scmi/src/devices/common.rs index 11d4b08..d79dcd1 100644 --- a/crates/scmi/src/devices/common.rs +++ b/crates/scmi/src/devices/common.rs @@ -1,6 +1,14 @@ // SPDX-FileCopyrightText: Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 +//! Common functionality for SCMI bindings to host devices. +//! +//! A new kind of devices can be added in [available_devices] using +//! [DeviceSpecification::new] calls. +//! +//! The module also defines common infrastructure to provide sensor devices to +//! SCMI, see [SensorT]. + use std::collections::{HashMap, HashSet}; use std::ffi::OsString; use std::fmt::Write; @@ -19,6 +27,8 @@ use crate::scmi::{ use super::{fake, iio}; +/// Enumeration of vhost-device-scmi exit codes. +// TODO: It should be better placed elsewhere but it's currently used only here. enum ExitCodes { Help = 1, } @@ -100,14 +110,36 @@ impl DeviceProperties { pub type MaybeDevice = Result, DeviceError>; type DeviceConstructor = fn(&DeviceProperties) -> MaybeDevice; +/// Definition of a device kind. +/// +/// Use [DeviceSpecification::new] to create it. pub struct DeviceSpecification { + /// Function to call to create the device. + /// + /// The device properties are those provided on the command line by the + /// user. pub(crate) constructor: DeviceConstructor, + /// Short description of the device. + /// + /// Single line, not a complete sentence. short_help: String, + /// Long description of the device. + /// + /// Complete sentences, can span multiple lines. long_help: String, + /// Description of the device parameters available to the user. + /// + /// Each item in the vector corresponds to a single parameter description + /// and should start with the parameter name and a followup colon. parameters_help: Vec, } impl DeviceSpecification { + /// Creates a new device specification. + /// + /// See [DeviceSpecification] for the meaning of the arguments. + /// The device specification must be used in [available_devices] to + /// actually add the device. fn new( constructor: DeviceConstructor, short_help: &str, @@ -126,8 +158,16 @@ impl DeviceSpecification { } } +/// Mapping of device identifiers (names) to device specifications. +/// +/// The string keys correspond to device identifiers specified on the command +/// line. type NameDeviceMapping = HashMap<&'static str, DeviceSpecification>; +/// Creates device mapping and adds all the supported devices to it. +/// +/// If you want to introduce a new kind of host device bindings, insert a +/// device identifier + [DeviceSpecification] to [NameDeviceMapping] here. pub fn available_devices() -> NameDeviceMapping { let mut devices: NameDeviceMapping = HashMap::new(); devices.insert( @@ -190,9 +230,17 @@ pub fn print_devices_help() { // Common sensor infrastructure +/// Basic information about the sensor. +/// +/// It is typically used as a field in structs implementing sensor devices. #[derive(Debug)] pub struct Sensor { + /// The sensor name (possibly truncated) as reported to the guest. pub name: String, + /// Whether the sensor is enabled. + /// + /// Sensors can be enabled and disabled using SCMI. [Sensor]s created + /// using [Sensor::new] are disabled initially. enabled: bool, } @@ -206,34 +254,92 @@ impl Sensor { } } +/// Common base that sensor devices can use to simplify their implementation. +/// +/// To add a new kind of sensor bindings, you must implement +/// [crate::scmi::ScmiDevice], define [DeviceSpecification] and add it to +/// [NameDeviceMapping] created in [available_devices]. You can do it fully +/// yourself or use this trait to simplify the implementation. +/// +/// The trait is typically used as follows: +/// +/// ```rust +/// struct MySensor { +/// sensor: Sensor, +/// // other fields as needed +/// } +/// +/// impl SensorT for MySensor { +/// // provide trait functions implementation as needed +/// } +/// +/// impl MySensor { +/// pub fn new_device(properties: &DeviceProperties) -> MaybeDevice { +/// check_device_properties(properties, &[], &["name"])?; +/// let sensor = Sensor::new(properties, "mydevice"); +/// let my_sensor = MySensor { sensor }; +/// let sensor_device = SensorDevice(Box::new(my_sensor)); +/// Ok(Box::new(sensor_device)) +/// } +/// } +/// ``` +/// +/// See [crate::devices::fake::FakeSensor] implementation for an example. pub trait SensorT: Send { + /// Returns the inner [Sensor] instance, immutable. fn sensor(&self) -> &Sensor; + /// Returns the inner [Sensor] instance, mutable. fn sensor_mut(&mut self) -> &mut Sensor; + /// Performs any non-default initialization on the sensor. + /// + /// If the initialization fails, a corresponding error message is + /// returned. fn initialize(&mut self) -> Result<(), DeviceError> { Ok(()) } + /// Returns the id of the SCMI protocol used to communicate with the + /// sensor. + /// + /// Usually no need to redefine this. fn protocol(&self) -> ProtocolId { SENSOR_PROTOCOL_ID } + /// Returns an error message about invalid property `name`. + /// + /// Usually no need to redefine this. fn invalid_property(&self, name: &str) -> Result<(), DeviceError> { Result::Err(DeviceError::InvalidProperty(name.to_owned())) } + /// Processes a device property specified on the command line. + /// + /// The function is called on all the device properties from the command line. fn process_property(&mut self, name: &str, _value: &str) -> Result<(), DeviceError> { self.invalid_property(name) } + /// Returns the number of axes of the given sensor. + /// + /// If the sensor provides just a scalar value, 0 must be returned (the + /// default return value here). Otherwise a non-zero value must be + /// returned, even for vector sensors with a single access. fn number_of_axes(&self) -> u32 { 0 } + /// Formats the unit of the given `axis` for SCMI protocol. + /// + /// Usually no need to redefine this. fn format_unit(&self, axis: u32) -> u32 { (self.unit_exponent(axis) as u32 & 0x1F) << 11 | u32::from(self.unit()) } + /// Returns SCMI description of the sensor. + /// + /// Usually no need to redefine this. fn description_get(&self) -> DeviceResult { // Continuous update required by Linux SCMI IIO driver let low = 1 << 30; @@ -255,18 +361,26 @@ pub trait SensorT: Send { Ok(values) } + /// Returns the SCMI unit of the sensor. fn unit(&self) -> u8 { scmi::SENSOR_UNIT_UNSPECIFIED } + /// Returns the decadic exponent to apply to the sensor values. fn unit_exponent(&self, _axis: u32) -> i8 { 0 } + /// Returns the prefix of axes names. + /// + /// Usually no need to redefine this. fn axis_name_prefix(&self) -> String { "axis".to_owned() } + /// Returns the suffix of the given axis. + /// + /// Usually no need to redefine this. fn axis_name_suffix(&self, axis: u32) -> char { match axis { 0 => 'X', @@ -276,6 +390,9 @@ pub trait SensorT: Send { } } + /// Returns the SCMI description of the given axis. + /// + /// Usually no need to redefine this. fn axis_description(&self, axis: u32) -> Vec { let mut values = vec![]; values.push(MessageValue::Unsigned(axis)); // axis id @@ -292,11 +409,19 @@ pub trait SensorT: Send { values } + /// Returns the SCMI configuration of the sensor. + /// + /// The default implementation here returns just whether the sensor is + /// enabled or not. fn config_get(&self) -> DeviceResult { let config = u32::from(self.sensor().enabled); Ok(vec![MessageValue::Unsigned(config)]) } + /// Processes the SCMI configuration of the sensor. + /// + /// The default implementation here permits and implements only enabling + /// and disabling the sensor. fn config_set(&mut self, config: u32) -> DeviceResult { if config & 0xFFFFFFFE != 0 { return Result::Err(ScmiDeviceError::UnsupportedRequest); @@ -306,8 +431,19 @@ pub trait SensorT: Send { Ok(vec![]) } + /// Returns SCMI reading of the sensor values. + /// + /// It is a sequence of [MessageValue::Unsigned] values, 4 of them for each + /// sensor axis. See the SCMI standard for the exact specification of the + /// result. fn reading_get(&mut self) -> DeviceResult; + /// Handles the given protocol message with the given parameters. + /// + /// Usually no need to redefine this, unless more than the basic + /// functionality is needed, in which case it would be probably better to + /// enhance this trait with additional functions and improved + /// implementation. fn handle(&mut self, message_id: MessageId, parameters: &[MessageValue]) -> DeviceResult { match message_id { SENSOR_DESCRIPTION_GET => self.description_get(), diff --git a/crates/scmi/src/devices/fake.rs b/crates/scmi/src/devices/fake.rs index bc760b8..c0936b4 100644 --- a/crates/scmi/src/devices/fake.rs +++ b/crates/scmi/src/devices/fake.rs @@ -1,7 +1,13 @@ // SPDX-FileCopyrightText: Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 -// Fake sensor +//! Fake sensor implementation. +//! +//! The fake sensor is completely implemented here rather than bound to a host +//! device. It emulates a dummy accelerometer device that increments an axis +//! reading value on each its retrieval. Useful for initial testing and +//! arranging SCMI virtualization setup without the need to bind real host +//! devices. use crate::scmi::{self, DeviceResult, MessageValue}; diff --git a/crates/scmi/src/devices/iio.rs b/crates/scmi/src/devices/iio.rs index e5dcfe9..4b5522e 100644 --- a/crates/scmi/src/devices/iio.rs +++ b/crates/scmi/src/devices/iio.rs @@ -1,7 +1,13 @@ // SPDX-FileCopyrightText: Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 -// Industrial I/O sensors +//! Industrial I/O (IIO) sensors bindings. +//! +//! Basic functionality for exposing `/sys/bus/iio/devices/` stuff as guest +//! SCMI devices. Only some typical cases are supported. If you want more +//! functionality, you must enhance the implementation here. +//! +//! For some entry points, see [IIOSensor] and [Axis]. use std::cmp::{max, min}; use std::ffi::{OsStr, OsString}; @@ -16,14 +22,30 @@ use crate::scmi::{self, DeviceResult, MessageValue, ScmiDeviceError, MAX_SIMPLE_ use super::common::{DeviceError, DeviceProperties, MaybeDevice, Sensor, SensorDevice, SensorT}; +/// Information about units used by the given Linux IIO channel. struct UnitMapping<'a> { + /// IIO sysfs channel prefix, e.g. "in_accel". channel: &'a str, + /// One of the SCMI unit constants from [crate::scmi] (enum is not used to + /// avoid type conversions everywhere). unit: u8, + /// Decadic exponent to be used to convert the given unit to the SCMI unit. + /// For example, the exponent is 0 for no conversion, -3 to convert + /// milliamps here to amps in SCMI, or 3 to convert kilopascals here to + /// pascals in SCMI. unit_exponent: i8, // max. 5 bits actually } -// Incomplete, just a sample. -// TODO: Make some macro(s) for this. +/// Specification of IIO channel units. +/// +/// Based on +/// . +/// Not everything from there is present -- channels here with more complicated +/// unit transformations (beyond using a decadic exponent; e.g. degrees to +/// radians or units not defined in SCMI) are omitted. If an IIO channel +/// doesn't have unit specification here, it can be still used by the unit +/// reported in SCMI will be [crate::scmi::SENSOR_UNIT_UNSPECIFIED]. +// TODO: Make some macro(s) for this? const UNIT_MAPPING: &[UnitMapping] = &[ UnitMapping { channel: "in_accel", @@ -137,23 +159,46 @@ const UNIT_MAPPING: &[UnitMapping] = &[ }, ]; +/// Representation of an IIO channel axis. +/// +/// Used also for scalar values. #[derive(PartialEq, Debug)] struct Axis { + /// Full sysfs path to the axis value file stripped of "_raw". path: OsString, // without "_raw" suffix + /// Axis unit exponent, see [UnitMapping::unit_exponent] and [UNIT_MAPPING]. unit_exponent: i8, + /// Additional exponent to apply to the axis values. It is computed from + /// the axis value scaling (see [IIOSensor::custom_exponent] to provide a + /// sufficiently accurate SCMI value that is represented by an integer (not + /// a float) + decadic exponent. custom_exponent: i8, } +/// Particular IIO sensor specification. +/// +/// An IIO sensor is specified by an IIO sysfs device directory and a channel +/// prefix within the directory (i.e. more devices can be defined for a single +/// IIO device directory). All other information about the sensor is retrieved +/// from the device directory and from [UNIT_MAPPING]. #[derive(Debug)] -pub(crate) struct IIOSensor { +pub struct IIOSensor { + /// Common sensor instance. sensor: Sensor, - // Full /sys path to the device directory + /// Full sysfs path to the device directory. + /// + /// Provided by the user. path: OsString, - // Prefix of the device type in the device directory, e.g. "in_accel" + /// Prefix of the device type in the device directory, e.g. "in_accel". + /// + /// Provided by the user. channel: OsString, - // Whether the sensor is scalar or has one or more axes + /// Whether the sensor is scalar or has one or more axes. + /// + /// Determined automatically by looking for presence of `*_[xyz]_raw` files + /// with the given channel prefix. scalar: bool, - // Paths to "_raw" files + /// Axes descriptions, see [Axis] for more details. axes: Vec, } @@ -282,10 +327,10 @@ fn read_number_from_file(path: &Path) -> Result, ScmiDevic impl IIOSensor { #[allow(clippy::new_ret_no_self)] - pub fn new(properties: &DeviceProperties) -> Result { + pub fn new(properties: &DeviceProperties) -> Result { properties.check(&["path", "channel"], &["name"])?; let sensor = Sensor::new(properties, "iio"); - Ok(IIOSensor { + Ok(Self { sensor, path: OsString::from(properties.get("path").unwrap()), channel: OsString::from(properties.get("channel").unwrap()), @@ -295,7 +340,7 @@ impl IIOSensor { } pub fn new_device(properties: &DeviceProperties) -> MaybeDevice { - let iio_sensor = IIOSensor::new(properties)?; + let iio_sensor = Self::new(properties)?; let sensor_device = SensorDevice(Box::new(iio_sensor)); Ok(Box::new(sensor_device)) } diff --git a/crates/scmi/src/devices/mod.rs b/crates/scmi/src/devices/mod.rs index a776667..5b7ea61 100644 --- a/crates/scmi/src/devices/mod.rs +++ b/crates/scmi/src/devices/mod.rs @@ -1,6 +1,13 @@ // SPDX-FileCopyrightText: Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 +//! Implementation of SCMI bindings to host devices. +//! +//! The general infrastructure is implemented in [crate::devices::common] module. +//! Access to particular kinds of devices is implemented in the other modules: +//! - [crate::devices::fake] provides a fake sensor. +//! - [crate::devices::iio] implements access to industrial I/O (IIO) devices. + pub mod common; pub mod fake; pub mod iio; diff --git a/crates/scmi/src/main.rs b/crates/scmi/src/main.rs index b61281b..60b5072 100644 --- a/crates/scmi/src/main.rs +++ b/crates/scmi/src/main.rs @@ -2,6 +2,36 @@ // SPDX-License-Identifier: Apache-2.0 // Based on implementation of other devices here, Copyright by Linaro Ltd. +//! vhost-user daemon implementation for +//! [System Control and Management Interface](https://developer.arm.com/Architectures/System%20Control%20and%20Management%20Interface) +//! (SCMI). +//! +//! Currently, the mandatory parts of the following SCMI protocols are implemented: +//! +//! - base +//! - sensor management +//! +//! As for sensor management, support for industrial I/O (IIO) Linux devices +//! and a fake sensor device is implemented. +//! +//! The daemon listens on a socket that is specified using `--socket-path` +//! command line option. Usually at least one exposed device is specified, +//! which is done using `--device` command line option. It can be used more +//! than once, for different devices. `--device help` lists the available +//! devices and their options. +//! +//! The daemon normally logs info and higher messages to the standard error +//! output. To log more messages, you can set `RUST_LOG` environment variable, +//! e.g. to `debug`. +//! +//! Here is an example command line invocation of the daemon: +//! +//! ```sh +//! RUST_LOG=debug vhost-device-scmi \ +//! --socket ~/tmp/scmi.sock \ +//! --device iio,path=/sys/bus/iio/devices/iio:device0,channel=in_accel +//! ``` + mod devices; mod scmi; mod vhu_scmi; diff --git a/crates/scmi/src/scmi.rs b/crates/scmi/src/scmi.rs index 5bcca1e..3653528 100644 --- a/crates/scmi/src/scmi.rs +++ b/crates/scmi/src/scmi.rs @@ -1,6 +1,15 @@ // SPDX-FileCopyrightText: Red Hat, Inc. // SPDX-License-Identifier: Apache-2.0 +//! Implementation of SCMI and some of its protocols. +//! +//! This module implements SCMI infrastructure and some of the SCMI protocols. +//! See [HandlerMap::new] how to add support for another SCMI protocol or to add +//! more functionality to an already implemented SCMI protocol. +//! +//! If you want to add new devices (e.g. SCMI bindings to some kinds of host +//! devices), see [crate::devices] modules. + use std::{ cmp::min, collections::HashMap, @@ -17,6 +26,9 @@ pub type MessageHeader = u32; pub const MAX_SIMPLE_STRING_LENGTH: usize = 16; // incl. NULL terminator +/// Wrapper around SCMI values of the basic types SCMI defines. +/// +/// Everything communicating to/from SCMI must be composed of them. // SCMI specification talks about Le32 parameter and return values. // VirtIO SCMI specification talks about u8 SCMI values. // Let's stick with SCMI specification for implementation simplicity. @@ -38,6 +50,9 @@ impl MessageValue { pub type MessageValues = Vec; +/// Enumeration of SCMI message types, mapped to the corresponding SCMI codes. +/// +/// The only one we currently support is [MessageType::Command]. #[derive(Debug, PartialEq)] enum MessageType { // 4-bit unsigned integer @@ -48,6 +63,7 @@ pub type MessageId = u8; pub type ProtocolId = u8; type NParameters = u8; +/// Mapping of return values to SCMI return status codes. #[derive(Clone, Copy)] // Not all the codes are currently used but let's have a complete return status // enumeration from the SCMI specification here. @@ -75,6 +91,11 @@ impl ReturnStatus { } } +/// Representation of [MessageValue] sequence used to construct [ScmiResponse]. +/// +/// The sequence includes the response code (see the helper constructors for +/// adding them) but it doesn't include the SCMI message header. The header is +/// added in [ScmiResponse]. struct Response { values: MessageValues, } @@ -105,6 +126,9 @@ impl From<&MessageValues> for Response { } } +/// SCMI response in SCMI representation byte. +/// +/// Use [ScmiResponse::from] function to construct it. #[derive(Debug)] pub struct ScmiResponse { header: MessageHeader, @@ -112,6 +136,8 @@ pub struct ScmiResponse { } impl ScmiResponse { + /// Creates [ScmiResponse] instance from the (unchanged) SCMI request + /// `header` and a [Response] composed of [MessageValue]s. fn from(header: MessageHeader, response: Response) -> Self { debug!("response arguments: {:?}", response.values); let mut ret_bytes: Vec = vec![]; @@ -155,6 +181,10 @@ impl ScmiResponse { } } +/// Representation of a parsed SCMI request. +/// +/// Use [ScmiRequest::get_unsigned] and [ScmiRequest::get_usize] functions to +/// retrieve its parameters as `u32` and `usize` values respectively. pub struct ScmiRequest { header: MessageHeader, // 32-bit unsigned integer, split below: message_id: MessageId, // bits 7:0 @@ -246,12 +276,20 @@ enum ParameterType { type ParameterSpecification = Vec; type HandlerFunction = fn(&ScmiHandler, &ScmiRequest) -> Response; + +/// Specification of an SCMI message handler. +/// +/// No need to create this directly, use [HandlerMap::bind] to add message +/// handlers. struct HandlerInfo { name: String, parameters: ParameterSpecification, function: HandlerFunction, } +/// Mapping of SCMI protocols and messages to handlers. +/// +/// See [HandlerMap::new] and [HandlerMap::bind] how to add new handlers. // HandlerMap layout is suboptimal but let's prefer simplicity for now. struct HandlerMap(HashMap<(ProtocolId, MessageId), HandlerInfo>); @@ -271,6 +309,13 @@ impl HandlerMap { self.0.get(&(protocol_id, message_id)) } + /// Add a handler for a SCMI protocol message. + /// + /// `protocol_id` & `message_id` specify the corresponding SCMI protocol + /// and message codes identifying the request to handle using `function`. + /// Expected SCMI parameters (unsigned or signed 32-bit integers) are + /// specified in `parameters`. `name` serves just for identifying the + /// handlers easily in logs and error messages. fn bind( &mut self, protocol_id: ProtocolId, @@ -295,6 +340,7 @@ impl HandlerMap { ); } + /// Adds SCMI base protocol handlers. fn make_base_handlers(&mut self) { self.bind( BASE_PROTOCOL_ID, @@ -353,6 +399,7 @@ impl HandlerMap { ); } + /// Adds SCMI sensor protocol handlers. fn make_sensor_handlers(&mut self) { self.bind( SENSOR_PROTOCOL_ID, @@ -505,9 +552,24 @@ pub enum ScmiDeviceError { UnsupportedRequest, } +/// The highest representation of an SCMI device. +/// +/// A device is an entity bound to a SCMI protocol that can take an SCMI +/// message id and parameters and respond with [MessageValue]s. See +/// [crate::devices] how devices are defined and created. pub trait ScmiDevice: Send { + /// Initializes the device (if needed). + /// + /// If any error occurs preventing the operation of the device, a + /// corresponding error message must be returned. fn initialize(&mut self) -> Result<(), DeviceError>; + /// Returns the SCMI protocol id that the device is attached to. fn protocol(&self) -> ProtocolId; + /// Handles an SCMI request. + /// + /// `message_id` is an SCMI message id from the + /// given SCMI protocol and `parameters` are the SCMI request parameters + /// already represented as [MessageValue]s. fn handle( &mut self, message_id: MessageId, @@ -517,6 +579,7 @@ pub trait ScmiDevice: Send { type DeviceList = Vec>; +/// Mapping of SCMI protocols to devices that can handle them. struct DeviceMap(Arc>>); impl DeviceMap { @@ -572,6 +635,13 @@ pub struct ScmiHandler { } impl ScmiHandler { + /// Creates an instance for handling SCMI requests. + /// + /// The function also defines handlers for particular SCMI protocols. + /// It creates a [HandlerMap] and then adds SCMI message handlers to + /// it using [HandlerMap::bind] function. This is the place (i.e. the + /// functions called from here) where to add bindings for SCMI protocols and + /// messages. pub fn new() -> Self { Self { handlers: HandlerMap::new(), diff --git a/crates/scmi/src/vhu_scmi.rs b/crates/scmi/src/vhu_scmi.rs index 45d9cd0..fd8b443 100644 --- a/crates/scmi/src/vhu_scmi.rs +++ b/crates/scmi/src/vhu_scmi.rs @@ -2,6 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 // Based on https://github.com/rust-vmm/vhost-device, Copyright by Linaro Ltd. +//! General part of the vhost-user SCMI backend. Nothing very different from +//! the other rust-vmm backends. + use log::{debug, error, warn}; use std::io; use std::io::Result as IoResult; @@ -82,14 +85,14 @@ pub struct VuScmiBackend { event_idx: bool, pub exit_event: EventFd, mem: Option>, - // Event vring and descriptors serve for asynchronous responses and notifications. - // They are obtained from the driver and we store them here for later use. - // (We currently don't implement asynchronous responses or notifications but we support - // the event queue because the Linux VIRTIO SCMI driver seems to be unhappy if it is not - // present. And it doesn't harm to be ready for possible event queue use in future.) + /// Event vring and descriptors serve for asynchronous responses and notifications. + /// They are obtained from the driver and we store them here for later use. + /// (We currently don't implement asynchronous responses or notifications but we support + /// the event queue because the Linux VIRTIO SCMI driver seems to be unhappy if it is not + /// present. And it doesn't harm to be ready for possible event queue use in future.) event_vring: Option, event_descriptors: Vec>>, - // The abstraction of request handling, with all the needed information stored inside. + /// The abstraction of request handling, with all the needed information stored inside. scmi_handler: ScmiHandler, } From 0261d315ac1537dfc3af9fdc70554a2ed99b4385 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Mon, 14 Aug 2023 17:13:41 +0200 Subject: [PATCH 151/189] scmi: Improve command line processing When a device help is requested with `-d help', the socket argument is still required. This patch: - replaces `-d help' with --help-devices; - stops requiring the socket argument in such a case; - prints help in case of command line parsing errors. Signed-off-by: Milan Zamazal --- crates/scmi/README.md | 2 +- crates/scmi/src/devices/common.rs | 9 +--- crates/scmi/src/main.rs | 75 ++++++++++++++++++++++--------- crates/scmi/src/vhu_scmi.rs | 5 +-- 4 files changed, 56 insertions(+), 35 deletions(-) diff --git a/crates/scmi/README.md b/crates/scmi/README.md index 5b97275..1d0c550 100644 --- a/crates/scmi/README.md +++ b/crates/scmi/README.md @@ -31,7 +31,7 @@ the Examples section below. Can be used multiple times for multiple exposed devices. If no device is specified then no device will be provided to the guest OS but VirtIO SCMI will be still available there. - Use `help` as the device ID to list help on all the available devices. + Use `--help-devices` to list help on all the available devices. You can set `RUST_LOG` environment variable to `debug` to get maximum messages on the standard error output. diff --git a/crates/scmi/src/devices/common.rs b/crates/scmi/src/devices/common.rs index d79dcd1..ec51b2c 100644 --- a/crates/scmi/src/devices/common.rs +++ b/crates/scmi/src/devices/common.rs @@ -12,7 +12,6 @@ use std::collections::{HashMap, HashSet}; use std::ffi::OsString; use std::fmt::Write; -use std::process::exit; use itertools::Itertools; use log::debug; @@ -27,12 +26,7 @@ use crate::scmi::{ use super::{fake, iio}; -/// Enumeration of vhost-device-scmi exit codes. -// TODO: It should be better placed elsewhere but it's currently used only here. -enum ExitCodes { - Help = 1, -} - +/// Non-SCMI related device errors. #[derive(Debug, ThisError)] pub enum DeviceError { #[error("{0}")] @@ -225,7 +219,6 @@ fn devices_help() -> String { pub fn print_devices_help() { let help = devices_help(); println!("{}", help); - exit(ExitCodes::Help as i32); } // Common sensor infrastructure diff --git a/crates/scmi/src/main.rs b/crates/scmi/src/main.rs index 60b5072..36c9561 100644 --- a/crates/scmi/src/main.rs +++ b/crates/scmi/src/main.rs @@ -17,7 +17,7 @@ //! The daemon listens on a socket that is specified using `--socket-path` //! command line option. Usually at least one exposed device is specified, //! which is done using `--device` command line option. It can be used more -//! than once, for different devices. `--device help` lists the available +//! than once, for different devices. `--help-devices` lists the available //! devices and their options. //! //! The daemon normally logs info and higher messages to the standard error @@ -36,14 +36,14 @@ mod devices; mod scmi; mod vhu_scmi; -use devices::common::{DeviceDescription, DeviceProperties}; +use devices::common::{print_devices_help, DeviceDescription, DeviceProperties}; use std::{ process::exit, sync::{Arc, RwLock}, }; -use clap::Parser; +use clap::{CommandFactory, Parser}; use itertools::Itertools; use log::{debug, error, info, warn}; @@ -58,16 +58,15 @@ type Result = std::result::Result; #[derive(Parser)] struct ScmiArgs { // Location of vhost-user Unix domain socket. - #[clap(short, long, help = "vhost-user socket to use")] - socket_path: String, + // Required, unless one of the --help options is used. + #[clap(short, long, help = "vhost-user socket to use (required)")] + socket_path: Option, // Specification of SCMI devices to create. - #[clap( - short, - long, - help = "Devices to expose (use `help' device for more info)" - )] + #[clap(short, long, help = "Devices to expose")] #[arg(num_args(1..))] device: Vec, + #[clap(long, help = "Print help on available devices")] + help_devices: bool, } pub struct VuScmiConfig { @@ -79,9 +78,12 @@ impl TryFrom for VuScmiConfig { type Error = String; fn try_from(cmd_args: ScmiArgs) -> Result { - let socket_path = cmd_args.socket_path.trim().to_string(); - let device_iterator = cmd_args.device.iter(); + if cmd_args.socket_path.is_none() { + return Result::Err("Required argument socket-path was not provided".to_string()); + } + let socket_path = cmd_args.socket_path.unwrap().trim().to_string(); let mut devices: DeviceDescription = vec![]; + let device_iterator = cmd_args.device.iter(); for d in device_iterator { let mut split = d.split(','); let name = split.next().unwrap().to_owned(); @@ -139,18 +141,32 @@ fn start_backend(config: VuScmiConfig) -> Result<()> { } } +fn process_args(args: ScmiArgs) -> Option { + if args.help_devices { + print_devices_help(); + None + } else { + Some(args) + } +} + +fn print_help(message: &String) { + println!("{message}\n"); + let mut command = ScmiArgs::command(); + command.print_help().unwrap(); +} + fn main() { env_logger::init(); - match VuScmiConfig::try_from(ScmiArgs::parse()) { - Ok(config) => { - if let Err(error) = start_backend(config) { - error!("{error}"); - exit(1); + if let Some(args) = process_args(ScmiArgs::parse()) { + match VuScmiConfig::try_from(args) { + Ok(config) => { + if let Err(error) = start_backend(config) { + error!("{error}"); + exit(1); + } } - } - Err(message) => { - println!("{message}"); - // TODO: print help + Err(message) => print_help(&message), } } } @@ -170,7 +186,7 @@ mod tests { -d fake,name=bar" ); let params: Vec<&str> = params_string.split_whitespace().collect(); - let args: ScmiArgs = Parser::parse_from(params); + let args: ScmiArgs = process_args(Parser::parse_from(params)).unwrap(); let config = VuScmiConfig::try_from(args).unwrap(); assert_eq!(config.socket_path, path); let devices = vec![ @@ -189,4 +205,19 @@ mod tests { ]; assert_eq!(config.devices, devices); } + + #[test] + fn test_device_help_processing() { + let params_string = "binary --help-devices".to_string(); + let params: Vec<&str> = params_string.split_whitespace().collect(); + let args: ScmiArgs = Parser::parse_from(params); + let processed = process_args(args); + assert!(processed.is_none()); + } + + #[test] + fn test_help() { + // No way known to me to capture print_help() output from clap. + print_help(&String::from("test")); + } } diff --git a/crates/scmi/src/vhu_scmi.rs b/crates/scmi/src/vhu_scmi.rs index fd8b443..856a1b1 100644 --- a/crates/scmi/src/vhu_scmi.rs +++ b/crates/scmi/src/vhu_scmi.rs @@ -23,7 +23,7 @@ use vm_memory::{ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; -use crate::devices::common::{available_devices, print_devices_help, DeviceError}; +use crate::devices::common::{available_devices, DeviceError}; use crate::scmi::{MessageHeader, ScmiHandler, ScmiRequest}; use crate::VuScmiConfig; @@ -101,9 +101,6 @@ impl VuScmiBackend { let mut handler = ScmiHandler::new(); let device_mapping = available_devices(); for (name, properties) in config.devices.iter() { - if name == "help" { - print_devices_help(); - } match device_mapping.get(name.as_str()) { Some(specification) => match (specification.constructor)(properties) { Ok(mut device) => { From 07cbe73dc83ea3c15a3b835b432e26c3502c3908 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Wed, 30 Aug 2023 16:34:36 +0200 Subject: [PATCH 152/189] scmi: Improve output on backend configuration error Print a properly formatted error to both the log and terminal. Signed-off-by: Milan Zamazal --- crates/scmi/src/main.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/scmi/src/main.rs b/crates/scmi/src/main.rs index 36c9561..8a925d8 100644 --- a/crates/scmi/src/main.rs +++ b/crates/scmi/src/main.rs @@ -107,8 +107,12 @@ impl TryFrom for VuScmiConfig { fn start_backend(config: VuScmiConfig) -> Result<()> { loop { debug!("Starting backend"); - // TODO: Print a nice error message on backend configuration failure. - let backend = Arc::new(RwLock::new(VuScmiBackend::new(&config).unwrap())); + let backend_instance = VuScmiBackend::new(&config); + if let Err(error) = backend_instance { + return Err(error.to_string()); + } + + let backend = Arc::new(RwLock::new(backend_instance.unwrap())); let listener = Listener::new(config.socket_path.clone(), true).unwrap(); let mut daemon = VhostUserDaemon::new( "vhost-device-scmi".to_owned(), @@ -163,6 +167,7 @@ fn main() { Ok(config) => { if let Err(error) = start_backend(config) { error!("{error}"); + println!("{error}"); exit(1); } } From 58650819ff745ad15d8af6b5a1adf05bdc1e6cd1 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Mon, 21 Aug 2023 17:53:16 +0200 Subject: [PATCH 153/189] Increase coverage score CI complains not only when the score is too low but also when it is too high. Accommodate the increased average coverage caused by adding vhost-device-scmi and its unit tests. Signed-off-by: Milan Zamazal --- coverage_config_x86_64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 1773554..0586cba 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 68.2, + "coverage_score": 71.0, "exclude_path": "", "crate_features": "" } From 923fa4312aff0c8b918d35dd4eb4836167d96afb Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Mon, 4 Sep 2023 10:45:57 +0200 Subject: [PATCH 154/189] scmi: Add CHANGELOG.md Signed-off-by: Milan Zamazal --- crates/scmi/CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 crates/scmi/CHANGELOG.md diff --git a/crates/scmi/CHANGELOG.md b/crates/scmi/CHANGELOG.md new file mode 100644 index 0000000..51d3f04 --- /dev/null +++ b/crates/scmi/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog +## [Unreleased] + +### Added + +### Changed + +### Fixed + +### Deprecated + +## [0.1.0] + +First release + From c0a5c39b99787d67c0121f620f78a3f38e214e72 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Mon, 4 Sep 2023 11:16:32 +0200 Subject: [PATCH 155/189] scmi: Add some notes on testing to README Signed-off-by: Milan Zamazal --- crates/scmi/README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/scmi/README.md b/crates/scmi/README.md index 1d0c550..d16c090 100644 --- a/crates/scmi/README.md +++ b/crates/scmi/README.md @@ -70,7 +70,25 @@ implemented. See source code (`scmi` crate) documentation for details and how to add more protocols, host device bindings or other functionality. -## Kernel support for testing +## Testing + +SCMI is supported only on Arm in Linux. This restriction doesn't +apply to the host, which can be any architecture as long as the guest +is Arm. + +The easiest way to test it on the guest side is using the Linux SCMI +Industrial I/O driver there. If an 3-axes accelerometer or gyroscope +VirtIO SCMI device is present and the guest kernel is compiled with +`CONFIG_IIO_SCMI` enabled then the device should be available in +`/sys/bus/iio/devices/`. The vhost-device-scmi fake device is +suitable for this. + +Of course, other means of accessing SCMI devices can be used too. The +following Linux kernel command line can be useful to obtain SCMI trace +information, in addition to SCMI related messages in dmesg: +`trace_event=scmi:* ftrace=function ftrace_filter=scmi*`. + +### Kernel support for testing `kernel` subdirectory contains [instructions](kernel/iio-dummy/README.md) how to create emulated From 5fc40c083861c1609ac3cd5367b3d96bfe48691e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 15:16:14 +0000 Subject: [PATCH 156/189] build(deps): bump pest from 2.7.2 to 2.7.3 Bumps [pest](https://github.com/pest-parser/pest) from 2.7.2 to 2.7.3. - [Release notes](https://github.com/pest-parser/pest/releases) - [Commits](https://github.com/pest-parser/pest/compare/v2.7.2...v2.7.3) --- updated-dependencies: - dependency-name: pest dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7258ce1..eab1cf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -728,10 +728,11 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pest" -version = "2.7.2" +version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" +checksum = "d7a4d085fd991ac8d5b05a147b437791b4260b76326baf0fc60cf7c9c27ecd33" dependencies = [ + "memchr", "thiserror", "ucd-trie", ] From 9c05c28a3a89928a3722d602fa81ad7bb4c59820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 12 Sep 2023 14:57:10 +0100 Subject: [PATCH 157/189] build(deps): bump vm-memory to 0.12.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should hopefully stop cargo audit complaining about our old version. Signed-off-by: Alex Bennée --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eab1cf5..f85559c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1362,9 +1362,9 @@ dependencies = [ [[package]] name = "vm-memory" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3750e9b70da7f2ce2f7bf942c886d45f9bae064135c398f05635bf77e926a2ef" +checksum = "9dc276f0d00c17b9aeb584da0f1e1c673df0d183cc2539e3636ec8cbc5eae99b" dependencies = [ "arc-swap", "bitflags 1.3.2", From 95d8e1d846ac011c3578337c76ccb2ebbc275557 Mon Sep 17 00:00:00 2001 From: Milan Zamazal Date: Tue, 12 Sep 2023 11:56:30 +0200 Subject: [PATCH 158/189] scmi: Add a SCMI link to the top README Signed-off-by: Milan Zamazal --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 372f225..a18bed6 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Here is the list of device backends that we support: - [GPIO](https://github.com/rust-vmm/vhost-device/blob/main/crates/gpio/README.md) - [I2C](https://github.com/rust-vmm/vhost-device/blob/main/crates/i2c/README.md) - [RNG](https://github.com/rust-vmm/vhost-device/blob/main/crates/rng/README.md) +- [SCMI](https://github.com/rust-vmm/vhost-device/blob/main/crates/scmi/README.md) - [SCSI](https://github.com/rust-vmm/vhost-device/blob/main/crates/scsi/README.md) - [VSOCK](https://github.com/rust-vmm/vhost-device/blob/main/crates/vsock/README.md) From 212d6282bb52c4d0b5233f35f39dd78473c3a777 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 07:47:58 +0000 Subject: [PATCH 159/189] build(deps): bump pest_derive from 2.7.2 to 2.7.3 Bumps [pest_derive](https://github.com/pest-parser/pest) from 2.7.2 to 2.7.3. - [Release notes](https://github.com/pest-parser/pest/releases) - [Commits](https://github.com/pest-parser/pest/compare/v2.7.2...v2.7.3) --- updated-dependencies: - dependency-name: pest_derive dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f85559c..a830e34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -739,9 +739,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.2" +version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" +checksum = "a2bee7be22ce7918f641a33f08e3f43388c7656772244e2bbb2477f44cc9021a" dependencies = [ "pest", "pest_generator", @@ -749,9 +749,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.2" +version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" +checksum = "d1511785c5e98d79a05e8a6bc34b4ac2168a0e3e92161862030ad84daa223141" dependencies = [ "pest", "pest_meta", @@ -762,9 +762,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.2" +version = "2.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" +checksum = "b42f0394d3123e33353ca5e1e89092e533d2cc490389f2bd6131c43c634ebc5f" dependencies = [ "once_cell", "pest", From 802d09c542216b6f7beb1df30563fec5ba942946 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 07:56:42 +0000 Subject: [PATCH 160/189] build(deps): bump serde_json from 1.0.105 to 1.0.106 Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.105 to 1.0.106. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.105...v1.0.106) --- updated-dependencies: - dependency-name: serde_json dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a830e34..f560dfb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -959,9 +959,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" dependencies = [ "itoa", "ryu", From 5f1c19d9e5c97947f921e5dfa76f9fd771c2d2ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 07:35:04 +0000 Subject: [PATCH 161/189] build(deps): bump rust-vmm-ci from `7c1057e` to `665f31f` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `7c1057e` to `665f31f`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/7c1057e9bcba7ed5090fd62b9fef0570fadb06e6...665f31f4b4e050270604d9b534058fa9b9d39adc) --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 7c1057e..665f31f 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 7c1057e9bcba7ed5090fd62b9fef0570fadb06e6 +Subproject commit 665f31f4b4e050270604d9b534058fa9b9d39adc From 313ca7c0f2096b8b68e514e32a5e86242202b77f Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Wed, 13 Sep 2023 12:13:38 +0200 Subject: [PATCH 162/189] gpio: silence (valid) clippy warning for now This is a valid issue, but resolving takes some time. Meanwhile, we do not want to block the CI. Issue #442 was filed to track finding a real fix. This should be reverted once the real fix happens. Signed-off-by: Erik Schilling --- crates/gpio/src/gpio.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/crates/gpio/src/gpio.rs b/crates/gpio/src/gpio.rs index e891a4e..e9e9d5e 100644 --- a/crates/gpio/src/gpio.rs +++ b/crates/gpio/src/gpio.rs @@ -219,11 +219,22 @@ impl GpioDevice for PhysDevice { .set_consumer("vhu-gpio") .map_err(Error::GpiodFailed)?; - state.request = Some(Arc::new(Mutex::new( - self.chip - .request_lines(Some(&rconfig), &lconfig) - .map_err(Error::GpiodFailed)?, - ))); + // This is causing a warning since libgpiod's request_config is + // not `Send`. + // We, however, unsafely claim that it is by marking PhysDevice as + // `Send`. This is wrong, but until we figure out what to do, we + // just silence the clippy warning here. + // + // https://github.com/rust-vmm/vhost-device/issues/442 tracks + // finding a solution to this. + #[allow(clippy::arc_with_non_send_sync)] + { + state.request = Some(Arc::new(Mutex::new( + self.chip + .request_lines(Some(&rconfig), &lconfig) + .map_err(Error::GpiodFailed)?, + ))); + } } Ok(()) From 2681d12435ffeda21025c5eb2398fb5d52601d73 Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Wed, 13 Sep 2023 13:43:11 +0200 Subject: [PATCH 163/189] tests: adjust code coverage after tool updates CI was updated and now claims higher code coverage. Adjusting the values to what the CI claims now. Signed-off-by: Erik Schilling --- coverage_config_x86_64.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 0586cba..bf64db5 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 71.0, + "coverage_score": 73.42, "exclude_path": "", "crate_features": "" } From 51c5bc04319786fe263c74093671d6e78791249d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 15:06:18 +0000 Subject: [PATCH 164/189] build(deps): bump clap from 4.3.23 to 4.4.3 Bumps [clap](https://github.com/clap-rs/clap) from 4.3.23 to 4.4.3. - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/v4.3.23...v4.4.3) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 22 ++++++++++------------ crates/gpio/Cargo.toml | 2 +- crates/i2c/Cargo.toml | 2 +- crates/rng/Cargo.toml | 2 +- crates/scmi/Cargo.toml | 2 +- crates/scsi/Cargo.toml | 2 +- crates/vsock/Cargo.toml | 2 +- 7 files changed, 16 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f560dfb..8170737 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,16 +24,15 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] @@ -63,9 +62,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" +checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" dependencies = [ "anstyle", "windows-sys", @@ -192,20 +191,19 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.23" +version = "4.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03aef18ddf7d879c15ce20f04826ef8418101c7e528014c3eeea13321047dca3" +checksum = "84ed82781cea27b43c9b106a979fe450a13a31aab0500595fb3fc06616de08e6" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.23" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ce6fffb678c9b80a70b6b6de0aad31df727623a70fd9a842c30cd573e2fa98" +checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" dependencies = [ "anstream", "anstyle", @@ -215,9 +213,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.12" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck 0.4.1", "proc-macro2", diff --git a/crates/gpio/Cargo.toml b/crates/gpio/Cargo.toml index 9570030..9f19c92 100644 --- a/crates/gpio/Cargo.toml +++ b/crates/gpio/Cargo.toml @@ -15,7 +15,7 @@ edition = "2021" xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] [dependencies] -clap = { version = "4.3", features = ["derive"] } +clap = { version = "4.4", features = ["derive"] } env_logger = "0.10" libc = "0.2" log = "0.4" diff --git a/crates/i2c/Cargo.toml b/crates/i2c/Cargo.toml index d44992e..398b983 100644 --- a/crates/i2c/Cargo.toml +++ b/crates/i2c/Cargo.toml @@ -15,7 +15,7 @@ edition = "2021" xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] [dependencies] -clap = { version = "4.3", features = ["derive"] } +clap = { version = "4.4", features = ["derive"] } env_logger = "0.10" libc = "0.2" log = "0.4" diff --git a/crates/rng/Cargo.toml b/crates/rng/Cargo.toml index e0a17e4..0770dd5 100644 --- a/crates/rng/Cargo.toml +++ b/crates/rng/Cargo.toml @@ -13,7 +13,7 @@ edition = "2021" xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] [dependencies] -clap = { version = "4.3", features = ["derive"] } +clap = { version = "4.4", features = ["derive"] } env_logger = "0.10" epoll = "4.3" libc = "0.2" diff --git a/crates/scmi/Cargo.toml b/crates/scmi/Cargo.toml index be2929f..588caaf 100644 --- a/crates/scmi/Cargo.toml +++ b/crates/scmi/Cargo.toml @@ -10,7 +10,7 @@ license = "Apache-2.0 OR BSD-3-Clause" edition = "2021" [dependencies] -clap = { version = "4.3", features = ["derive"] } +clap = { version = "4.4", features = ["derive"] } env_logger = "0.10" itertools = "0.10" log = "0.4" diff --git a/crates/scsi/Cargo.toml b/crates/scsi/Cargo.toml index 3cba3c9..5f9511f 100644 --- a/crates/scsi/Cargo.toml +++ b/crates/scsi/Cargo.toml @@ -15,7 +15,7 @@ edition = "2021" xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] [dependencies] -clap = { version = "4.3", features = ["derive"] } +clap = { version = "4.4", features = ["derive"] } env_logger = "0.10" epoll = "4.3" log = "0.4" diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index 46d4e16..c4371f3 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -14,7 +14,7 @@ xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] [dependencies] byteorder = "1" -clap = { version = "4.3", features = ["derive"] } +clap = { version = "4.4", features = ["derive"] } env_logger = "0.10" epoll = "4.3.2" futures = { version = "0.3", features = ["thread-pool"] } From 758605aa09f3630bd2ff308e992ef69fe5a67021 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 04:03:05 +0000 Subject: [PATCH 165/189] build(deps): bump serde_json from 1.0.106 to 1.0.107 Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.106 to 1.0.107. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.106...v1.0.107) --- updated-dependencies: - dependency-name: serde_json dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8170737..ecd43a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -957,9 +957,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", From 8d5be26c46e9ef3841bdde9b38a33e354c79ed03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:50:20 +0000 Subject: [PATCH 166/189] build(deps): bump aho-corasick from 1.0.4 to 1.0.5 Bumps [aho-corasick](https://github.com/BurntSushi/aho-corasick) from 1.0.4 to 1.0.5. - [Commits](https://github.com/BurntSushi/aho-corasick/compare/1.0.4...1.0.5) --- updated-dependencies: - dependency-name: aho-corasick dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ecd43a9..da6a4fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" dependencies = [ "memchr", ] From a3fc80b2692a2a885c52944ab1e7ba62a98ae46b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 08:57:38 +0000 Subject: [PATCH 167/189] build(deps): bump typenum from 1.16.0 to 1.17.0 Bumps [typenum](https://github.com/paholg/typenum) from 1.16.0 to 1.17.0. - [Release notes](https://github.com/paholg/typenum/releases) - [Changelog](https://github.com/paholg/typenum/blob/main/CHANGELOG.md) - [Commits](https://github.com/paholg/typenum/compare/v1.16.0...v1.17.0) --- updated-dependencies: - dependency-name: typenum dependency-type: indirect update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da6a4fe..16bd70a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1136,9 +1136,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" From 38caab24c5087551d6ec12d0002df798e5e4f5ac Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Mon, 11 Sep 2023 20:37:45 -0700 Subject: [PATCH 168/189] vsock: Don't allow duplicate CIDs Currently, while updating the `cid_map`, it is not checked if the map already contained a key with the same CID before. So, fail to create the vsock thread if the CID is already in use. Signed-off-by: Priyansh Rathi --- crates/vsock/src/vhu_vsock.rs | 2 ++ crates/vsock/src/vhu_vsock_thread.rs | 44 +++++++++++++++++++++------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index 0d25b38..e67cc16 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -135,6 +135,8 @@ pub(crate) enum Error { EventFdCreate(std::io::Error), #[error("Raw vsock packets queue is empty")] EmptyRawPktsQueue, + #[error("CID already in use by another vsock device")] + CidAlreadyInUse, } impl std::convert::From for std::io::Error { diff --git a/crates/vsock/src/vhu_vsock_thread.rs b/crates/vsock/src/vhu_vsock_thread.rs index 0c2ab2f..b0fe774 100644 --- a/crates/vsock/src/vhu_vsock_thread.rs +++ b/crates/vsock/src/vhu_vsock_thread.rs @@ -111,14 +111,21 @@ impl VhostUserVsockThread { cid_map.clone(), ); - cid_map.write().unwrap().insert( - guest_cid, - ( - thread_backend.raw_pkts_queue.clone(), - groups_set, - sibling_event_fd.try_clone().unwrap(), - ), - ); + { + let mut cid_map = cid_map.write().unwrap(); + if cid_map.contains_key(&guest_cid) { + return Err(Error::CidAlreadyInUse); + } + + cid_map.insert( + guest_cid, + ( + thread_backend.raw_pkts_queue.clone(), + groups_set, + sibling_event_fd.try_clone().unwrap(), + ), + ); + } let thread = VhostUserVsockThread { mem: None, @@ -824,9 +831,14 @@ mod tests { .join("test_vsock_thread_failures.vsock") .display() .to_string(); - let mut t = - VhostUserVsockThread::new(vsock_socket_path, 3, CONN_TX_BUF_SIZE, groups, cid_map) - .unwrap(); + let mut t = VhostUserVsockThread::new( + vsock_socket_path, + 3, + CONN_TX_BUF_SIZE, + groups.clone(), + cid_map.clone(), + ) + .unwrap(); assert!(VhostUserVsockThread::epoll_register(-1, -1, epoll::Events::EPOLLIN).is_err()); assert!(VhostUserVsockThread::epoll_modify(-1, -1, epoll::Events::EPOLLIN).is_err()); assert!(VhostUserVsockThread::epoll_unregister(-1, -1).is_err()); @@ -848,6 +860,16 @@ mod tests { assert!(t.process_rx(&vring, false).is_err()); assert!(t.process_rx(&vring, true).is_err()); + // trying to use a CID that is already in use should fail + let vsock_socket_path2 = test_dir + .path() + .join("test_vsock_thread_failures2.vsock") + .display() + .to_string(); + let t2 = + VhostUserVsockThread::new(vsock_socket_path2, 3, CONN_TX_BUF_SIZE, groups, cid_map); + assert!(t2.is_err()); + test_dir.close().unwrap(); } } From 9a766a28b7cf8a265abf92986d36eb9f99f5a7ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 04:23:59 +0000 Subject: [PATCH 169/189] build(deps): bump regex from 1.9.3 to 1.9.4 Bumps [regex](https://github.com/rust-lang/regex) from 1.9.3 to 1.9.4. - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/1.9.3...1.9.4) --- updated-dependencies: - dependency-name: regex dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 16bd70a..d6eaa66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -862,9 +862,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.3" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" dependencies = [ "aho-corasick", "memchr", @@ -874,9 +874,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" dependencies = [ "aho-corasick", "memchr", From b39be694a49f71f01aa00c58f9eba38c1ce6cdc5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 04:23:45 +0000 Subject: [PATCH 170/189] build(deps): bump shlex from 1.1.0 to 1.2.0 Bumps [shlex](https://github.com/comex/rust-shlex) from 1.1.0 to 1.2.0. - [Changelog](https://github.com/comex/rust-shlex/blob/master/CHANGELOG.md) - [Commits](https://github.com/comex/rust-shlex/commits) --- updated-dependencies: - dependency-name: shlex dependency-type: indirect update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d6eaa66..0f19d51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -992,9 +992,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" [[package]] name = "slab" From ed5b597c70549b12d406fa98313196ee546d4beb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 04:23:38 +0000 Subject: [PATCH 171/189] build(deps): bump hermit-abi from 0.3.2 to 0.3.3 Bumps [hermit-abi](https://github.com/hermitcore/hermit-rs) from 0.3.2 to 0.3.3. - [Release notes](https://github.com/hermitcore/hermit-rs/releases) - [Commits](https://github.com/hermitcore/hermit-rs/compare/hermit-abi-0.3.2...hermit-abi-0.3.3) --- updated-dependencies: - dependency-name: hermit-abi dependency-type: indirect update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f19d51..35a55b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -511,9 +511,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "humantime" From 7b2632b509b61ed9d408b68cb3101f2913eb19f9 Mon Sep 17 00:00:00 2001 From: Jeongik Cha Date: Tue, 19 Sep 2023 23:12:00 +0900 Subject: [PATCH 172/189] [vsock] refactor VhostUserVsockThread worker For now, VhostUserVsockThread uses thread pool executor from futures, but it doesn't need to use thread pool executor and futures because we just need background worker thread, and a way to let it work. So I removed unnecessary external dependency and made the logic simpler by using just thread and channel Signed-off-by: Jeongik Cha --- Cargo.lock | 128 -------------------------- crates/vsock/Cargo.toml | 1 - crates/vsock/src/vhu_vsock.rs | 2 - crates/vsock/src/vhu_vsock_thread.rs | 129 +++++++++++++++------------ 4 files changed, 71 insertions(+), 189 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 35a55b2..dc722c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,12 +93,6 @@ dependencies = [ "syn 2.0.29", ] -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "base64" version = "0.13.1" @@ -362,96 +356,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", - "num_cpus", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -665,16 +569,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "num_enum" version = "0.7.0" @@ -769,18 +663,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "pin-project-lite" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "pkg-config" version = "0.3.27" @@ -996,15 +878,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - [[package]] name = "strsim" version = "0.10.0" @@ -1299,7 +1172,6 @@ dependencies = [ "config", "env_logger", "epoll", - "futures", "log", "serde", "serde_yaml", diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index c4371f3..3788e63 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -17,7 +17,6 @@ byteorder = "1" clap = { version = "4.4", features = ["derive"] } env_logger = "0.10" epoll = "4.3.2" -futures = { version = "0.3", features = ["thread-pool"] } log = "0.4" thiserror = "1.0" vhost = { version = "0.8", features = ["vhost-user-slave"] } diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vsock/src/vhu_vsock.rs index e67cc16..34ef99a 100644 --- a/crates/vsock/src/vhu_vsock.rs +++ b/crates/vsock/src/vhu_vsock.rs @@ -115,8 +115,6 @@ pub(crate) enum Error { IterateQueue, #[error("No rx request available")] NoRequestRx, - #[error("Unable to create thread pool")] - CreateThreadPool(std::io::Error), #[error("Packet missing data buffer")] PktBufMissing, #[error("Failed to connect to unix socket")] diff --git a/crates/vsock/src/vhu_vsock_thread.rs b/crates/vsock/src/vhu_vsock_thread.rs index b0fe774..fcefc4a 100644 --- a/crates/vsock/src/vhu_vsock_thread.rs +++ b/crates/vsock/src/vhu_vsock_thread.rs @@ -12,10 +12,11 @@ use std::{ net::{UnixListener, UnixStream}, prelude::{AsRawFd, FromRawFd, RawFd}, }, - sync::{Arc, RwLock}, + sync::mpsc::Sender, + sync::{mpsc, Arc, RwLock}, + thread, }; -use futures::executor::{ThreadPool, ThreadPoolBuilder}; use log::warn; use vhost_user_backend::{VringEpollHandler, VringRwLock, VringT}; use virtio_queue::QueueOwnedT; @@ -42,6 +43,15 @@ enum RxQueueType { Standard, RawPkts, } + +// Data which is required by a worker handling event idx. +struct EventData { + vring: VringRwLock, + event_idx: bool, + head_idx: u16, + used_len: usize, +} + pub(crate) struct VhostUserVsockThread { /// Guest memory map. pub mem: Option>, @@ -61,8 +71,8 @@ pub(crate) struct VhostUserVsockThread { pub thread_backend: VsockThreadBackend, /// CID of the guest. guest_cid: u64, - /// Thread pool to handle event idx. - pool: ThreadPool, + /// Channel to a worker which handles event idx. + sender: Sender, /// host side port on which application listens. local_port: Wrapping, /// The tx buffer size @@ -126,7 +136,15 @@ impl VhostUserVsockThread { ), ); } - + let (sender, receiver) = mpsc::channel::(); + thread::spawn(move || loop { + // TODO: Understand why doing the following in the background thread works. + // maybe we'd better have thread pool for the entire application if necessary. + let Ok(event_data) = receiver.recv() else { + break; + }; + Self::vring_handle_event(event_data); + }); let thread = VhostUserVsockThread { mem: None, event_idx: false, @@ -137,10 +155,7 @@ impl VhostUserVsockThread { epoll_file, thread_backend, guest_cid, - pool: ThreadPoolBuilder::new() - .pool_size(1) - .create() - .map_err(Error::CreateThreadPool)?, + sender, local_port: Wrapping(0), tx_buffer_size, sibling_event_fd, @@ -152,6 +167,37 @@ impl VhostUserVsockThread { Ok(thread) } + fn vring_handle_event(event_data: EventData) { + if event_data.event_idx { + if event_data + .vring + .add_used(event_data.head_idx, event_data.used_len as u32) + .is_err() + { + warn!("Could not return used descriptors to ring"); + } + match event_data.vring.needs_notification() { + Err(_) => { + warn!("Could not check if queue needs to be notified"); + event_data.vring.signal_used_queue().unwrap(); + } + Ok(needs_notification) => { + if needs_notification { + event_data.vring.signal_used_queue().unwrap(); + } + } + } + } else { + if event_data + .vring + .add_used(event_data.head_idx, event_data.used_len as u32) + .is_err() + { + warn!("Could not return used descriptors to ring"); + } + event_data.vring.signal_used_queue().unwrap(); + } + } /// Register a file with an epoll to listen for events in evset. pub fn epoll_register(epoll_fd: RawFd, fd: RawFd, evset: epoll::Events) -> Result<()> { epoll::ctl( @@ -504,31 +550,14 @@ impl VhostUserVsockThread { let vring = vring.clone(); let event_idx = self.event_idx; - - self.pool.spawn_ok(async move { - // TODO: Understand why doing the following in the pool works - if event_idx { - if vring.add_used(head_idx, used_len as u32).is_err() { - warn!("Could not return used descriptors to ring"); - } - match vring.needs_notification() { - Err(_) => { - warn!("Could not check if queue needs to be notified"); - vring.signal_used_queue().unwrap(); - } - Ok(needs_notification) => { - if needs_notification { - vring.signal_used_queue().unwrap(); - } - } - } - } else { - if vring.add_used(head_idx, used_len as u32).is_err() { - warn!("Could not return used descriptors to ring"); - } - vring.signal_used_queue().unwrap(); - } - }); + self.sender + .send(EventData { + vring, + event_idx, + head_idx, + used_len, + }) + .unwrap(); match rx_queue_type { RxQueueType::Standard => { @@ -661,30 +690,14 @@ impl VhostUserVsockThread { let vring = vring.clone(); let event_idx = self.event_idx; - - self.pool.spawn_ok(async move { - if event_idx { - if vring.add_used(head_idx, used_len as u32).is_err() { - warn!("Could not return used descriptors to ring"); - } - match vring.needs_notification() { - Err(_) => { - warn!("Could not check if queue needs to be notified"); - vring.signal_used_queue().unwrap(); - } - Ok(needs_notification) => { - if needs_notification { - vring.signal_used_queue().unwrap(); - } - } - } - } else { - if vring.add_used(head_idx, used_len as u32).is_err() { - warn!("Could not return used descriptors to ring"); - } - vring.signal_used_queue().unwrap(); - } - }); + self.sender + .send(EventData { + vring, + event_idx, + head_idx, + used_len, + }) + .unwrap(); } Ok(used_any) From 11cf80dcc97cf0915b68f57571eaad6977e80295 Mon Sep 17 00:00:00 2001 From: Jeongik Cha Date: Tue, 26 Sep 2023 17:22:57 +0900 Subject: [PATCH 173/189] [vsock] remove unused feature to reduce deps this project uses only yaml config, so remove some of features in config to reduce deps Signed-off-by: Jeongik Cha --- Cargo.lock | 214 +--------------------------------------- crates/vsock/Cargo.toml | 4 +- 2 files changed, 3 insertions(+), 215 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc722c0..8017b7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,17 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "aho-corasick" version = "1.0.5" @@ -93,12 +82,6 @@ dependencies = [ "syn 2.0.29", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "bindgen" version = "0.63.0" @@ -133,15 +116,6 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "byteorder" version = "1.4.3" @@ -236,53 +210,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" dependencies = [ "async-trait", - "json5", "lazy_static", "nom", "pathdiff", - "ron", - "rust-ini", "serde", - "serde_json", - "toml", "yaml-rust", ] -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "dlv-list" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" - [[package]] name = "either" version = "1.9.0" @@ -356,16 +290,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.10" @@ -383,15 +307,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.14.0" @@ -432,7 +347,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown", ] [[package]] @@ -467,17 +382,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" -[[package]] -name = "json5" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" -dependencies = [ - "pest", - "pest_derive", - "serde", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -596,16 +500,6 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" -[[package]] -name = "ordered-multimap" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" -dependencies = [ - "dlv-list", - "hashbrown 0.12.3", -] - [[package]] name = "pathdiff" version = "0.2.1" @@ -618,51 +512,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" -[[package]] -name = "pest" -version = "2.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a4d085fd991ac8d5b05a147b437791b4260b76326baf0fc60cf7c9c27ecd33" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bee7be22ce7918f641a33f08e3f43388c7656772244e2bbb2477f44cc9021a" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1511785c5e98d79a05e8a6bc34b4ac2168a0e3e92161862030ad84daa223141" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.29", -] - -[[package]] -name = "pest_meta" -version = "2.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42f0394d3123e33353ca5e1e89092e533d2cc490389f2bd6131c43c634ebc5f" -dependencies = [ - "once_cell", - "pest", - "sha2", -] - [[package]] name = "pkg-config" version = "0.3.27" @@ -771,27 +620,6 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" -[[package]] -name = "ron" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" -dependencies = [ - "base64", - "bitflags 1.3.2", - "serde", -] - -[[package]] -name = "rust-ini" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - [[package]] name = "rustc-hash" version = "1.1.0" @@ -837,17 +665,6 @@ dependencies = [ "syn 2.0.29", ] -[[package]] -name = "serde_json" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" -dependencies = [ - "itoa", - "ryu", - "serde", -] - [[package]] name = "serde_yaml" version = "0.9.25" @@ -861,17 +678,6 @@ dependencies = [ "unsafe-libyaml", ] -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "shlex" version = "1.2.0" @@ -1007,18 +813,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - [[package]] name = "unicode-ident" version = "1.0.11" @@ -1049,12 +843,6 @@ version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - [[package]] name = "vhost" version = "0.8.1" diff --git a/crates/vsock/Cargo.toml b/crates/vsock/Cargo.toml index 3788e63..ec90f38 100644 --- a/crates/vsock/Cargo.toml +++ b/crates/vsock/Cargo.toml @@ -26,8 +26,8 @@ virtio-queue = "0.9" virtio-vsock = "0.3.1" vm-memory = "0.12" vmm-sys-util = "0.11" -config = "0.13" -serde = "1" +config = { version = "0.13", default-features = false, features = ["yaml"] } +serde = { version = "1", features = ["derive"] } serde_yaml = "0.9" [dev-dependencies] From 5a7759e316ae3adfe37217d6765a1ccda25b2a55 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 26 Sep 2023 13:48:46 +0300 Subject: [PATCH 174/189] Prepend vhost-device- to crate directories Having the directory of a crate match the name of the crate (i.e. the one defined in its Cargo.toml) is intuitive and unambiguous. Signed-off-by: Manos Pitsidianakis --- Cargo.toml | 12 ++++++------ README.md | 12 ++++++------ crates/{gpio => vhost-device-gpio}/CHANGELOG.md | 0 crates/{gpio => vhost-device-gpio}/Cargo.toml | 0 crates/{gpio => vhost-device-gpio}/LICENSE-APACHE | 0 .../{gpio => vhost-device-gpio}/LICENSE-BSD-3-Clause | 0 crates/{gpio => vhost-device-gpio}/README.md | 0 crates/{gpio => vhost-device-gpio}/src/backend.rs | 0 crates/{gpio => vhost-device-gpio}/src/gpio.rs | 0 crates/{gpio => vhost-device-gpio}/src/main.rs | 0 crates/{gpio => vhost-device-gpio}/src/vhu_gpio.rs | 0 crates/{i2c => vhost-device-i2c}/CHANGELOG.md | 0 crates/{i2c => vhost-device-i2c}/Cargo.toml | 0 crates/{i2c => vhost-device-i2c}/LICENSE-APACHE | 0 .../{i2c => vhost-device-i2c}/LICENSE-BSD-3-Clause | 0 crates/{i2c => vhost-device-i2c}/README.md | 0 crates/{i2c => vhost-device-i2c}/src/i2c.rs | 0 crates/{i2c => vhost-device-i2c}/src/main.rs | 0 crates/{i2c => vhost-device-i2c}/src/vhu_i2c.rs | 0 crates/{rng => vhost-device-rng}/CHANGELOG.md | 0 crates/{rng => vhost-device-rng}/Cargo.toml | 0 crates/{rng => vhost-device-rng}/LICENSE-APACHE | 0 .../{rng => vhost-device-rng}/LICENSE-BSD-3-Clause | 0 crates/{rng => vhost-device-rng}/README.md | 0 crates/{rng => vhost-device-rng}/src/main.rs | 0 crates/{rng => vhost-device-rng}/src/vhu_rng.rs | 0 crates/{scmi => vhost-device-scmi}/CHANGELOG.md | 0 crates/{scmi => vhost-device-scmi}/Cargo.toml | 0 crates/{scmi => vhost-device-scmi}/LICENSE-APACHE | 0 .../{scmi => vhost-device-scmi}/LICENSE-BSD-3-Clause | 0 crates/{scmi => vhost-device-scmi}/README.md | 2 +- .../kernel/iio-dummy/.gitignore | 0 .../kernel/iio-dummy/Makefile | 0 .../kernel/iio-dummy/README.md | 0 .../kernel/iio-dummy/iio-dummy-fix.patch | 0 .../kernel/iio-dummy/iio_modified_dummy.c | 0 .../kernel/iio-dummy/iio_modified_dummy.h | 0 .../src/devices/common.rs | 0 .../{scmi => vhost-device-scmi}/src/devices/fake.rs | 0 .../{scmi => vhost-device-scmi}/src/devices/iio.rs | 0 .../{scmi => vhost-device-scmi}/src/devices/mod.rs | 0 crates/{scmi => vhost-device-scmi}/src/main.rs | 0 crates/{scmi => vhost-device-scmi}/src/scmi.rs | 0 crates/{scmi => vhost-device-scmi}/src/vhu_scmi.rs | 0 crates/{scsi => vhost-device-scsi}/ARCHITECTURE.md | 0 crates/{scsi => vhost-device-scsi}/CHANGELOG.md | 0 crates/{scsi => vhost-device-scsi}/Cargo.toml | 0 crates/{scsi => vhost-device-scsi}/LICENSE-APACHE | 0 .../{scsi => vhost-device-scsi}/LICENSE-BSD-3-Clause | 0 crates/{scsi => vhost-device-scsi}/README.md | 0 crates/{scsi => vhost-device-scsi}/src/main.rs | 0 .../src/scsi/emulation/block_device.rs | 0 .../src/scsi/emulation/command.rs | 0 .../src/scsi/emulation/missing_lun.rs | 0 .../src/scsi/emulation/mod.rs | 0 .../src/scsi/emulation/mode_page.rs | 0 .../src/scsi/emulation/response_data.rs | 0 .../src/scsi/emulation/target.rs | 0 .../src/scsi/emulation/tests/bad_lun.rs | 0 .../src/scsi/emulation/tests/generic.rs | 0 .../src/scsi/emulation/tests/mod.rs | 0 .../tests/report_supported_operation_codes.rs | 0 crates/{scsi => vhost-device-scsi}/src/scsi/mod.rs | 0 crates/{scsi => vhost-device-scsi}/src/scsi/sense.rs | 0 crates/{scsi => vhost-device-scsi}/src/vhu_scsi.rs | 0 crates/{scsi => vhost-device-scsi}/src/virtio.rs | 0 .../test/.containerignore | 0 crates/{scsi => vhost-device-scsi}/test/.gitignore | 0 .../{scsi => vhost-device-scsi}/test/Containerfile | 0 crates/{scsi => vhost-device-scsi}/test/README.md | 0 .../{scsi => vhost-device-scsi}/test/invoke-test.sh | 0 .../{scsi => vhost-device-scsi}/test/start-test.sh | 0 .../{scsi => vhost-device-scsi}/test/test-script.sh | 0 crates/{vsock => vhost-device-vsock}/CHANGELOG.md | 0 crates/{vsock => vhost-device-vsock}/Cargo.toml | 0 crates/{vsock => vhost-device-vsock}/LICENSE-APACHE | 0 .../LICENSE-BSD-3-Clause | 0 crates/{vsock => vhost-device-vsock}/README.md | 0 crates/{vsock => vhost-device-vsock}/src/main.rs | 0 crates/{vsock => vhost-device-vsock}/src/rxops.rs | 0 crates/{vsock => vhost-device-vsock}/src/rxqueue.rs | 0 .../src/thread_backend.rs | 0 crates/{vsock => vhost-device-vsock}/src/txbuf.rs | 0 .../{vsock => vhost-device-vsock}/src/vhu_vsock.rs | 0 .../src/vhu_vsock_thread.rs | 0 .../{vsock => vhost-device-vsock}/src/vsock_conn.rs | 0 86 files changed, 13 insertions(+), 13 deletions(-) rename crates/{gpio => vhost-device-gpio}/CHANGELOG.md (100%) rename crates/{gpio => vhost-device-gpio}/Cargo.toml (100%) rename crates/{gpio => vhost-device-gpio}/LICENSE-APACHE (100%) rename crates/{gpio => vhost-device-gpio}/LICENSE-BSD-3-Clause (100%) rename crates/{gpio => vhost-device-gpio}/README.md (100%) rename crates/{gpio => vhost-device-gpio}/src/backend.rs (100%) rename crates/{gpio => vhost-device-gpio}/src/gpio.rs (100%) rename crates/{gpio => vhost-device-gpio}/src/main.rs (100%) rename crates/{gpio => vhost-device-gpio}/src/vhu_gpio.rs (100%) rename crates/{i2c => vhost-device-i2c}/CHANGELOG.md (100%) rename crates/{i2c => vhost-device-i2c}/Cargo.toml (100%) rename crates/{i2c => vhost-device-i2c}/LICENSE-APACHE (100%) rename crates/{i2c => vhost-device-i2c}/LICENSE-BSD-3-Clause (100%) rename crates/{i2c => vhost-device-i2c}/README.md (100%) rename crates/{i2c => vhost-device-i2c}/src/i2c.rs (100%) rename crates/{i2c => vhost-device-i2c}/src/main.rs (100%) rename crates/{i2c => vhost-device-i2c}/src/vhu_i2c.rs (100%) rename crates/{rng => vhost-device-rng}/CHANGELOG.md (100%) rename crates/{rng => vhost-device-rng}/Cargo.toml (100%) rename crates/{rng => vhost-device-rng}/LICENSE-APACHE (100%) rename crates/{rng => vhost-device-rng}/LICENSE-BSD-3-Clause (100%) rename crates/{rng => vhost-device-rng}/README.md (100%) rename crates/{rng => vhost-device-rng}/src/main.rs (100%) rename crates/{rng => vhost-device-rng}/src/vhu_rng.rs (100%) rename crates/{scmi => vhost-device-scmi}/CHANGELOG.md (100%) rename crates/{scmi => vhost-device-scmi}/Cargo.toml (100%) rename crates/{scmi => vhost-device-scmi}/LICENSE-APACHE (100%) rename crates/{scmi => vhost-device-scmi}/LICENSE-BSD-3-Clause (100%) rename crates/{scmi => vhost-device-scmi}/README.md (97%) rename crates/{scmi => vhost-device-scmi}/kernel/iio-dummy/.gitignore (100%) rename crates/{scmi => vhost-device-scmi}/kernel/iio-dummy/Makefile (100%) rename crates/{scmi => vhost-device-scmi}/kernel/iio-dummy/README.md (100%) rename crates/{scmi => vhost-device-scmi}/kernel/iio-dummy/iio-dummy-fix.patch (100%) rename crates/{scmi => vhost-device-scmi}/kernel/iio-dummy/iio_modified_dummy.c (100%) rename crates/{scmi => vhost-device-scmi}/kernel/iio-dummy/iio_modified_dummy.h (100%) rename crates/{scmi => vhost-device-scmi}/src/devices/common.rs (100%) rename crates/{scmi => vhost-device-scmi}/src/devices/fake.rs (100%) rename crates/{scmi => vhost-device-scmi}/src/devices/iio.rs (100%) rename crates/{scmi => vhost-device-scmi}/src/devices/mod.rs (100%) rename crates/{scmi => vhost-device-scmi}/src/main.rs (100%) rename crates/{scmi => vhost-device-scmi}/src/scmi.rs (100%) rename crates/{scmi => vhost-device-scmi}/src/vhu_scmi.rs (100%) rename crates/{scsi => vhost-device-scsi}/ARCHITECTURE.md (100%) rename crates/{scsi => vhost-device-scsi}/CHANGELOG.md (100%) rename crates/{scsi => vhost-device-scsi}/Cargo.toml (100%) rename crates/{scsi => vhost-device-scsi}/LICENSE-APACHE (100%) rename crates/{scsi => vhost-device-scsi}/LICENSE-BSD-3-Clause (100%) rename crates/{scsi => vhost-device-scsi}/README.md (100%) rename crates/{scsi => vhost-device-scsi}/src/main.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/scsi/emulation/block_device.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/scsi/emulation/command.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/scsi/emulation/missing_lun.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/scsi/emulation/mod.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/scsi/emulation/mode_page.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/scsi/emulation/response_data.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/scsi/emulation/target.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/scsi/emulation/tests/bad_lun.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/scsi/emulation/tests/generic.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/scsi/emulation/tests/mod.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/scsi/emulation/tests/report_supported_operation_codes.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/scsi/mod.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/scsi/sense.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/vhu_scsi.rs (100%) rename crates/{scsi => vhost-device-scsi}/src/virtio.rs (100%) rename crates/{scsi => vhost-device-scsi}/test/.containerignore (100%) rename crates/{scsi => vhost-device-scsi}/test/.gitignore (100%) rename crates/{scsi => vhost-device-scsi}/test/Containerfile (100%) rename crates/{scsi => vhost-device-scsi}/test/README.md (100%) rename crates/{scsi => vhost-device-scsi}/test/invoke-test.sh (100%) rename crates/{scsi => vhost-device-scsi}/test/start-test.sh (100%) rename crates/{scsi => vhost-device-scsi}/test/test-script.sh (100%) rename crates/{vsock => vhost-device-vsock}/CHANGELOG.md (100%) rename crates/{vsock => vhost-device-vsock}/Cargo.toml (100%) rename crates/{vsock => vhost-device-vsock}/LICENSE-APACHE (100%) rename crates/{vsock => vhost-device-vsock}/LICENSE-BSD-3-Clause (100%) rename crates/{vsock => vhost-device-vsock}/README.md (100%) rename crates/{vsock => vhost-device-vsock}/src/main.rs (100%) rename crates/{vsock => vhost-device-vsock}/src/rxops.rs (100%) rename crates/{vsock => vhost-device-vsock}/src/rxqueue.rs (100%) rename crates/{vsock => vhost-device-vsock}/src/thread_backend.rs (100%) rename crates/{vsock => vhost-device-vsock}/src/txbuf.rs (100%) rename crates/{vsock => vhost-device-vsock}/src/vhu_vsock.rs (100%) rename crates/{vsock => vhost-device-vsock}/src/vhu_vsock_thread.rs (100%) rename crates/{vsock => vhost-device-vsock}/src/vsock_conn.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 72e3adf..4f76d61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,10 +2,10 @@ resolver = "2" members = [ - "crates/gpio", - "crates/i2c", - "crates/rng", - "crates/scsi", - "crates/scmi", - "crates/vsock", + "crates/vhost-device-gpio", + "crates/vhost-device-i2c", + "crates/vhost-device-rng", + "crates/vhost-device-scsi", + "crates/vhost-device-scmi", + "crates/vhost-device-vsock", ] diff --git a/README.md b/README.md index a18bed6..e487535 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,12 @@ crates. Here is the list of device backends that we support: -- [GPIO](https://github.com/rust-vmm/vhost-device/blob/main/crates/gpio/README.md) -- [I2C](https://github.com/rust-vmm/vhost-device/blob/main/crates/i2c/README.md) -- [RNG](https://github.com/rust-vmm/vhost-device/blob/main/crates/rng/README.md) -- [SCMI](https://github.com/rust-vmm/vhost-device/blob/main/crates/scmi/README.md) -- [SCSI](https://github.com/rust-vmm/vhost-device/blob/main/crates/scsi/README.md) -- [VSOCK](https://github.com/rust-vmm/vhost-device/blob/main/crates/vsock/README.md) +- [GPIO](https://github.com/rust-vmm/vhost-device/blob/main/crates/vhost-device-gpio/README.md) +- [I2C](https://github.com/rust-vmm/vhost-device/blob/main/crates/vhost-device-i2c/README.md) +- [RNG](https://github.com/rust-vmm/vhost-device/blob/main/crates/vhost-device-rng/README.md) +- [SCMI](https://github.com/rust-vmm/vhost-device/blob/main/crates/vhost-device-scmi/README.md) +- [SCSI](https://github.com/rust-vmm/vhost-device/blob/main/crates/vhost-device-scsi/README.md) +- [VSOCK](https://github.com/rust-vmm/vhost-device/blob/main/crates/vhost-device-vsock/README.md) ## Testing and Code Coverage diff --git a/crates/gpio/CHANGELOG.md b/crates/vhost-device-gpio/CHANGELOG.md similarity index 100% rename from crates/gpio/CHANGELOG.md rename to crates/vhost-device-gpio/CHANGELOG.md diff --git a/crates/gpio/Cargo.toml b/crates/vhost-device-gpio/Cargo.toml similarity index 100% rename from crates/gpio/Cargo.toml rename to crates/vhost-device-gpio/Cargo.toml diff --git a/crates/gpio/LICENSE-APACHE b/crates/vhost-device-gpio/LICENSE-APACHE similarity index 100% rename from crates/gpio/LICENSE-APACHE rename to crates/vhost-device-gpio/LICENSE-APACHE diff --git a/crates/gpio/LICENSE-BSD-3-Clause b/crates/vhost-device-gpio/LICENSE-BSD-3-Clause similarity index 100% rename from crates/gpio/LICENSE-BSD-3-Clause rename to crates/vhost-device-gpio/LICENSE-BSD-3-Clause diff --git a/crates/gpio/README.md b/crates/vhost-device-gpio/README.md similarity index 100% rename from crates/gpio/README.md rename to crates/vhost-device-gpio/README.md diff --git a/crates/gpio/src/backend.rs b/crates/vhost-device-gpio/src/backend.rs similarity index 100% rename from crates/gpio/src/backend.rs rename to crates/vhost-device-gpio/src/backend.rs diff --git a/crates/gpio/src/gpio.rs b/crates/vhost-device-gpio/src/gpio.rs similarity index 100% rename from crates/gpio/src/gpio.rs rename to crates/vhost-device-gpio/src/gpio.rs diff --git a/crates/gpio/src/main.rs b/crates/vhost-device-gpio/src/main.rs similarity index 100% rename from crates/gpio/src/main.rs rename to crates/vhost-device-gpio/src/main.rs diff --git a/crates/gpio/src/vhu_gpio.rs b/crates/vhost-device-gpio/src/vhu_gpio.rs similarity index 100% rename from crates/gpio/src/vhu_gpio.rs rename to crates/vhost-device-gpio/src/vhu_gpio.rs diff --git a/crates/i2c/CHANGELOG.md b/crates/vhost-device-i2c/CHANGELOG.md similarity index 100% rename from crates/i2c/CHANGELOG.md rename to crates/vhost-device-i2c/CHANGELOG.md diff --git a/crates/i2c/Cargo.toml b/crates/vhost-device-i2c/Cargo.toml similarity index 100% rename from crates/i2c/Cargo.toml rename to crates/vhost-device-i2c/Cargo.toml diff --git a/crates/i2c/LICENSE-APACHE b/crates/vhost-device-i2c/LICENSE-APACHE similarity index 100% rename from crates/i2c/LICENSE-APACHE rename to crates/vhost-device-i2c/LICENSE-APACHE diff --git a/crates/i2c/LICENSE-BSD-3-Clause b/crates/vhost-device-i2c/LICENSE-BSD-3-Clause similarity index 100% rename from crates/i2c/LICENSE-BSD-3-Clause rename to crates/vhost-device-i2c/LICENSE-BSD-3-Clause diff --git a/crates/i2c/README.md b/crates/vhost-device-i2c/README.md similarity index 100% rename from crates/i2c/README.md rename to crates/vhost-device-i2c/README.md diff --git a/crates/i2c/src/i2c.rs b/crates/vhost-device-i2c/src/i2c.rs similarity index 100% rename from crates/i2c/src/i2c.rs rename to crates/vhost-device-i2c/src/i2c.rs diff --git a/crates/i2c/src/main.rs b/crates/vhost-device-i2c/src/main.rs similarity index 100% rename from crates/i2c/src/main.rs rename to crates/vhost-device-i2c/src/main.rs diff --git a/crates/i2c/src/vhu_i2c.rs b/crates/vhost-device-i2c/src/vhu_i2c.rs similarity index 100% rename from crates/i2c/src/vhu_i2c.rs rename to crates/vhost-device-i2c/src/vhu_i2c.rs diff --git a/crates/rng/CHANGELOG.md b/crates/vhost-device-rng/CHANGELOG.md similarity index 100% rename from crates/rng/CHANGELOG.md rename to crates/vhost-device-rng/CHANGELOG.md diff --git a/crates/rng/Cargo.toml b/crates/vhost-device-rng/Cargo.toml similarity index 100% rename from crates/rng/Cargo.toml rename to crates/vhost-device-rng/Cargo.toml diff --git a/crates/rng/LICENSE-APACHE b/crates/vhost-device-rng/LICENSE-APACHE similarity index 100% rename from crates/rng/LICENSE-APACHE rename to crates/vhost-device-rng/LICENSE-APACHE diff --git a/crates/rng/LICENSE-BSD-3-Clause b/crates/vhost-device-rng/LICENSE-BSD-3-Clause similarity index 100% rename from crates/rng/LICENSE-BSD-3-Clause rename to crates/vhost-device-rng/LICENSE-BSD-3-Clause diff --git a/crates/rng/README.md b/crates/vhost-device-rng/README.md similarity index 100% rename from crates/rng/README.md rename to crates/vhost-device-rng/README.md diff --git a/crates/rng/src/main.rs b/crates/vhost-device-rng/src/main.rs similarity index 100% rename from crates/rng/src/main.rs rename to crates/vhost-device-rng/src/main.rs diff --git a/crates/rng/src/vhu_rng.rs b/crates/vhost-device-rng/src/vhu_rng.rs similarity index 100% rename from crates/rng/src/vhu_rng.rs rename to crates/vhost-device-rng/src/vhu_rng.rs diff --git a/crates/scmi/CHANGELOG.md b/crates/vhost-device-scmi/CHANGELOG.md similarity index 100% rename from crates/scmi/CHANGELOG.md rename to crates/vhost-device-scmi/CHANGELOG.md diff --git a/crates/scmi/Cargo.toml b/crates/vhost-device-scmi/Cargo.toml similarity index 100% rename from crates/scmi/Cargo.toml rename to crates/vhost-device-scmi/Cargo.toml diff --git a/crates/scmi/LICENSE-APACHE b/crates/vhost-device-scmi/LICENSE-APACHE similarity index 100% rename from crates/scmi/LICENSE-APACHE rename to crates/vhost-device-scmi/LICENSE-APACHE diff --git a/crates/scmi/LICENSE-BSD-3-Clause b/crates/vhost-device-scmi/LICENSE-BSD-3-Clause similarity index 100% rename from crates/scmi/LICENSE-BSD-3-Clause rename to crates/vhost-device-scmi/LICENSE-BSD-3-Clause diff --git a/crates/scmi/README.md b/crates/vhost-device-scmi/README.md similarity index 97% rename from crates/scmi/README.md rename to crates/vhost-device-scmi/README.md index d16c090..bcd6403 100644 --- a/crates/scmi/README.md +++ b/crates/vhost-device-scmi/README.md @@ -67,7 +67,7 @@ The currently supported SCMI protocols are: Basically only the mandatory and necessary parts of the protocols are implemented. -See source code (`scmi` crate) documentation for details and how to +See source code (`vhost-device-scmi` crate) documentation for details and how to add more protocols, host device bindings or other functionality. ## Testing diff --git a/crates/scmi/kernel/iio-dummy/.gitignore b/crates/vhost-device-scmi/kernel/iio-dummy/.gitignore similarity index 100% rename from crates/scmi/kernel/iio-dummy/.gitignore rename to crates/vhost-device-scmi/kernel/iio-dummy/.gitignore diff --git a/crates/scmi/kernel/iio-dummy/Makefile b/crates/vhost-device-scmi/kernel/iio-dummy/Makefile similarity index 100% rename from crates/scmi/kernel/iio-dummy/Makefile rename to crates/vhost-device-scmi/kernel/iio-dummy/Makefile diff --git a/crates/scmi/kernel/iio-dummy/README.md b/crates/vhost-device-scmi/kernel/iio-dummy/README.md similarity index 100% rename from crates/scmi/kernel/iio-dummy/README.md rename to crates/vhost-device-scmi/kernel/iio-dummy/README.md diff --git a/crates/scmi/kernel/iio-dummy/iio-dummy-fix.patch b/crates/vhost-device-scmi/kernel/iio-dummy/iio-dummy-fix.patch similarity index 100% rename from crates/scmi/kernel/iio-dummy/iio-dummy-fix.patch rename to crates/vhost-device-scmi/kernel/iio-dummy/iio-dummy-fix.patch diff --git a/crates/scmi/kernel/iio-dummy/iio_modified_dummy.c b/crates/vhost-device-scmi/kernel/iio-dummy/iio_modified_dummy.c similarity index 100% rename from crates/scmi/kernel/iio-dummy/iio_modified_dummy.c rename to crates/vhost-device-scmi/kernel/iio-dummy/iio_modified_dummy.c diff --git a/crates/scmi/kernel/iio-dummy/iio_modified_dummy.h b/crates/vhost-device-scmi/kernel/iio-dummy/iio_modified_dummy.h similarity index 100% rename from crates/scmi/kernel/iio-dummy/iio_modified_dummy.h rename to crates/vhost-device-scmi/kernel/iio-dummy/iio_modified_dummy.h diff --git a/crates/scmi/src/devices/common.rs b/crates/vhost-device-scmi/src/devices/common.rs similarity index 100% rename from crates/scmi/src/devices/common.rs rename to crates/vhost-device-scmi/src/devices/common.rs diff --git a/crates/scmi/src/devices/fake.rs b/crates/vhost-device-scmi/src/devices/fake.rs similarity index 100% rename from crates/scmi/src/devices/fake.rs rename to crates/vhost-device-scmi/src/devices/fake.rs diff --git a/crates/scmi/src/devices/iio.rs b/crates/vhost-device-scmi/src/devices/iio.rs similarity index 100% rename from crates/scmi/src/devices/iio.rs rename to crates/vhost-device-scmi/src/devices/iio.rs diff --git a/crates/scmi/src/devices/mod.rs b/crates/vhost-device-scmi/src/devices/mod.rs similarity index 100% rename from crates/scmi/src/devices/mod.rs rename to crates/vhost-device-scmi/src/devices/mod.rs diff --git a/crates/scmi/src/main.rs b/crates/vhost-device-scmi/src/main.rs similarity index 100% rename from crates/scmi/src/main.rs rename to crates/vhost-device-scmi/src/main.rs diff --git a/crates/scmi/src/scmi.rs b/crates/vhost-device-scmi/src/scmi.rs similarity index 100% rename from crates/scmi/src/scmi.rs rename to crates/vhost-device-scmi/src/scmi.rs diff --git a/crates/scmi/src/vhu_scmi.rs b/crates/vhost-device-scmi/src/vhu_scmi.rs similarity index 100% rename from crates/scmi/src/vhu_scmi.rs rename to crates/vhost-device-scmi/src/vhu_scmi.rs diff --git a/crates/scsi/ARCHITECTURE.md b/crates/vhost-device-scsi/ARCHITECTURE.md similarity index 100% rename from crates/scsi/ARCHITECTURE.md rename to crates/vhost-device-scsi/ARCHITECTURE.md diff --git a/crates/scsi/CHANGELOG.md b/crates/vhost-device-scsi/CHANGELOG.md similarity index 100% rename from crates/scsi/CHANGELOG.md rename to crates/vhost-device-scsi/CHANGELOG.md diff --git a/crates/scsi/Cargo.toml b/crates/vhost-device-scsi/Cargo.toml similarity index 100% rename from crates/scsi/Cargo.toml rename to crates/vhost-device-scsi/Cargo.toml diff --git a/crates/scsi/LICENSE-APACHE b/crates/vhost-device-scsi/LICENSE-APACHE similarity index 100% rename from crates/scsi/LICENSE-APACHE rename to crates/vhost-device-scsi/LICENSE-APACHE diff --git a/crates/scsi/LICENSE-BSD-3-Clause b/crates/vhost-device-scsi/LICENSE-BSD-3-Clause similarity index 100% rename from crates/scsi/LICENSE-BSD-3-Clause rename to crates/vhost-device-scsi/LICENSE-BSD-3-Clause diff --git a/crates/scsi/README.md b/crates/vhost-device-scsi/README.md similarity index 100% rename from crates/scsi/README.md rename to crates/vhost-device-scsi/README.md diff --git a/crates/scsi/src/main.rs b/crates/vhost-device-scsi/src/main.rs similarity index 100% rename from crates/scsi/src/main.rs rename to crates/vhost-device-scsi/src/main.rs diff --git a/crates/scsi/src/scsi/emulation/block_device.rs b/crates/vhost-device-scsi/src/scsi/emulation/block_device.rs similarity index 100% rename from crates/scsi/src/scsi/emulation/block_device.rs rename to crates/vhost-device-scsi/src/scsi/emulation/block_device.rs diff --git a/crates/scsi/src/scsi/emulation/command.rs b/crates/vhost-device-scsi/src/scsi/emulation/command.rs similarity index 100% rename from crates/scsi/src/scsi/emulation/command.rs rename to crates/vhost-device-scsi/src/scsi/emulation/command.rs diff --git a/crates/scsi/src/scsi/emulation/missing_lun.rs b/crates/vhost-device-scsi/src/scsi/emulation/missing_lun.rs similarity index 100% rename from crates/scsi/src/scsi/emulation/missing_lun.rs rename to crates/vhost-device-scsi/src/scsi/emulation/missing_lun.rs diff --git a/crates/scsi/src/scsi/emulation/mod.rs b/crates/vhost-device-scsi/src/scsi/emulation/mod.rs similarity index 100% rename from crates/scsi/src/scsi/emulation/mod.rs rename to crates/vhost-device-scsi/src/scsi/emulation/mod.rs diff --git a/crates/scsi/src/scsi/emulation/mode_page.rs b/crates/vhost-device-scsi/src/scsi/emulation/mode_page.rs similarity index 100% rename from crates/scsi/src/scsi/emulation/mode_page.rs rename to crates/vhost-device-scsi/src/scsi/emulation/mode_page.rs diff --git a/crates/scsi/src/scsi/emulation/response_data.rs b/crates/vhost-device-scsi/src/scsi/emulation/response_data.rs similarity index 100% rename from crates/scsi/src/scsi/emulation/response_data.rs rename to crates/vhost-device-scsi/src/scsi/emulation/response_data.rs diff --git a/crates/scsi/src/scsi/emulation/target.rs b/crates/vhost-device-scsi/src/scsi/emulation/target.rs similarity index 100% rename from crates/scsi/src/scsi/emulation/target.rs rename to crates/vhost-device-scsi/src/scsi/emulation/target.rs diff --git a/crates/scsi/src/scsi/emulation/tests/bad_lun.rs b/crates/vhost-device-scsi/src/scsi/emulation/tests/bad_lun.rs similarity index 100% rename from crates/scsi/src/scsi/emulation/tests/bad_lun.rs rename to crates/vhost-device-scsi/src/scsi/emulation/tests/bad_lun.rs diff --git a/crates/scsi/src/scsi/emulation/tests/generic.rs b/crates/vhost-device-scsi/src/scsi/emulation/tests/generic.rs similarity index 100% rename from crates/scsi/src/scsi/emulation/tests/generic.rs rename to crates/vhost-device-scsi/src/scsi/emulation/tests/generic.rs diff --git a/crates/scsi/src/scsi/emulation/tests/mod.rs b/crates/vhost-device-scsi/src/scsi/emulation/tests/mod.rs similarity index 100% rename from crates/scsi/src/scsi/emulation/tests/mod.rs rename to crates/vhost-device-scsi/src/scsi/emulation/tests/mod.rs diff --git a/crates/scsi/src/scsi/emulation/tests/report_supported_operation_codes.rs b/crates/vhost-device-scsi/src/scsi/emulation/tests/report_supported_operation_codes.rs similarity index 100% rename from crates/scsi/src/scsi/emulation/tests/report_supported_operation_codes.rs rename to crates/vhost-device-scsi/src/scsi/emulation/tests/report_supported_operation_codes.rs diff --git a/crates/scsi/src/scsi/mod.rs b/crates/vhost-device-scsi/src/scsi/mod.rs similarity index 100% rename from crates/scsi/src/scsi/mod.rs rename to crates/vhost-device-scsi/src/scsi/mod.rs diff --git a/crates/scsi/src/scsi/sense.rs b/crates/vhost-device-scsi/src/scsi/sense.rs similarity index 100% rename from crates/scsi/src/scsi/sense.rs rename to crates/vhost-device-scsi/src/scsi/sense.rs diff --git a/crates/scsi/src/vhu_scsi.rs b/crates/vhost-device-scsi/src/vhu_scsi.rs similarity index 100% rename from crates/scsi/src/vhu_scsi.rs rename to crates/vhost-device-scsi/src/vhu_scsi.rs diff --git a/crates/scsi/src/virtio.rs b/crates/vhost-device-scsi/src/virtio.rs similarity index 100% rename from crates/scsi/src/virtio.rs rename to crates/vhost-device-scsi/src/virtio.rs diff --git a/crates/scsi/test/.containerignore b/crates/vhost-device-scsi/test/.containerignore similarity index 100% rename from crates/scsi/test/.containerignore rename to crates/vhost-device-scsi/test/.containerignore diff --git a/crates/scsi/test/.gitignore b/crates/vhost-device-scsi/test/.gitignore similarity index 100% rename from crates/scsi/test/.gitignore rename to crates/vhost-device-scsi/test/.gitignore diff --git a/crates/scsi/test/Containerfile b/crates/vhost-device-scsi/test/Containerfile similarity index 100% rename from crates/scsi/test/Containerfile rename to crates/vhost-device-scsi/test/Containerfile diff --git a/crates/scsi/test/README.md b/crates/vhost-device-scsi/test/README.md similarity index 100% rename from crates/scsi/test/README.md rename to crates/vhost-device-scsi/test/README.md diff --git a/crates/scsi/test/invoke-test.sh b/crates/vhost-device-scsi/test/invoke-test.sh similarity index 100% rename from crates/scsi/test/invoke-test.sh rename to crates/vhost-device-scsi/test/invoke-test.sh diff --git a/crates/scsi/test/start-test.sh b/crates/vhost-device-scsi/test/start-test.sh similarity index 100% rename from crates/scsi/test/start-test.sh rename to crates/vhost-device-scsi/test/start-test.sh diff --git a/crates/scsi/test/test-script.sh b/crates/vhost-device-scsi/test/test-script.sh similarity index 100% rename from crates/scsi/test/test-script.sh rename to crates/vhost-device-scsi/test/test-script.sh diff --git a/crates/vsock/CHANGELOG.md b/crates/vhost-device-vsock/CHANGELOG.md similarity index 100% rename from crates/vsock/CHANGELOG.md rename to crates/vhost-device-vsock/CHANGELOG.md diff --git a/crates/vsock/Cargo.toml b/crates/vhost-device-vsock/Cargo.toml similarity index 100% rename from crates/vsock/Cargo.toml rename to crates/vhost-device-vsock/Cargo.toml diff --git a/crates/vsock/LICENSE-APACHE b/crates/vhost-device-vsock/LICENSE-APACHE similarity index 100% rename from crates/vsock/LICENSE-APACHE rename to crates/vhost-device-vsock/LICENSE-APACHE diff --git a/crates/vsock/LICENSE-BSD-3-Clause b/crates/vhost-device-vsock/LICENSE-BSD-3-Clause similarity index 100% rename from crates/vsock/LICENSE-BSD-3-Clause rename to crates/vhost-device-vsock/LICENSE-BSD-3-Clause diff --git a/crates/vsock/README.md b/crates/vhost-device-vsock/README.md similarity index 100% rename from crates/vsock/README.md rename to crates/vhost-device-vsock/README.md diff --git a/crates/vsock/src/main.rs b/crates/vhost-device-vsock/src/main.rs similarity index 100% rename from crates/vsock/src/main.rs rename to crates/vhost-device-vsock/src/main.rs diff --git a/crates/vsock/src/rxops.rs b/crates/vhost-device-vsock/src/rxops.rs similarity index 100% rename from crates/vsock/src/rxops.rs rename to crates/vhost-device-vsock/src/rxops.rs diff --git a/crates/vsock/src/rxqueue.rs b/crates/vhost-device-vsock/src/rxqueue.rs similarity index 100% rename from crates/vsock/src/rxqueue.rs rename to crates/vhost-device-vsock/src/rxqueue.rs diff --git a/crates/vsock/src/thread_backend.rs b/crates/vhost-device-vsock/src/thread_backend.rs similarity index 100% rename from crates/vsock/src/thread_backend.rs rename to crates/vhost-device-vsock/src/thread_backend.rs diff --git a/crates/vsock/src/txbuf.rs b/crates/vhost-device-vsock/src/txbuf.rs similarity index 100% rename from crates/vsock/src/txbuf.rs rename to crates/vhost-device-vsock/src/txbuf.rs diff --git a/crates/vsock/src/vhu_vsock.rs b/crates/vhost-device-vsock/src/vhu_vsock.rs similarity index 100% rename from crates/vsock/src/vhu_vsock.rs rename to crates/vhost-device-vsock/src/vhu_vsock.rs diff --git a/crates/vsock/src/vhu_vsock_thread.rs b/crates/vhost-device-vsock/src/vhu_vsock_thread.rs similarity index 100% rename from crates/vsock/src/vhu_vsock_thread.rs rename to crates/vhost-device-vsock/src/vhu_vsock_thread.rs diff --git a/crates/vsock/src/vsock_conn.rs b/crates/vhost-device-vsock/src/vsock_conn.rs similarity index 100% rename from crates/vsock/src/vsock_conn.rs rename to crates/vhost-device-vsock/src/vsock_conn.rs From 10482e1c4aafd197021310e2a0f154bb9e7fce36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 04:14:50 +0000 Subject: [PATCH 175/189] build(deps): bump memchr from 2.5.0 to 2.6.4 Bumps [memchr](https://github.com/BurntSushi/memchr) from 2.5.0 to 2.6.4. - [Commits](https://github.com/BurntSushi/memchr/compare/2.5.0...2.6.4) --- updated-dependencies: - dependency-name: memchr dependency-type: indirect update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8017b7b..c245509 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -453,9 +453,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "minimal-lexical" From ce0e11c7919fe74141298a7d42fc77e2cc1ab73e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 04:14:37 +0000 Subject: [PATCH 176/189] build(deps): bump serde from 1.0.185 to 1.0.188 Bumps [serde](https://github.com/serde-rs/serde) from 1.0.185 to 1.0.188. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.185...v1.0.188) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c245509..73d2fa5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -647,18 +647,18 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "serde" -version = "1.0.185" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.185" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc59dfdcbad1437773485e0367fea4b090a2e0a16d9ffc46af47764536a298ec" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", From 3169f288994bc0465c9c08ab217d9bb2b31d265a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 05:01:49 +0000 Subject: [PATCH 177/189] build(deps): bump aho-corasick from 1.0.5 to 1.1.1 Bumps [aho-corasick](https://github.com/BurntSushi/aho-corasick) from 1.0.5 to 1.1.1. - [Commits](https://github.com/BurntSushi/aho-corasick/compare/1.0.5...1.1.1) --- updated-dependencies: - dependency-name: aho-corasick dependency-type: indirect update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 73d2fa5..a9bf1ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aho-corasick" -version = "1.0.5" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" dependencies = [ "memchr", ] From a8091b88935683f573dacab6bd03e111a243672f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 05:01:13 +0000 Subject: [PATCH 178/189] build(deps): bump rust-vmm-ci from `665f31f` to `9751aaa` Bumps [rust-vmm-ci](https://github.com/rust-vmm/rust-vmm-ci) from `665f31f` to `9751aaa`. - [Commits](https://github.com/rust-vmm/rust-vmm-ci/compare/665f31f4b4e050270604d9b534058fa9b9d39adc...9751aaa0d0706964b1d4a228509a86bc25ffc0e7) --- updated-dependencies: - dependency-name: rust-vmm-ci dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- rust-vmm-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-vmm-ci b/rust-vmm-ci index 665f31f..9751aaa 160000 --- a/rust-vmm-ci +++ b/rust-vmm-ci @@ -1 +1 @@ -Subproject commit 665f31f4b4e050270604d9b534058fa9b9d39adc +Subproject commit 9751aaa0d0706964b1d4a228509a86bc25ffc0e7 From 072fadaaba527133c3466cac90a8c5dd386b1f9e Mon Sep 17 00:00:00 2001 From: Erik Schilling Date: Thu, 5 Oct 2023 17:18:57 +0200 Subject: [PATCH 179/189] dependabot: enable update grouping This should group updates into a single PR. Hopefully, that simplfies updates. Updates that do _actually_ require code changes, will need a separate PR anyway, after which dependabot can be asked to rebase/recreate. Suggested-by: Patrick Roy Suggested-by: Stefano Garzarella Signed-off-by: Erik Schilling --- .github/dependabot.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c624534..1986b1e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,10 +4,13 @@ updates: directory: "/" schedule: interval: weekly - open-pull-requests-limit: 3 allow: - dependency-type: direct - dependency-type: indirect + groups: + vhost-device: + patterns: + - "*" - package-ecosystem: gitsubmodule directory: "/" schedule: From cdc826ecfc04fe89a2427b29e179c2fb1c8cc95e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Oct 2023 08:44:18 +0000 Subject: [PATCH 180/189] build(deps): bump the vhost-device group with 22 updates Bumps the vhost-device group with 22 updates: | Package | From | To | | --- | --- | --- | | [clap](https://github.com/clap-rs/clap) | `4.4.3` | `4.4.6` | | [libc](https://github.com/rust-lang/libc) | `0.2.147` | `0.2.148` | | [thiserror](https://github.com/dtolnay/thiserror) | `1.0.47` | `1.0.49` | | [vmm-sys-util](https://github.com/rust-vmm/vmm-sys-util) | `0.11.1` | `0.11.2` | | [itertools](https://github.com/rust-itertools/itertools) | `0.10.5` | `0.11.0` | | [byteorder](https://github.com/BurntSushi/byteorder) | `1.4.3` | `1.5.0` | | [anstyle](https://github.com/rust-cli/anstyle) | `1.0.1` | `1.0.4` | | [anstyle-parse](https://github.com/rust-cli/anstyle) | `0.2.1` | `0.2.2` | | [clap_lex](https://github.com/clap-rs/clap) | `0.5.0` | `0.5.1` | | [fastrand](https://github.com/smol-rs/fastrand) | `2.0.0` | `2.0.1` | | [hashbrown](https://github.com/rust-lang/hashbrown) | `0.14.0` | `0.14.1` | | [indexmap](https://github.com/bluss/indexmap) | `2.0.0` | `2.0.2` | | [linux-raw-sys](https://github.com/sunfishcode/linux-raw-sys) | `0.4.5` | `0.4.8` | | [proc-macro2](https://github.com/dtolnay/proc-macro2) | `1.0.66` | `1.0.68` | | [regex](https://github.com/rust-lang/regex) | `1.9.4` | `1.9.6` | | [rustix](https://github.com/bytecodealliance/rustix) | `0.38.8` | `0.38.17` | | [termcolor](https://github.com/BurntSushi/termcolor) | `1.2.0` | `1.3.0` | | [toml_edit](https://github.com/toml-rs/toml) | `0.19.14` | `0.19.15` | | [unicode-ident](https://github.com/dtolnay/unicode-ident) | `1.0.11` | `1.0.12` | | [which](https://github.com/harryfei/which-rs) | `4.4.0` | `4.4.2` | | [winapi-util](https://github.com/BurntSushi/winapi-util) | `0.1.5` | `0.1.6` | | [winnow](https://github.com/winnow-rs/winnow) | `0.5.15` | `0.5.16` | Updates `clap` from 4.4.3 to 4.4.6 - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.4.3...v4.4.6) Updates `libc` from 0.2.147 to 0.2.148 - [Release notes](https://github.com/rust-lang/libc/releases) - [Commits](https://github.com/rust-lang/libc/compare/0.2.147...0.2.148) Updates `thiserror` from 1.0.47 to 1.0.49 - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.47...1.0.49) Updates `vmm-sys-util` from 0.11.1 to 0.11.2 - [Release notes](https://github.com/rust-vmm/vmm-sys-util/releases) - [Changelog](https://github.com/rust-vmm/vmm-sys-util/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-vmm/vmm-sys-util/commits) Updates `itertools` from 0.10.5 to 0.11.0 - [Changelog](https://github.com/rust-itertools/itertools/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-itertools/itertools/compare/v0.10.5...v0.11.0) Updates `byteorder` from 1.4.3 to 1.5.0 - [Changelog](https://github.com/BurntSushi/byteorder/blob/master/CHANGELOG.md) - [Commits](https://github.com/BurntSushi/byteorder/compare/1.4.3...1.5.0) Updates `anstyle` from 1.0.1 to 1.0.4 - [Commits](https://github.com/rust-cli/anstyle/compare/anstyle-ls-v1.0.1...v1.0.4) Updates `anstyle-parse` from 0.2.1 to 0.2.2 - [Commits](https://github.com/rust-cli/anstyle/compare/anstyle-parse-v0.2.1...anstyle-parse-v0.2.2) Updates `clap_lex` from 0.5.0 to 0.5.1 - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/clap_lex-v0.5.0...clap_lex-v0.5.1) Updates `fastrand` from 2.0.0 to 2.0.1 - [Release notes](https://github.com/smol-rs/fastrand/releases) - [Changelog](https://github.com/smol-rs/fastrand/blob/master/CHANGELOG.md) - [Commits](https://github.com/smol-rs/fastrand/compare/v2.0.0...v2.0.1) Updates `hashbrown` from 0.14.0 to 0.14.1 - [Changelog](https://github.com/rust-lang/hashbrown/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/hashbrown/compare/v0.14.0...v0.14.1) Updates `indexmap` from 2.0.0 to 2.0.2 - [Changelog](https://github.com/bluss/indexmap/blob/master/RELEASES.md) - [Commits](https://github.com/bluss/indexmap/compare/2.0.0...2.0.2) Updates `linux-raw-sys` from 0.4.5 to 0.4.8 - [Commits](https://github.com/sunfishcode/linux-raw-sys/compare/v0.4.5...v0.4.8) Updates `proc-macro2` from 1.0.66 to 1.0.68 - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.66...1.0.68) Updates `regex` from 1.9.4 to 1.9.6 - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/1.9.4...1.9.6) Updates `rustix` from 0.38.8 to 0.38.17 - [Release notes](https://github.com/bytecodealliance/rustix/releases) - [Commits](https://github.com/bytecodealliance/rustix/compare/v0.38.8...v0.38.17) Updates `termcolor` from 1.2.0 to 1.3.0 - [Commits](https://github.com/BurntSushi/termcolor/compare/1.2.0...1.3.0) Updates `toml_edit` from 0.19.14 to 0.19.15 - [Commits](https://github.com/toml-rs/toml/compare/v0.19.14...v0.19.15) Updates `unicode-ident` from 1.0.11 to 1.0.12 - [Release notes](https://github.com/dtolnay/unicode-ident/releases) - [Commits](https://github.com/dtolnay/unicode-ident/compare/1.0.11...1.0.12) Updates `which` from 4.4.0 to 4.4.2 - [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/harryfei/which-rs/compare/4.4.0...4.4.2) Updates `winapi-util` from 0.1.5 to 0.1.6 - [Commits](https://github.com/BurntSushi/winapi-util/compare/winapi-util-0.1.5...0.1.6) Updates `winnow` from 0.5.15 to 0.5.16 - [Changelog](https://github.com/winnow-rs/winnow/blob/main/CHANGELOG.md) - [Commits](https://github.com/winnow-rs/winnow/compare/v0.5.15...v0.5.16) --- updated-dependencies: - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: libc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: vmm-sys-util dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: itertools dependency-type: direct:production update-type: version-update:semver-minor dependency-group: vhost-device - dependency-name: byteorder dependency-type: direct:production update-type: version-update:semver-minor dependency-group: vhost-device - dependency-name: anstyle dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: anstyle-parse dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: clap_lex dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: fastrand dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: hashbrown dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: indexmap dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: linux-raw-sys dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: proc-macro2 dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: regex dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: rustix dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: termcolor dependency-type: indirect update-type: version-update:semver-minor dependency-group: vhost-device - dependency-name: toml_edit dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: unicode-ident dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: which dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: winapi-util dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: winnow dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device ... Signed-off-by: dependabot[bot] --- Cargo.lock | 120 +++++++++++++++------------- crates/vhost-device-scmi/Cargo.toml | 2 +- 2 files changed, 66 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9bf1ee..8f4e8b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.5.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", @@ -27,15 +27,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] @@ -51,9 +51,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "2.1.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys", @@ -118,9 +118,9 @@ checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" @@ -159,9 +159,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.3" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84ed82781cea27b43c9b106a979fe450a13a31aab0500595fb3fc06616de08e6" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" dependencies = [ "clap_builder", "clap_derive", @@ -169,9 +169,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.2" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" dependencies = [ "anstream", "anstyle", @@ -193,9 +193,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "colorchoice" @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "getrandom" @@ -309,9 +309,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" [[package]] name = "heck" @@ -334,6 +334,15 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys", +] + [[package]] name = "humantime" version = "2.1.0" @@ -342,9 +351,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "indexmap" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ "equivalent", "hashbrown", @@ -369,9 +378,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] @@ -396,9 +405,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libgpiod" @@ -441,9 +450,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" [[package]] name = "log" @@ -536,9 +545,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c" dependencies = [ "unicode-ident", ] @@ -593,9 +602,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.4" +version = "1.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" +checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" dependencies = [ "aho-corasick", "memchr", @@ -605,9 +614,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" dependencies = [ "aho-corasick", "memchr", @@ -628,9 +637,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.8" +version = "0.38.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" dependencies = [ "bitflags 2.4.0", "errno 0.3.2", @@ -760,27 +769,27 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.47" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.47" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", @@ -804,9 +813,9 @@ checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.19.14" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", @@ -815,9 +824,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" @@ -1034,9 +1043,9 @@ dependencies = [ [[package]] name = "vmm-sys-util" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46" +checksum = "48b7b084231214f7427041e4220d77dfe726897a6d41fddee450696e66ff2a29" dependencies = [ "bitflags 1.3.2", "libc", @@ -1050,13 +1059,14 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix", ] [[package]] @@ -1077,9 +1087,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -1158,9 +1168,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "037711d82167854aff2018dfd193aa0fef5370f456732f0d5a0c59b0f1b4b907" dependencies = [ "memchr", ] diff --git a/crates/vhost-device-scmi/Cargo.toml b/crates/vhost-device-scmi/Cargo.toml index 588caaf..914ab14 100644 --- a/crates/vhost-device-scmi/Cargo.toml +++ b/crates/vhost-device-scmi/Cargo.toml @@ -12,7 +12,7 @@ edition = "2021" [dependencies] clap = { version = "4.4", features = ["derive"] } env_logger = "0.10" -itertools = "0.10" +itertools = "0.11" log = "0.4" thiserror = "1.0" vhost = { version = "0.8", features = ["vhost-user-slave"] } From 5f3f3ec1ed7d0a8024117ccdb2ce940ed8f79273 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 04:44:35 +0000 Subject: [PATCH 181/189] build(deps): bump the vhost-device group with 3 updates Bumps the vhost-device group with 3 updates: [libc](https://github.com/rust-lang/libc), [linux-raw-sys](https://github.com/sunfishcode/linux-raw-sys) and [proc-macro2](https://github.com/dtolnay/proc-macro2). Updates `libc` from 0.2.148 to 0.2.149 - [Release notes](https://github.com/rust-lang/libc/releases) - [Commits](https://github.com/rust-lang/libc/compare/0.2.148...0.2.149) Updates `linux-raw-sys` from 0.4.8 to 0.4.10 - [Commits](https://github.com/sunfishcode/linux-raw-sys/compare/v0.4.8...v0.4.10) Updates `proc-macro2` from 1.0.68 to 1.0.69 - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.68...1.0.69) --- updated-dependencies: - dependency-name: libc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: linux-raw-sys dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device - dependency-name: proc-macro2 dependency-type: indirect update-type: version-update:semver-patch dependency-group: vhost-device ... Signed-off-by: dependabot[bot] --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8f4e8b2..d2b7014 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -405,9 +405,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libgpiod" @@ -450,9 +450,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.8" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "log" @@ -545,9 +545,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] From 344b4d5ec527a2f46591868b52fa24557850b4a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 6 Apr 2023 16:42:27 +0100 Subject: [PATCH 182/189] gpio: move DummyDevice and rename to MockGpioDevice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are about to use it to simulate a device for the real binary so lets move it out of the gpio module into its own. We also migrate the VirtIO constants to keep the imports somewhat sane. Signed-off-by: Alex Bennée --- crates/vhost-device-gpio/src/backend.rs | 4 +- crates/vhost-device-gpio/src/gpio.rs | 188 ++------------------ crates/vhost-device-gpio/src/main.rs | 4 + crates/vhost-device-gpio/src/mock_gpio.rs | 140 +++++++++++++++ crates/vhost-device-gpio/src/vhu_gpio.rs | 20 ++- crates/vhost-device-gpio/src/virtio_gpio.rs | 33 ++++ 6 files changed, 208 insertions(+), 181 deletions(-) create mode 100644 crates/vhost-device-gpio/src/mock_gpio.rs create mode 100644 crates/vhost-device-gpio/src/virtio_gpio.rs diff --git a/crates/vhost-device-gpio/src/backend.rs b/crates/vhost-device-gpio/src/backend.rs index 872073b..4c8a897 100644 --- a/crates/vhost-device-gpio/src/backend.rs +++ b/crates/vhost-device-gpio/src/backend.rs @@ -210,7 +210,7 @@ mod tests { use assert_matches::assert_matches; use super::*; - use crate::gpio::tests::DummyDevice; + use crate::mock_gpio::MockGpioDevice; impl DeviceConfig { pub fn new_with(devices: Vec) -> Self { @@ -294,7 +294,7 @@ mod tests { let cmd_args = get_cmd_args(socket_name, "1:4:3:5", 4); assert_matches!( - start_backend::(cmd_args).unwrap_err(), + start_backend::(cmd_args).unwrap_err(), Error::FailedJoiningThreads ); } diff --git a/crates/vhost-device-gpio/src/gpio.rs b/crates/vhost-device-gpio/src/gpio.rs index e9e9d5e..ae29003 100644 --- a/crates/vhost-device-gpio/src/gpio.rs +++ b/crates/vhost-device-gpio/src/gpio.rs @@ -13,7 +13,9 @@ use libgpiod::{chip, line, request, Error as LibGpiodError}; use thiserror::Error as ThisError; use vm_memory::{ByteValued, Le16, Le32}; -type Result = std::result::Result; +use crate::virtio_gpio::*; + +pub(crate) type Result = std::result::Result; #[derive(Copy, Clone, Debug, PartialEq, ThisError)] /// Errors related to low level gpio helpers @@ -47,32 +49,6 @@ pub(crate) enum Error { GpioOperationFailed(&'static str), } -/// Virtio specification definitions -/// Virtio GPIO request types -pub(crate) const VIRTIO_GPIO_MSG_GET_LINE_NAMES: u16 = 0x0001; -pub(crate) const VIRTIO_GPIO_MSG_GET_DIRECTION: u16 = 0x0002; -pub(crate) const VIRTIO_GPIO_MSG_SET_DIRECTION: u16 = 0x0003; -pub(crate) const VIRTIO_GPIO_MSG_GET_VALUE: u16 = 0x0004; -pub(crate) const VIRTIO_GPIO_MSG_SET_VALUE: u16 = 0x0005; -pub(crate) const VIRTIO_GPIO_MSG_IRQ_TYPE: u16 = 0x0006; - -/// Direction types -pub(crate) const VIRTIO_GPIO_DIRECTION_NONE: u8 = 0x00; -pub(crate) const VIRTIO_GPIO_DIRECTION_OUT: u8 = 0x01; -pub(crate) const VIRTIO_GPIO_DIRECTION_IN: u8 = 0x02; - -/// Virtio GPIO IRQ types -pub(crate) const VIRTIO_GPIO_IRQ_TYPE_NONE: u16 = 0x00; -pub(crate) const VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING: u16 = 0x01; -pub(crate) const VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING: u16 = 0x02; -pub(crate) const VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH: u16 = - VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING | VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING; -pub(crate) const VIRTIO_GPIO_IRQ_TYPE_LEVEL_HIGH: u16 = 0x04; -pub(crate) const VIRTIO_GPIO_IRQ_TYPE_LEVEL_LOW: u16 = 0x08; -const VIRTIO_GPIO_IRQ_TYPE_ALL: u16 = VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH - | VIRTIO_GPIO_IRQ_TYPE_LEVEL_HIGH - | VIRTIO_GPIO_IRQ_TYPE_LEVEL_LOW; - /// Virtio GPIO Configuration #[derive(Copy, Clone, Debug, Default, PartialEq)] #[repr(C)] @@ -384,10 +360,10 @@ impl GpioDevice for PhysDevice { } #[derive(Debug, Copy, Clone)] -struct GpioState { - dir: u8, - val: Option, - irq_type: u16, +pub(crate) struct GpioState { + pub dir: u8, + pub val: Option, + pub irq_type: u16, } #[derive(Debug)] @@ -553,135 +529,7 @@ pub(crate) mod tests { use super::Error; use super::*; - - #[derive(Debug)] - pub(crate) struct DummyDevice { - ngpio: u16, - pub(crate) gpio_names: Vec, - state: RwLock>, - num_gpios_result: Result, - gpio_name_result: Result, - direction_result: Result, - set_direction_result: Result<()>, - value_result: Result, - set_value_result: Result<()>, - set_irq_type_result: Result<()>, - pub(crate) wait_for_irq_result: Result, - } - - impl DummyDevice { - pub(crate) fn new(ngpio: u16) -> Self { - Self { - ngpio, - gpio_names: vec!['\0'.to_string(); ngpio.into()], - state: RwLock::new(vec![ - GpioState { - dir: VIRTIO_GPIO_DIRECTION_NONE, - val: None, - irq_type: VIRTIO_GPIO_IRQ_TYPE_NONE, - }; - ngpio.into() - ]), - num_gpios_result: Ok(0), - gpio_name_result: Ok("".to_string()), - direction_result: Ok(0), - set_direction_result: Ok(()), - value_result: Ok(0), - set_value_result: Ok(()), - set_irq_type_result: Ok(()), - wait_for_irq_result: Ok(true), - } - } - } - - impl GpioDevice for DummyDevice { - fn open(_device: u32) -> Result - where - Self: Sized, - { - Ok(DummyDevice::new(8)) - } - - fn num_gpios(&self) -> Result { - if self.num_gpios_result.is_err() { - return self.num_gpios_result; - } - - Ok(self.ngpio) - } - - fn gpio_name(&self, gpio: u16) -> Result { - assert!((gpio as usize) < self.gpio_names.len()); - - if self.gpio_name_result.is_err() { - return self.gpio_name_result.clone(); - } - - Ok(self.gpio_names[gpio as usize].clone()) - } - - fn direction(&self, gpio: u16) -> Result { - if self.direction_result.is_err() { - return self.direction_result; - } - - Ok(self.state.read().unwrap()[gpio as usize].dir) - } - - fn set_direction(&self, gpio: u16, dir: u8, value: u32) -> Result<()> { - if self.set_direction_result.is_err() { - return self.set_direction_result; - } - - self.state.write().unwrap()[gpio as usize].dir = dir; - self.state.write().unwrap()[gpio as usize].val = match dir { - VIRTIO_GPIO_DIRECTION_NONE => None, - VIRTIO_GPIO_DIRECTION_IN => self.state.read().unwrap()[gpio as usize].val, - VIRTIO_GPIO_DIRECTION_OUT => Some(value as u16), - - _ => return Err(Error::GpioDirectionInvalid(dir as u32)), - }; - - Ok(()) - } - - fn value(&self, gpio: u16) -> Result { - if self.value_result.is_err() { - return self.value_result; - } - - if let Some(val) = self.state.read().unwrap()[gpio as usize].val { - Ok(val as u8) - } else { - Err(Error::GpioCurrentValueInvalid) - } - } - - fn set_value(&self, gpio: u16, value: u32) -> Result<()> { - if self.set_value_result.is_err() { - return self.set_value_result; - } - - self.state.write().unwrap()[gpio as usize].val = Some(value as u16); - Ok(()) - } - - fn set_irq_type(&self, _gpio: u16, _value: u16) -> Result<()> { - if self.set_irq_type_result.is_err() { - return self.set_irq_type_result; - } - - Ok(()) - } - - fn wait_for_interrupt(&self, _gpio: u16) -> Result { - if self.wait_for_irq_result.is_err() { - return self.wait_for_irq_result; - } - - Ok(true) - } - } + use crate::mock_gpio::MockGpioDevice; #[test] fn test_verify_gpio_controller() { @@ -700,7 +548,7 @@ pub(crate) mod tests { // Controller adds '\0' for each line. let names_size = size_of_val(&gpio_names) + gpio_names.len(); - let mut device = DummyDevice::new(NGPIO); + let mut device = MockGpioDevice::new(NGPIO); device.gpio_names.clear(); device.gpio_names.append(&mut gpio_names.clone()); let controller = GpioController::new(device).unwrap(); @@ -754,7 +602,7 @@ pub(crate) mod tests { #[test] fn test_verify_gpio_operation() { const NGPIO: u16 = 256; - let device = DummyDevice::new(NGPIO); + let device = MockGpioDevice::new(NGPIO); let controller = GpioController::new(device).unwrap(); for gpio in 0..NGPIO { @@ -880,19 +728,19 @@ pub(crate) mod tests { const NGPIO: u16 = 256; // Get num lines failure let error = Error::GpioOperationFailed("get-num-lines"); - let mut device = DummyDevice::new(NGPIO); + let mut device = MockGpioDevice::new(NGPIO); device.num_gpios_result = Err(error); assert_eq!(GpioController::new(device).unwrap_err(), error); // Get line name failure let error = Error::GpioOperationFailed("get-line-name"); - let mut device = DummyDevice::new(NGPIO); + let mut device = MockGpioDevice::new(NGPIO); device.gpio_name_result = Err(error); assert_eq!(GpioController::new(device).unwrap_err(), error); // Get direction failure let error = Error::GpioOperationFailed("get-direction"); - let mut device = DummyDevice::new(NGPIO); + let mut device = MockGpioDevice::new(NGPIO); device.direction_result = Err(error); assert_eq!(GpioController::new(device).unwrap_err(), error); } @@ -900,7 +748,7 @@ pub(crate) mod tests { #[test] fn test_gpio_set_direction_failure() { const NGPIO: u16 = 256; - let device = DummyDevice::new(NGPIO); + let device = MockGpioDevice::new(NGPIO); let controller = GpioController::new(device).unwrap(); for gpio in 0..NGPIO { @@ -930,7 +778,7 @@ pub(crate) mod tests { #[test] fn test_gpio_set_value_failure() { const NGPIO: u16 = 256; - let device = DummyDevice::new(NGPIO); + let device = MockGpioDevice::new(NGPIO); let controller = GpioController::new(device).unwrap(); for gpio in 0..NGPIO { @@ -948,7 +796,7 @@ pub(crate) mod tests { #[test] fn test_gpio_set_irq_type_failure() { const NGPIO: u16 = 256; - let device = DummyDevice::new(NGPIO); + let device = MockGpioDevice::new(NGPIO); let controller = GpioController::new(device).unwrap(); for gpio in 0..NGPIO { @@ -1002,7 +850,7 @@ pub(crate) mod tests { fn test_gpio_wait_for_interrupt_failure() { const NGPIO: u16 = 256; let err = Error::GpioIrqTypeInvalid(0); - let mut device = DummyDevice::new(NGPIO); + let mut device = MockGpioDevice::new(NGPIO); device.wait_for_irq_result = Err(err); @@ -1016,7 +864,7 @@ pub(crate) mod tests { #[test] fn test_gpio_operation_failure() { const NGPIO: u16 = 256; - let device = DummyDevice::new(NGPIO); + let device = MockGpioDevice::new(NGPIO); let controller = GpioController::new(device).unwrap(); for gpio in 0..NGPIO { diff --git a/crates/vhost-device-gpio/src/main.rs b/crates/vhost-device-gpio/src/main.rs index 9dcb24d..34550df 100644 --- a/crates/vhost-device-gpio/src/main.rs +++ b/crates/vhost-device-gpio/src/main.rs @@ -9,8 +9,12 @@ mod backend; #[cfg(target_env = "gnu")] mod gpio; +#[cfg(test)] +mod mock_gpio; #[cfg(target_env = "gnu")] mod vhu_gpio; +#[cfg(target_env = "gnu")] +mod virtio_gpio; #[cfg(target_env = "gnu")] fn main() { diff --git a/crates/vhost-device-gpio/src/mock_gpio.rs b/crates/vhost-device-gpio/src/mock_gpio.rs new file mode 100644 index 0000000..0191562 --- /dev/null +++ b/crates/vhost-device-gpio/src/mock_gpio.rs @@ -0,0 +1,140 @@ +// Mock GPIO backend device for testing +// +// Copyright 2023 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use std::sync::RwLock; + +use crate::gpio::{Error, GpioDevice, GpioState, Result}; +use crate::virtio_gpio::*; + +#[derive(Debug)] +pub(crate) struct MockGpioDevice { + ngpio: u16, + pub(crate) gpio_names: Vec, + state: RwLock>, + pub num_gpios_result: Result, + pub gpio_name_result: Result, + pub direction_result: Result, + set_direction_result: Result<()>, + value_result: Result, + set_value_result: Result<()>, + set_irq_type_result: Result<()>, + pub(crate) wait_for_irq_result: Result, +} + +impl MockGpioDevice { + pub(crate) fn new(ngpio: u16) -> Self { + Self { + ngpio, + gpio_names: vec!['\0'.to_string(); ngpio.into()], + state: RwLock::new(vec![ + GpioState { + dir: VIRTIO_GPIO_DIRECTION_NONE, + val: None, + irq_type: VIRTIO_GPIO_IRQ_TYPE_NONE, + }; + ngpio.into() + ]), + num_gpios_result: Ok(0), + gpio_name_result: Ok("".to_string()), + direction_result: Ok(0), + set_direction_result: Ok(()), + value_result: Ok(0), + set_value_result: Ok(()), + set_irq_type_result: Ok(()), + wait_for_irq_result: Ok(true), + } + } +} + +impl GpioDevice for MockGpioDevice { + fn open(_device: u32) -> Result + where + Self: Sized, + { + Ok(MockGpioDevice::new(8)) + } + + fn num_gpios(&self) -> Result { + if self.num_gpios_result.is_err() { + return self.num_gpios_result; + } + + Ok(self.ngpio) + } + + fn gpio_name(&self, gpio: u16) -> Result { + assert!((gpio as usize) < self.gpio_names.len()); + + if self.gpio_name_result.is_err() { + return self.gpio_name_result.clone(); + } + + Ok(self.gpio_names[gpio as usize].clone()) + } + + fn direction(&self, gpio: u16) -> Result { + if self.direction_result.is_err() { + return self.direction_result; + } + + Ok(self.state.read().unwrap()[gpio as usize].dir) + } + + fn set_direction(&self, gpio: u16, dir: u8, value: u32) -> Result<()> { + if self.set_direction_result.is_err() { + return self.set_direction_result; + } + + self.state.write().unwrap()[gpio as usize].dir = dir; + self.state.write().unwrap()[gpio as usize].val = match dir { + VIRTIO_GPIO_DIRECTION_NONE => None, + VIRTIO_GPIO_DIRECTION_IN => self.state.read().unwrap()[gpio as usize].val, + VIRTIO_GPIO_DIRECTION_OUT => Some(value as u16), + + _ => return Err(Error::GpioDirectionInvalid(dir as u32)), + }; + + Ok(()) + } + + fn value(&self, gpio: u16) -> Result { + if self.value_result.is_err() { + return self.value_result; + } + + if let Some(val) = self.state.read().unwrap()[gpio as usize].val { + Ok(val as u8) + } else { + Err(Error::GpioCurrentValueInvalid) + } + } + + fn set_value(&self, gpio: u16, value: u32) -> Result<()> { + if self.set_value_result.is_err() { + return self.set_value_result; + } + + self.state.write().unwrap()[gpio as usize].val = Some(value as u16); + Ok(()) + } + + fn set_irq_type(&self, _gpio: u16, _value: u16) -> Result<()> { + if self.set_irq_type_result.is_err() { + return self.set_irq_type_result; + } + + Ok(()) + } + + fn wait_for_interrupt(&self, _gpio: u16) -> Result { + if self.wait_for_irq_result.is_err() { + return self.wait_for_irq_result; + } + + Ok(true) + } +} diff --git a/crates/vhost-device-gpio/src/vhu_gpio.rs b/crates/vhost-device-gpio/src/vhu_gpio.rs index 016cfdc..f11b44e 100644 --- a/crates/vhost-device-gpio/src/vhu_gpio.rs +++ b/crates/vhost-device-gpio/src/vhu_gpio.rs @@ -30,7 +30,8 @@ use vm_memory::{ use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::{EventFd, EFD_NONBLOCK}; -use crate::gpio::{GpioController, GpioDevice, VIRTIO_GPIO_IRQ_TYPE_NONE}; +use crate::gpio::{GpioController, GpioDevice}; +use crate::virtio_gpio::VIRTIO_GPIO_IRQ_TYPE_NONE; /// Possible values of the status field const VIRTIO_GPIO_STATUS_OK: u8 = 0x0; @@ -505,9 +506,10 @@ mod tests { use super::Error; use super::*; - use crate::gpio::tests::DummyDevice; use crate::gpio::Error as GpioError; use crate::gpio::*; + use crate::mock_gpio::MockGpioDevice; + use crate::virtio_gpio::*; // Prepares a single chain of descriptors for request queue fn prepare_desc_chain( @@ -629,7 +631,7 @@ mod tests { } // Validate descriptor chains after processing them, checks pass/failure of - // operation and the value of the buffers updated by the `DummyDevice`. + // operation and the value of the buffers updated by the `MockGpioDevice`. fn validate_desc_chains( desc_chains: Vec, status: u8, @@ -656,7 +658,7 @@ mod tests { fn test_gpio_process_requests_success() { const NGPIO: u16 = 256; const GPIO: u16 = 5; - let device = DummyDevice::new(NGPIO); + let device = MockGpioDevice::new(NGPIO); let controller = GpioController::new(device).unwrap(); let backend = VhostUserGpioBackend::new(controller).unwrap(); let mem = GuestMemoryAtomic::new( @@ -707,7 +709,7 @@ mod tests { fn test_gpio_process_requests_failure() { const NGPIO: u16 = 256; const GPIO: u16 = 5; - let device = DummyDevice::new(NGPIO); + let device = MockGpioDevice::new(NGPIO); let controller = GpioController::new(device).unwrap(); let backend = VhostUserGpioBackend::new(controller).unwrap(); let mem = GuestMemoryAtomic::new( @@ -808,7 +810,7 @@ mod tests { fn test_gpio_process_events_success() { const NGPIO: u16 = 256; const GPIO: u16 = 5; - let device = DummyDevice::new(NGPIO); + let device = MockGpioDevice::new(NGPIO); let controller = GpioController::new(device).unwrap(); let mut backend = VhostUserGpioBackend::new(controller).unwrap(); let mem = GuestMemoryAtomic::new( @@ -862,7 +864,7 @@ mod tests { fn test_gpio_process_events_multi_success() { const NGPIO: u16 = 256; const GPIO: u16 = 5; - let device = DummyDevice::new(NGPIO); + let device = MockGpioDevice::new(NGPIO); let controller = GpioController::new(device).unwrap(); let mut backend = VhostUserGpioBackend::new(controller).unwrap(); let mem = GuestMemoryAtomic::new( @@ -951,7 +953,7 @@ mod tests { fn test_gpio_process_events_failure() { const NGPIO: u16 = 256; let err = GpioError::GpioIrqTypeInvalid(0); - let mut device = DummyDevice::new(NGPIO); + let mut device = MockGpioDevice::new(NGPIO); // This will make process-request fail later with // VIRTIO_GPIO_IRQ_STATUS_INVALID error. @@ -1104,7 +1106,7 @@ mod tests { // Controller adds '\0' for each line. let names_size = std::mem::size_of_val(&gpio_names) + gpio_names.len(); - let mut device = DummyDevice::new(NGPIO); + let mut device = MockGpioDevice::new(NGPIO); device.gpio_names.clear(); device.gpio_names.append(&mut gpio_names); let controller = GpioController::new(device).unwrap(); diff --git a/crates/vhost-device-gpio/src/virtio_gpio.rs b/crates/vhost-device-gpio/src/virtio_gpio.rs new file mode 100644 index 0000000..3574297 --- /dev/null +++ b/crates/vhost-device-gpio/src/virtio_gpio.rs @@ -0,0 +1,33 @@ +// VirtIO GPIO definitions +// +// Copyright 2023 Linaro Ltd. All Rights Reserved. +// Viresh Kumar +// +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +/// Virtio specification definitions +/// Virtio GPIO request types + +pub const VIRTIO_GPIO_MSG_GET_LINE_NAMES: u16 = 0x0001; +pub const VIRTIO_GPIO_MSG_GET_DIRECTION: u16 = 0x0002; +pub const VIRTIO_GPIO_MSG_SET_DIRECTION: u16 = 0x0003; +pub const VIRTIO_GPIO_MSG_GET_VALUE: u16 = 0x0004; +pub const VIRTIO_GPIO_MSG_SET_VALUE: u16 = 0x0005; +pub const VIRTIO_GPIO_MSG_IRQ_TYPE: u16 = 0x0006; + +/// Direction types +pub const VIRTIO_GPIO_DIRECTION_NONE: u8 = 0x00; +pub const VIRTIO_GPIO_DIRECTION_OUT: u8 = 0x01; +pub const VIRTIO_GPIO_DIRECTION_IN: u8 = 0x02; + +/// Virtio GPIO IRQ types +pub const VIRTIO_GPIO_IRQ_TYPE_NONE: u16 = 0x00; +pub const VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING: u16 = 0x01; +pub const VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING: u16 = 0x02; +pub const VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH: u16 = + VIRTIO_GPIO_IRQ_TYPE_EDGE_RISING | VIRTIO_GPIO_IRQ_TYPE_EDGE_FALLING; +pub const VIRTIO_GPIO_IRQ_TYPE_LEVEL_HIGH: u16 = 0x04; +pub const VIRTIO_GPIO_IRQ_TYPE_LEVEL_LOW: u16 = 0x08; +pub const VIRTIO_GPIO_IRQ_TYPE_ALL: u16 = VIRTIO_GPIO_IRQ_TYPE_EDGE_BOTH + | VIRTIO_GPIO_IRQ_TYPE_LEVEL_HIGH + | VIRTIO_GPIO_IRQ_TYPE_LEVEL_LOW; From e020f33842ed90721df1fa1ba5ac72f615594cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 11 Apr 2023 20:41:53 +0100 Subject: [PATCH 183/189] gpio: make MockGpioDevice open take number of gpios MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This gives us a bit more flexibility on creating multiple sized MockGpioDevices. Signed-off-by: Alex Bennée --- crates/vhost-device-gpio/src/mock_gpio.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/vhost-device-gpio/src/mock_gpio.rs b/crates/vhost-device-gpio/src/mock_gpio.rs index 0191562..816712c 100644 --- a/crates/vhost-device-gpio/src/mock_gpio.rs +++ b/crates/vhost-device-gpio/src/mock_gpio.rs @@ -51,11 +51,11 @@ impl MockGpioDevice { } impl GpioDevice for MockGpioDevice { - fn open(_device: u32) -> Result + fn open(ngpios: u32) -> Result where Self: Sized, { - Ok(MockGpioDevice::new(8)) + Ok(MockGpioDevice::new(ngpios.try_into().unwrap())) } fn num_gpios(&self) -> Result { From 2298cc980bd002c2a29870a17ab3deb0c2d682ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 13 Apr 2023 10:33:23 +0100 Subject: [PATCH 184/189] gpio: give better names to MockGpioDevice pins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will help later with info!() output. Signed-off-by: Alex Bennée --- crates/vhost-device-gpio/src/mock_gpio.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/vhost-device-gpio/src/mock_gpio.rs b/crates/vhost-device-gpio/src/mock_gpio.rs index 816712c..6061a06 100644 --- a/crates/vhost-device-gpio/src/mock_gpio.rs +++ b/crates/vhost-device-gpio/src/mock_gpio.rs @@ -27,9 +27,14 @@ pub(crate) struct MockGpioDevice { impl MockGpioDevice { pub(crate) fn new(ngpio: u16) -> Self { + let mut gpio_names = Vec::with_capacity(ngpio.into()); + for i in 0..ngpio { + gpio_names.push(format!("dummy{}", i)); + } + Self { ngpio, - gpio_names: vec!['\0'.to_string(); ngpio.into()], + gpio_names, state: RwLock::new(vec![ GpioState { dir: VIRTIO_GPIO_DIRECTION_NONE, From e93b79ebf05b8f9b3311dbdd04f00cfa98852903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 6 Apr 2023 20:58:49 +0100 Subject: [PATCH 185/189] gpio: handle simulated device configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the configuration to accept the form n:sn:n:sn. Currently any config for simulated devices gets skipped. This introduces GpioDeviceType to represent the two types of GPIO device we have. Signed-off-by: Alex Bennée --- crates/vhost-device-gpio/src/backend.rs | 87 +++++++++++++++++++------ 1 file changed, 68 insertions(+), 19 deletions(-) diff --git a/crates/vhost-device-gpio/src/backend.rs b/crates/vhost-device-gpio/src/backend.rs index 4c8a897..2aae79e 100644 --- a/crates/vhost-device-gpio/src/backend.rs +++ b/crates/vhost-device-gpio/src/backend.rs @@ -43,6 +43,8 @@ pub(crate) enum Error { CouldNotCreateBackend(crate::vhu_gpio::Error), #[error("Could not create daemon: {0}")] CouldNotCreateDaemon(vhost_user_backend::Error), + #[error("Unimplemented")] + DeviceUnimplemented, } #[derive(Parser, Debug)] @@ -56,16 +58,40 @@ struct GpioArgs { #[clap(short = 'c', long, default_value_t = 1)] socket_count: usize, - /// List of GPIO devices, one for each guest, in the format [:]. The first entry is for - /// Guest that connects to socket 0, next one for socket 1, and so on. Each device number here - /// will be used to access the corresponding /dev/gpiochipX. Example, "-c 4 -l 3:4:6:1" + /// List of GPIO devices, one for each guest, in the format + /// [:]. The first entry is for Guest that connects to + /// socket 0, next one for socket 1, and so on. Each device number + /// here will be used to access the corresponding /dev/gpiochipX. + /// or simulate a GPIO device with N pins. Example, "-c 4 -l + /// 3:s4:6:s1" #[clap(short = 'l', long)] device_list: String, } +#[derive(Debug, Clone, Copy, PartialEq)] +enum GpioDeviceType { + PhysicalDevice { id: u32 }, + SimulatedDevice { num_gpios: u32 }, +} + +impl GpioDeviceType { + fn new(cfg: &str) -> Result { + match cfg.strip_prefix('s') { + Some(num) => { + let num_gpios = num.parse::().map_err(Error::ParseFailure)?; + Ok(GpioDeviceType::SimulatedDevice { num_gpios }) + } + _ => { + let id = cfg.parse::().map_err(Error::ParseFailure)?; + Ok(GpioDeviceType::PhysicalDevice { id }) + } + } + } +} + #[derive(Debug, PartialEq)] pub(crate) struct DeviceConfig { - inner: Vec, + inner: Vec, } impl DeviceConfig { @@ -73,13 +99,15 @@ impl DeviceConfig { Self { inner: Vec::new() } } - fn contains_device(&self, number: u32) -> bool { - self.inner.iter().any(|elem| *elem == number) + fn contains_device(&self, device: GpioDeviceType) -> bool { + self.inner.contains(&device) } - fn push(&mut self, device: u32) -> Result<()> { - if self.contains_device(device) { - return Err(Error::DeviceDuplicate(device)); + fn push(&mut self, device: GpioDeviceType) -> Result<()> { + if let GpioDeviceType::PhysicalDevice { id } = device { + if self.contains_device(device) { + return Err(Error::DeviceDuplicate(id)); + } } self.inner.push(device); @@ -95,8 +123,7 @@ impl TryFrom<&str> for DeviceConfig { let mut devices = DeviceConfig::new(); for info in list.iter() { - let number = info.parse::().map_err(Error::ParseFailure)?; - devices.push(number)?; + devices.push(GpioDeviceType::new(info)?)?; } Ok(devices) } @@ -140,7 +167,7 @@ fn start_backend(args: GpioArgs) -> Resul for i in 0..config.socket_count { let socket = config.socket_path.to_owned() + &i.to_string(); - let device_num = config.devices.inner[i]; + let cfg = config.devices.inner[i]; let handle: JoinHandle> = spawn(move || loop { // A separate thread is spawned for each socket and can connect to a separate guest. @@ -151,9 +178,17 @@ fn start_backend(args: GpioArgs) -> Resul // threads, and so the code uses unwrap() instead. The panic on a thread won't cause // trouble to other threads/guests or the main() function and should be safe for the // daemon. - let device = D::open(device_num).map_err(Error::CouldNotOpenDevice)?; - let controller = - GpioController::::new(device).map_err(Error::CouldNotCreateGpioController)?; + let controller = match cfg { + GpioDeviceType::PhysicalDevice { id } => match D::open(id) { + Ok(device) => match GpioController::::new(device) { + Ok(controller) => controller, + Err(error) => return Err(Error::CouldNotCreateGpioController(error)), + }, + Err(error) => return Err(Error::CouldNotOpenDevice(error)), + }, + _ => return Err(Error::DeviceUnimplemented), + }; + let backend = Arc::new(RwLock::new( VhostUserGpioBackend::new(controller).map_err(Error::CouldNotCreateBackend)?, )); @@ -214,7 +249,12 @@ mod tests { impl DeviceConfig { pub fn new_with(devices: Vec) -> Self { - DeviceConfig { inner: devices } + DeviceConfig { + inner: devices + .into_iter() + .map(|id| GpioDeviceType::PhysicalDevice { id }) + .collect(), + } } } @@ -230,10 +270,19 @@ mod tests { fn test_gpio_device_config() { let mut config = DeviceConfig::new(); - config.push(5).unwrap(); - config.push(6).unwrap(); + config + .push(GpioDeviceType::PhysicalDevice { id: 5 }) + .unwrap(); + config + .push(GpioDeviceType::PhysicalDevice { id: 6 }) + .unwrap(); - assert_matches!(config.push(5).unwrap_err(), Error::DeviceDuplicate(5)); + assert_matches!( + config + .push(GpioDeviceType::PhysicalDevice { id: 5 }) + .unwrap_err(), + Error::DeviceDuplicate(5) + ); } #[test] From a8ce1ec2e0a1eeeba87bc47d6187f031faf7d6f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 11 Apr 2023 15:36:30 +0100 Subject: [PATCH 186/189] gpio: introduce "mock_gpio" let cfg drive device type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we have our plumbing in place we can separate the starting of the device backend into a new function and call appropriately. As the config is no longer parsed inside start_backend we can ensure we pass any errors up rather than blindly unwrapping. We can drop DeviceUnimplemented as we can't build a version where this happens. Signed-off-by: Alex Bennée --- crates/vhost-device-gpio/Cargo.toml | 1 + crates/vhost-device-gpio/src/backend.rs | 137 +++++++++++++----------- crates/vhost-device-gpio/src/main.rs | 2 +- 3 files changed, 77 insertions(+), 63 deletions(-) diff --git a/crates/vhost-device-gpio/Cargo.toml b/crates/vhost-device-gpio/Cargo.toml index 9f19c92..779453c 100644 --- a/crates/vhost-device-gpio/Cargo.toml +++ b/crates/vhost-device-gpio/Cargo.toml @@ -13,6 +13,7 @@ edition = "2021" [features] xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"] +mock_gpio = [] [dependencies] clap = { version = "4.4", features = ["derive"] } diff --git a/crates/vhost-device-gpio/src/backend.rs b/crates/vhost-device-gpio/src/backend.rs index 2aae79e..466577a 100644 --- a/crates/vhost-device-gpio/src/backend.rs +++ b/crates/vhost-device-gpio/src/backend.rs @@ -20,6 +20,9 @@ use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; use crate::gpio::{GpioController, GpioDevice, PhysDevice}; use crate::vhu_gpio::VhostUserGpioBackend; +#[cfg(any(test, feature = "mock_gpio"))] +use crate::mock_gpio::MockGpioDevice; + pub(crate) type Result = std::result::Result; #[derive(Debug, ThisError)] @@ -43,12 +46,15 @@ pub(crate) enum Error { CouldNotCreateBackend(crate::vhu_gpio::Error), #[error("Could not create daemon: {0}")] CouldNotCreateDaemon(vhost_user_backend::Error), - #[error("Unimplemented")] - DeviceUnimplemented, } +const GPIO_AFTER_HELP: &str = "Each device number here will be used to \ +access the corresponding /dev/gpiochipX or simulate a GPIO device \ +with N pins (when feature enabled). \ +Example, \"-c 4 -l 3:s4:6:s1\"\n"; + #[derive(Parser, Debug)] -#[clap(author, version, about, long_about = None)] +#[clap(author, version, about, long_about = None, after_help = GPIO_AFTER_HELP)] struct GpioArgs { /// Location of vhost-user Unix domain socket. This is suffixed by 0,1,2..socket_count-1. #[clap(short, long)] @@ -59,24 +65,26 @@ struct GpioArgs { socket_count: usize, /// List of GPIO devices, one for each guest, in the format - /// [:]. The first entry is for Guest that connects to - /// socket 0, next one for socket 1, and so on. Each device number - /// here will be used to access the corresponding /dev/gpiochipX. - /// or simulate a GPIO device with N pins. Example, "-c 4 -l - /// 3:s4:6:s1" + /// [s][:[s]]. #[clap(short = 'l', long)] device_list: String, } #[derive(Debug, Clone, Copy, PartialEq)] enum GpioDeviceType { - PhysicalDevice { id: u32 }, - SimulatedDevice { num_gpios: u32 }, + PhysicalDevice { + id: u32, + }, + #[cfg(any(test, feature = "mock_gpio"))] + SimulatedDevice { + num_gpios: u32, + }, } impl GpioDeviceType { fn new(cfg: &str) -> Result { match cfg.strip_prefix('s') { + #[cfg(any(test, feature = "mock_gpio"))] Some(num) => { let num_gpios = num.parse::().map_err(Error::ParseFailure)?; Ok(GpioDeviceType::SimulatedDevice { num_gpios }) @@ -104,10 +112,14 @@ impl DeviceConfig { } fn push(&mut self, device: GpioDeviceType) -> Result<()> { - if let GpioDeviceType::PhysicalDevice { id } = device { - if self.contains_device(device) { - return Err(Error::DeviceDuplicate(id)); + match device { + GpioDeviceType::PhysicalDevice { id } => { + if self.contains_device(GpioDeviceType::PhysicalDevice { id }) { + return Err(Error::DeviceDuplicate(id)); + } } + #[cfg(any(test, feature = "mock_gpio"))] + GpioDeviceType::SimulatedDevice { num_gpios: _ } => {} } self.inner.push(device); @@ -161,8 +173,37 @@ impl TryFrom for GpioConfiguration { } } -fn start_backend(args: GpioArgs) -> Result<()> { - let config = GpioConfiguration::try_from(args).unwrap(); +fn start_device_backend(device: D, socket: String) { + let controller = GpioController::::new(device).unwrap(); + let backend = Arc::new(RwLock::new(VhostUserGpioBackend::new(controller).unwrap())); + let listener = Listener::new(socket, true).unwrap(); + + let mut daemon = VhostUserDaemon::new( + String::from("vhost-device-gpio-backend"), + backend.clone(), + GuestMemoryAtomic::new(GuestMemoryMmap::new()), + ) + .unwrap(); + + daemon.start(listener).unwrap(); + + match daemon.wait() { + Ok(()) => { + info!("Stopping cleanly."); + } + Err(vhost_user_backend::Error::HandleRequest(vhost_user::Error::PartialMessage)) => { + info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug."); + } + Err(e) => { + warn!("Error running daemon: {:?}", e); + } + } + // No matter the result, we need to shut down the worker thread. + backend.read().unwrap().exit_event.write(1).unwrap(); +} + +fn start_backend(args: GpioArgs) -> Result<()> { + let config = GpioConfiguration::try_from(args)?; let mut handles = Vec::new(); for i in 0..config.socket_count { @@ -178,47 +219,17 @@ fn start_backend(args: GpioArgs) -> Resul // threads, and so the code uses unwrap() instead. The panic on a thread won't cause // trouble to other threads/guests or the main() function and should be safe for the // daemon. - let controller = match cfg { - GpioDeviceType::PhysicalDevice { id } => match D::open(id) { - Ok(device) => match GpioController::::new(device) { - Ok(controller) => controller, - Err(error) => return Err(Error::CouldNotCreateGpioController(error)), - }, - Err(error) => return Err(Error::CouldNotOpenDevice(error)), - }, - _ => return Err(Error::DeviceUnimplemented), + match cfg { + GpioDeviceType::PhysicalDevice { id } => { + let controller = PhysDevice::open(id).unwrap(); + start_device_backend(controller, socket.clone()); + } + #[cfg(any(test, feature = "mock_gpio"))] + GpioDeviceType::SimulatedDevice { num_gpios } => { + let controller = MockGpioDevice::open(num_gpios).unwrap(); + start_device_backend(controller, socket.clone()); + } }; - - let backend = Arc::new(RwLock::new( - VhostUserGpioBackend::new(controller).map_err(Error::CouldNotCreateBackend)?, - )); - let listener = Listener::new(socket.clone(), true).unwrap(); - - let mut daemon = VhostUserDaemon::new( - String::from("vhost-device-gpio-backend"), - backend.clone(), - GuestMemoryAtomic::new(GuestMemoryMmap::new()), - ) - .map_err(Error::CouldNotCreateDaemon)?; - - daemon.start(listener).unwrap(); - - match daemon.wait() { - Ok(()) => { - info!("Stopping cleanly."); - } - Err(vhost_user_backend::Error::HandleRequest( - vhost_user::Error::PartialMessage | vhost_user::Error::Disconnected, - )) => { - info!("vhost-user connection closed with partial message. If the VM is shutting down, this is expected behavior; otherwise, it might be a bug."); - } - Err(e) => { - warn!("Error running daemon: {:?}", e); - } - } - - // No matter the result, we need to shut down the worker thread. - backend.read().unwrap().exit_event.write(1).unwrap(); }); handles.push(handle); @@ -234,8 +245,8 @@ fn start_backend(args: GpioArgs) -> Resul pub(crate) fn gpio_init() { env_logger::init(); - if let Err(e) = start_backend::(GpioArgs::parse()) { - error!("{e}"); + if let Err(e) = start_backend(GpioArgs::parse()) { + error!("Fatal error starting backend: {e}"); exit(1); } } @@ -245,7 +256,6 @@ mod tests { use assert_matches::assert_matches; use super::*; - use crate::mock_gpio::MockGpioDevice; impl DeviceConfig { pub fn new_with(devices: Vec) -> Self { @@ -316,6 +326,10 @@ mod tests { GpioConfiguration::try_from(cmd_args).unwrap_err(), Error::DeviceCountMismatch(5, 4) ); + + // Parse mixed device and simulated + let cmd_args = get_cmd_args(socket_name, "1:s4", 2); + assert_matches!(GpioConfiguration::try_from(cmd_args), Ok(_)); } #[test] @@ -337,13 +351,12 @@ mod tests { } #[test] - fn test_gpio_fail_listener() { - // This will fail the listeners and thread will panic. + fn test_gpio_fail_listener_mock() { let socket_name = "~/path/not/present/gpio"; - let cmd_args = get_cmd_args(socket_name, "1:4:3:5", 4); + let cmd_args = get_cmd_args(socket_name, "s1:s4:s3:s5", 4); assert_matches!( - start_backend::(cmd_args).unwrap_err(), + start_backend(cmd_args).unwrap_err(), Error::FailedJoiningThreads ); } diff --git a/crates/vhost-device-gpio/src/main.rs b/crates/vhost-device-gpio/src/main.rs index 34550df..cb0e11f 100644 --- a/crates/vhost-device-gpio/src/main.rs +++ b/crates/vhost-device-gpio/src/main.rs @@ -9,7 +9,7 @@ mod backend; #[cfg(target_env = "gnu")] mod gpio; -#[cfg(test)] +#[cfg(all(target_env = "gnu", any(test, feature = "mock_gpio")))] mod mock_gpio; #[cfg(target_env = "gnu")] mod vhu_gpio; From 28690b6e87d1fab181998796528b3930fa612b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 13 Apr 2023 10:40:23 +0100 Subject: [PATCH 187/189] gpio: add some info logging to MockGpioDevice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is not much point having a MockGpioDevice if you don't get to see when it state changes. Currently only MockGpioDevice emits anything bellow an error or warning so we default to the "info" level. Any debug added in the future will need to override RUST_LOG. Signed-off-by: Alex Bennée --- crates/vhost-device-gpio/src/backend.rs | 4 +++- crates/vhost-device-gpio/src/mock_gpio.rs | 18 +++++++++++++++++- crates/vhost-device-gpio/src/vhu_gpio.rs | 2 +- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/crates/vhost-device-gpio/src/backend.rs b/crates/vhost-device-gpio/src/backend.rs index 466577a..0b49142 100644 --- a/crates/vhost-device-gpio/src/backend.rs +++ b/crates/vhost-device-gpio/src/backend.rs @@ -12,6 +12,7 @@ use std::sync::{Arc, RwLock}; use std::thread::{spawn, JoinHandle}; use clap::Parser; +use env_logger::Env; use thiserror::Error as ThisError; use vhost::{vhost_user, vhost_user::Listener}; use vhost_user_backend::VhostUserDaemon; @@ -243,7 +244,8 @@ fn start_backend(args: GpioArgs) -> Result<()> { } pub(crate) fn gpio_init() { - env_logger::init(); + let env = Env::default().filter_or("RUST_LOG", "info"); + env_logger::init_from_env(env); if let Err(e) = start_backend(GpioArgs::parse()) { error!("Fatal error starting backend: {e}"); diff --git a/crates/vhost-device-gpio/src/mock_gpio.rs b/crates/vhost-device-gpio/src/mock_gpio.rs index 6061a06..d2ffed3 100644 --- a/crates/vhost-device-gpio/src/mock_gpio.rs +++ b/crates/vhost-device-gpio/src/mock_gpio.rs @@ -5,6 +5,7 @@ // // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause +use log::info; use std::sync::RwLock; use crate::gpio::{Error, GpioDevice, GpioState, Result}; @@ -90,6 +91,11 @@ impl GpioDevice for MockGpioDevice { } fn set_direction(&self, gpio: u16, dir: u8, value: u32) -> Result<()> { + info!( + "gpio {} set direction to {}", + self.gpio_names[gpio as usize], dir + ); + if self.set_direction_result.is_err() { return self.set_direction_result; } @@ -119,6 +125,11 @@ impl GpioDevice for MockGpioDevice { } fn set_value(&self, gpio: u16, value: u32) -> Result<()> { + info!( + "gpio {} set value to {}", + self.gpio_names[gpio as usize], value + ); + if self.set_value_result.is_err() { return self.set_value_result; } @@ -127,7 +138,12 @@ impl GpioDevice for MockGpioDevice { Ok(()) } - fn set_irq_type(&self, _gpio: u16, _value: u16) -> Result<()> { + fn set_irq_type(&self, gpio: u16, value: u16) -> Result<()> { + info!( + "gpio {} set irq type to {}", + self.gpio_name(gpio).unwrap(), + value + ); if self.set_irq_type_result.is_err() { return self.set_irq_type_result; } diff --git a/crates/vhost-device-gpio/src/vhu_gpio.rs b/crates/vhost-device-gpio/src/vhu_gpio.rs index f11b44e..3086b39 100644 --- a/crates/vhost-device-gpio/src/vhu_gpio.rs +++ b/crates/vhost-device-gpio/src/vhu_gpio.rs @@ -424,7 +424,7 @@ impl VhostUserBackendMut } fn set_event_idx(&mut self, enabled: bool) { - dbg!(self.event_idx = enabled); + self.event_idx = enabled; } fn update_memory(&mut self, mem: GuestMemoryAtomic) -> IoResult<()> { From e4aefda81734fe5632dc0557512975ff178dd782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 14 Sep 2023 15:23:24 +0100 Subject: [PATCH 188/189] gpio: document the "mock_gpio" feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Bennée --- crates/vhost-device-gpio/README.md | 22 ++++++++++++++++++++++ crates/vhost-device-rng/CHANGELOG.md | 2 ++ 2 files changed, 24 insertions(+) diff --git a/crates/vhost-device-gpio/README.md b/crates/vhost-device-gpio/README.md index c063698..0daa190 100644 --- a/crates/vhost-device-gpio/README.md +++ b/crates/vhost-device-gpio/README.md @@ -43,6 +43,28 @@ Examples section below. --socket-count. For example, the GPIO device 0 will be allocated to the guest with "0" path. +## MockGpioDevice support + +As connecting VM guests to random GPIO pins on your host is generally +asking for trouble you can enable the "mock_gpio" feature in your build: + + cargo build --features "mock_gpio" + +You can then enable simulated GPIOs using the 's' prefix: + + --device-list s4,s8 + +Which will create two gpio devices, the first with 4 pins and the +second with 8. By default updates are display via env logger: + + vhost-device-gpio -s /tmp/vus.sock -c 1 -l s4 + [2023-09-14T14:15:14Z INFO vhost_device_gpio::mock_gpio] gpio dummy0 set value to 1 + [2023-09-14T14:15:14Z INFO vhost_device_gpio::mock_gpio] gpio dummy0 set direction to 1 + [2023-09-14T14:15:14Z INFO vhost_device_gpio::mock_gpio] gpio dummy0 set direction to 0 + [2023-09-14T14:15:19Z INFO vhost_device_gpio::mock_gpio] gpio dummy1 set value to 1 + [2023-09-14T14:15:19Z INFO vhost_device_gpio::mock_gpio] gpio dummy1 set direction to 1 + [2023-09-14T14:15:19Z INFO vhost_device_gpio::mock_gpio] gpio dummy1 set direction to 0 + ## Examples The daemon should be started first: diff --git a/crates/vhost-device-rng/CHANGELOG.md b/crates/vhost-device-rng/CHANGELOG.md index 51d3f04..362be20 100644 --- a/crates/vhost-device-rng/CHANGELOG.md +++ b/crates/vhost-device-rng/CHANGELOG.md @@ -3,6 +3,8 @@ ### Added + - optional "mock_gpio" feature for testing + ### Changed ### Fixed From 026cf0e5320d24f72b416e55d7fd020fb2338034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 14 Sep 2023 14:12:35 +0100 Subject: [PATCH 189/189] gpio: re-arrange start_backend to handle config errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The compiler warns with the move that: error: variants `CouldNotOpenDevice`, `CouldNotCreateGpioController`, `CouldNotCreateBackend`, and `CouldNotCreateDaemon` are never constructed --> crates/vhost-device-gpio/src/backend.rs:43:5 | 31 | pub(crate) enum Error { | ----- variants in this enum ... 43 | CouldNotOpenDevice(crate::gpio::Error), | ^^^^^^^^^^^^^^^^^^ 44 | #[error("Could not create gpio controller: {0}")] 45 | CouldNotCreateGpioController(crate::gpio::Error), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 46 | #[error("Could not create gpio backend: {0}")] 47 | CouldNotCreateBackend(crate::vhu_gpio::Error), | ^^^^^^^^^^^^^^^^^^^^^ 48 | #[error("Could not create daemon: {0}")] 49 | CouldNotCreateDaemon(vhost_user_backend::Error), | ^^^^^^^^^^^^^^^^^^^^ | = note: `Error` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis = note: `-D dead-code` implied by `-D warnings` error: could not compile `vhost-device-gpio` (bin "vhost-device-gpio") due to previous error Because we never really care about failure within the spawn loop. Handle this by ensuring errors propagate back to the caller. Signed-off-by: Alex Bennée --- crates/vhost-device-gpio/src/backend.rs | 33 ++++++++++++++----------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/crates/vhost-device-gpio/src/backend.rs b/crates/vhost-device-gpio/src/backend.rs index 0b49142..e0cc8c6 100644 --- a/crates/vhost-device-gpio/src/backend.rs +++ b/crates/vhost-device-gpio/src/backend.rs @@ -174,9 +174,11 @@ impl TryFrom for GpioConfiguration { } } -fn start_device_backend(device: D, socket: String) { - let controller = GpioController::::new(device).unwrap(); - let backend = Arc::new(RwLock::new(VhostUserGpioBackend::new(controller).unwrap())); +fn start_device_backend(device: D, socket: String) -> Result<()> { + let controller = GpioController::new(device).map_err(Error::CouldNotCreateGpioController)?; + let backend = Arc::new(RwLock::new( + VhostUserGpioBackend::new(controller).map_err(Error::CouldNotCreateBackend)?, + )); let listener = Listener::new(socket, true).unwrap(); let mut daemon = VhostUserDaemon::new( @@ -184,7 +186,7 @@ fn start_device_backend(device: D, socket: String) { backend.clone(), GuestMemoryAtomic::new(GuestMemoryMmap::new()), ) - .unwrap(); + .map_err(Error::CouldNotCreateDaemon)?; daemon.start(listener).unwrap(); @@ -201,6 +203,7 @@ fn start_device_backend(device: D, socket: String) { } // No matter the result, we need to shut down the worker thread. backend.read().unwrap().exit_event.write(1).unwrap(); + Ok(()) } fn start_backend(args: GpioArgs) -> Result<()> { @@ -212,23 +215,23 @@ fn start_backend(args: GpioArgs) -> Result<()> { let cfg = config.devices.inner[i]; let handle: JoinHandle> = spawn(move || loop { - // A separate thread is spawned for each socket and can connect to a separate guest. - // These are run in an infinite loop to not require the daemon to be restarted once a - // guest exits. + // A separate thread is spawned for each socket and can + // connect to a separate guest. These are run in an + // infinite loop to not require the daemon to be restarted + // once a guest exits. // - // There isn't much value in complicating code here to return an error from the - // threads, and so the code uses unwrap() instead. The panic on a thread won't cause - // trouble to other threads/guests or the main() function and should be safe for the - // daemon. + // However if we fail to spawn (due to bad config or + // other reason) we will bail out of the spawning and + // propagate the error back to gpio_init(). match cfg { GpioDeviceType::PhysicalDevice { id } => { - let controller = PhysDevice::open(id).unwrap(); - start_device_backend(controller, socket.clone()); + let controller = PhysDevice::open(id).map_err(Error::CouldNotOpenDevice)?; + start_device_backend(controller, socket.clone())?; } #[cfg(any(test, feature = "mock_gpio"))] GpioDeviceType::SimulatedDevice { num_gpios } => { - let controller = MockGpioDevice::open(num_gpios).unwrap(); - start_device_backend(controller, socket.clone()); + let controller = MockGpioDevice::open(num_gpios).unwrap(); // cannot fail + start_device_backend(controller, socket.clone())?; } }; });