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 01/85] 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 9f8e2cae..95e88eb9 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 02/85] 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 95e88eb9..f84b9f85 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 03/85] 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 f84b9f85..4c5f550f 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 04/85] 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 c2f8c93e..3640704b 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 05/85] 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 4c5f550f..2c990858 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 = [ - "bitflags", + "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_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 06/85] 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 2c990858..7462b632 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 07/85] 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 7462b632..65878b94 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 08/85] 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 65878b94..c44dc709 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 09/85] 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 c44dc709..0fb2ff37 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 10/85] 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 3640704b..8627b376 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 11/85] 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 0fb2ff37..a593b788 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 12/85] 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 3d36c539..0f192f9a 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 102b3d10..41eea0c6 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 93d898f1..e9a95b42 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 13/85] 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 a593b788..fe616171 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 14/85] 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 fe616171..9aaaff15 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 15/85] 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 9aaaff15..860e5d24 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 1841ab95..ab37b344 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 90be5c94..8c5c13b2 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 00ff57cf..88398f7f 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 ae69186c..b8148435 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 16/85] 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 860e5d24..bceebbae 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 17/85] 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 bceebbae..01b47346 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 18/85] 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 01b47346..160b2bab 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 19/85] 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 f3e8cd8c..722ad249 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 0f192f9a..41c7ef45 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 f32df28e..d101047e 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 ff55cd8d..ef718d71 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 41eea0c6..72033f8a 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 e9a95b42..ab61630c 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 8436c95b..25e33354 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 20/85] 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 160b2bab..4807c61f 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 88398f7f..ce84dda8 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 21/85] 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 4807c61f..72b538d0 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 22/85] 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 72b538d0..2dcaa9b8 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 23/85] 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 2dcaa9b8..85191bbd 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 b8148435..e91f1cd6 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 722ad249..5190ccd7 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 41c7ef45..3d365d14 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 72033f8a..e9b12a32 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 24/85] 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 85191bbd..d1db3298 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 25/85] 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 d1db3298..095d39fb 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 26/85] 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 095d39fb..be524449 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 27/85] 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 e9b12a32..8e0863e9 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 28/85] 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 ab61630c..6d13e021 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 29/85] 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 5190ccd7..14d084bf 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 30/85] 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 be524449..b5e33496 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 31/85] 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 b5e33496..ae6a21e2 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 32/85] 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 ae6a21e2..654e36b0 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 33/85] 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 654e36b0..c8af154c 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 ab37b344..20c50d62 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 8c5c13b2..7e9c5705 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 ce84dda8..b77b6d8c 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 e91f1cd6..71fb3473 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 34/85] 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 c8af154c..f6330912 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 20c50d62..aba760df 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 7e9c5705..9608761e 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 b77b6d8c..fa9a99c2 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 71fb3473..50c3699f 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 35/85] 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 f6330912..e8248ed1 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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7254bb0f6111fa84cb24bbf1dfb327ad02b1056ce8ed7f13962b8d0ca3aaa2" -dependencies = [ - "virtio-bindings 0.1.0", - "virtio-queue 0.7.1", - "vm-memory 0.10.0", -] - -[[package]] -name = "vm-memory" -version = "0.10.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688a70366615b45575a424d9c665561c1b5ab2224d494f706b6a6812911a827c" +checksum = "cb198c4dd87bf0b4f6b5d8cb41284fca13763a5a1a7e5b8a7ccce45e46d4cf73" 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 36/85] 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 e8248ed1..c12322a8 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 37/85] 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 8627b376..285971e8 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 38/85] 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 c12322a8..75cda6dd 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 39/85] 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 75cda6dd..4a4e275e 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 40/85] 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 4a4e275e..6e5b6858 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 41/85] 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 6e5b6858..2c95bb07 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 4d1a538c..633fa4d3 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 00000000..682c9cad --- /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 00000000..5bf256ea --- /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 285971e8..8627b376 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 42/85] 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 00000000..b967efab --- /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 00000000..0c05d584 --- /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 00000000..7ced47ef --- /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 43/85] 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 b967efab..25dcad99 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 00000000..66777e78 --- /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 00000000..55721740 --- /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 00000000..cb94baa8 --- /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 00000000..377b9a4a --- /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 00000000..e0c30e76 --- /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 00000000..352d0698 --- /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 00000000..82e660cc --- /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 0c05d584..9c1f1589 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 7ced47ef..ad7da96e 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 44/85] 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 e3d77522..9cf6dcc8 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 377b9a4a..d697842e 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 00000000..38b7e4a2 --- /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 00000000..19f4bd47 --- /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 00000000..b112ba00 --- /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 00000000..016d6a60 --- /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 45/85] 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 00000000..d471959d --- /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 25dcad99..00000000 --- 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 5bf256ea..bfb8ec25 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 00000000..2cb12e40 --- /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 00000000..2f2ecb37 --- /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 46/85] 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 bfb8ec25..e48959f2 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 2cb12e40..915419e2 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 2f2ecb37..423c0aba 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 47/85] 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 00000000..2a2f7fae --- /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 00000000..46779df1 --- /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 48/85] 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 915419e2..85676546 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 49/85] 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 e48959f2..9e7813f1 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 66777e78..b21c833f 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 55721740..bdfed333 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 b112ba00..f23e01a1 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 50/85] 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 b21c833f..117c66cd 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 bdfed333..39fed99c 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 f23e01a1..8e8ffc18 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 51/85] 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 117c66cd..7ac4884a 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 39fed99c..43cb0b40 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 52/85] 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 00000000..d26621d7 --- /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 00000000..d26621d7 --- /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 00000000..6abc2765 --- /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 00000000..254d4e2a --- /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 00000000..34e48ea2 --- /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 00000000..6ebf8c6b --- /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 00000000..927aec57 --- /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 53/85] 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 254d4e2a..93220541 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 54/85] 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 14d084bf..8e594f0a 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 3d365d14..babb12ad 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 8e0863e9..3f52d95b 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 55/85] 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 2c95bb07..19a18080 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 56/85] 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 19a18080..6fedb447 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 57/85] 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 8627b376..285971e8 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 58/85] 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 6fedb447..c775fc97 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 50c3699f..841c7d9c 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 59/85] 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 c775fc97..fa1db9ae 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 aba760df..8dc7b3b3 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 60/85] 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 285971e8..7e9af575 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 61/85] 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 fa1db9ae..74d852b5 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 62/85] 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 74d852b5..e6aaf4a0 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 63/85] 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 e6aaf4a0..8c3e428b 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 64/85] 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 8c3e428b..1fe04f40 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 65/85] 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 1fe04f40..16dba7be 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 66/85] 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 16dba7be..b4e7ef04 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 8dc7b3b3..edd30494 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 9608761e..0505d883 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 fa9a99c2..b39d8f9e 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 682c9cad..b1b9d9d3 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 841c7d9c..bcbf0b86 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 67/85] 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 b4e7ef04..b1ca9b9d 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 68/85] 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 7e9af575..9dfe5b26 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 69/85] 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 b1ca9b9d..f3f12f00 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 70/85] 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 f3f12f00..f1138a0a 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 71/85] 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 06673f7e..018cc197 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 72/85] 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 babb12ad..86b2ad44 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 73/85] 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 | 30 +++- 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, 357 insertions(+), 44 deletions(-) diff --git a/crates/vsock/README.md b/crates/vsock/README.md index 8e594f0a..605b0a5e 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 86b2ad44..eb2d7e32 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 crate::vhu_vsock::{VhostUserVsockBackend, VsockConfig}; +use std::{ + collections::HashMap, + convert::TryFrom, + sync::{Arc, RwLock}, + thread, +}; + +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 d101047e..31e2f76a 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 3f52d95b..1dfc0aa3 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 6d13e021..d49718ff 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 74/85] 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 31e2f76a..7defac91 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 d49718ff..6fabdf46 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 75/85] 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 f1138a0a..0d3172ad 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 bcbf0b86..6b149e6b 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 76/85] 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 6fabdf46..a6c170d2 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 77/85] 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 0d3172ad..a95fec29 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 78/85] 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 a95fec29..8c240e21 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 edd30494..d3f85a26 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 0505d883..a610d8a9 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 b39d8f9e..043a98b0 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 b1b9d9d3..a2194fac 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 6b149e6b..04240b1f 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 79/85] 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 8c240e21..d358e7e5 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 80/85] 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 00000000..1cd601d0 --- /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 00000000..a60f1af6 --- /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 00000000..1cd601d0 --- /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 00000000..a60f1af6 --- /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 00000000..1cd601d0 --- /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 00000000..a60f1af6 --- /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 00000000..1cd601d0 --- /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 00000000..a60f1af6 --- /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 00000000..1cd601d0 --- /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 00000000..a60f1af6 --- /dev/null +++ b/crates/vsock/LICENSE-BSD-3-Clause @@ -0,0 +1 @@ +../../LICENSE-BSD-3-Clause \ No newline at end of file From 5d9c5e4895ea1b62aaa52407ea4c1c5f6ba62c32 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 4 Jul 2023 17:53:38 +0300 Subject: [PATCH 81/85] Add vhost-user-sound crate Signed-off-by: Manos Pitsidianakis --- Cargo.lock | 494 ++++++++++++++++---- Cargo.toml | 1 + README.md | 1 + crates/sound/CHANGELOG.md | 3 + crates/sound/Cargo.toml | 34 ++ crates/sound/LICENSE-APACHE | 1 + crates/sound/LICENSE-BSD-3-Clause | 1 + crates/sound/README.md | 48 ++ crates/sound/rustfmt.toml | 7 + crates/sound/src/audio_backends.rs | 30 ++ crates/sound/src/audio_backends/null.rs | 27 ++ crates/sound/src/audio_backends/pipewire.rs | 28 ++ crates/sound/src/device.rs | 215 +++++++++ crates/sound/src/lib.rs | 119 +++++ crates/sound/src/main.rs | 67 +++ crates/sound/src/virtio_sound.rs | 344 ++++++++++++++ 16 files changed, 1334 insertions(+), 86 deletions(-) create mode 100644 crates/sound/CHANGELOG.md create mode 100644 crates/sound/Cargo.toml create mode 120000 crates/sound/LICENSE-APACHE create mode 120000 crates/sound/LICENSE-BSD-3-Clause create mode 100644 crates/sound/README.md create mode 100644 crates/sound/rustfmt.toml create mode 100644 crates/sound/src/audio_backends.rs create mode 100644 crates/sound/src/audio_backends/null.rs create mode 100644 crates/sound/src/audio_backends/pipewire.rs create mode 100644 crates/sound/src/device.rs create mode 100644 crates/sound/src/lib.rs create mode 100644 crates/sound/src/main.rs create mode 100644 crates/sound/src/virtio_sound.rs diff --git a/Cargo.lock b/Cargo.lock index d358e7e5..4a71d80f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + [[package]] name = "arc-swap" version = "1.6.0" @@ -79,13 +85,13 @@ checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "7b2d0f03b3640e3a630367e40c468cb7f309529c708ed1d88597047b0e7c6ef7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -122,6 +128,28 @@ dependencies = [ "which", ] +[[package]] +name = "bindgen" +version = "0.64.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 1.0.109", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -164,6 +192,16 @@ dependencies = [ "nom", ] +[[package]] +name = "cfg-expr" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "215c0072ecc28f92eeb0eea38ba63ddfcb65c2828c46311d646f1a3ff5f9841c" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -183,9 +221,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.8" +version = "4.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9394150f5b4273a1763355bd1c2ec54cc5a2593f790587bcd6b2c947cfa9211" +checksum = "384e169cc618c613d5e3ca6404dda77a8685a63e08660dcc64abaf7da7cb0c7a" dependencies = [ "clap_builder", "clap_derive", @@ -194,13 +232,12 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.8" +version = "4.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a78fbdd3cc2914ddf37ba444114bc7765bbdcb55ec9cbe6fa054f0137400717" +checksum = "ef137bbe35aab78bdb468ccfba75a5f4d8321ae011d34063770780545176af2d" dependencies = [ "anstream", "anstyle", - "bitflags 1.3.2", "clap_lex", "strsim", ] @@ -214,7 +251,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -244,10 +281,16 @@ dependencies = [ "rust-ini", "serde", "serde_json", - "toml", + "toml 0.5.11", "yaml-rust", ] +[[package]] +name = "cookie-factory" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b" + [[package]] name = "cpufeatures" version = "0.2.8" @@ -429,7 +472,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -521,9 +564,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 +612,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.2", "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" @@ -633,8 +675,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa282e1da652deaeed776f6ef36d443689aeda19e5c0a3a2335c50b4611ce489" dependencies = [ - "bindgen", - "system-deps", + "bindgen 0.63.0", + "system-deps 2.0.3", ] [[package]] @@ -647,6 +689,33 @@ dependencies = [ "winapi", ] +[[package]] +name = "libspa" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "667dfbb50c3d1f7ee1d33afdc04d1255923ece7642db3303046e7d63d997d77d" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cookie-factory", + "errno 0.3.1", + "libc", + "libspa-sys", + "nom", + "system-deps 6.1.1", +] + +[[package]] +name = "libspa-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5b88f52534df7ca88d451ae9628e22124e3cc5c60966465a7db479534c7a" +dependencies = [ + "bindgen 0.64.0", + "cc", + "system-deps 6.1.1", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -659,6 +728,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" @@ -681,12 +756,35 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", + "pin-utils", + "static_assertions", +] + [[package]] name = "nom" version = "7.1.3" @@ -809,7 +907,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -825,9 +923,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" @@ -835,6 +933,35 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pipewire" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc2180a4a84b855be86e6cd72fa6fd4318278871d2b1082e7cd05fe64b135ccb" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "errno 0.3.1", + "libc", + "libspa", + "libspa-sys", + "nix", + "once_cell", + "pipewire-sys", + "thiserror", +] + +[[package]] +name = "pipewire-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a95290eedb7fb6aa3922fdc0261cd0ddeb940abcdbdef28778928106554d2123" +dependencies = [ + "bindgen 0.64.0", + "libspa-sys", + "system-deps 6.1.1", +] + [[package]] name = "pkg-config" version = "0.3.27" @@ -859,18 +986,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", ] @@ -968,15 +1095,28 @@ dependencies = [ "errno 0.3.1", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.8", + "windows-sys", +] + +[[package]] +name = "rustix" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabcb0461ebd01d6b79945797c27f8529082226cb630a9865a71870ff63532a4" +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,35 +1126,44 @@ 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.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf3bf93142acad5821c99197022e170842cdbc1c30482b98750c688c640842a" +checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + [[package]] name = "serde_yaml" version = "0.9.22" @@ -1028,6 +1177,20 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "serial_test" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "538c30747ae860d6fb88330addbbd3e0ddbe46d662d032855596d8a8ca260611" +dependencies = [ + "dashmap", + "futures", + "lazy_static", + "log", + "parking_lot", + "serial_test_derive 1.0.0", +] + [[package]] name = "serial_test" version = "2.0.0" @@ -1039,7 +1202,18 @@ dependencies = [ "lazy_static", "log", "parking_lot", - "serial_test_derive", + "serial_test_derive 2.0.0", +] + +[[package]] +name = "serial_test_derive" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "079a83df15f85d89a68d64ae1238f142f172b1fa915d0d76b26a7cba1b659a69" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -1050,7 +1224,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -1085,6 +1259,12 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.10.0" @@ -1122,9 +1302,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", @@ -1142,10 +1322,29 @@ dependencies = [ "strum", "strum_macros", "thiserror", - "toml", - "version-compare", + "toml 0.5.11", + "version-compare 0.0.11", ] +[[package]] +name = "system-deps" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" +dependencies = [ + "cfg-expr", + "heck 0.4.1", + "pkg-config", + "toml 0.7.5", + "version-compare 0.1.1", +] + +[[package]] +name = "target-lexicon" +version = "0.12.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1c7f239eb94671427157bd93b3694320f3668d4e1eff08c7285366fd777fac" + [[package]] name = "tempfile" version = "3.6.0" @@ -1156,7 +1355,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall", - "rustix", + "rustix 0.37.22", "windows-sys", ] @@ -1186,7 +1385,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn 2.0.23", ] [[package]] @@ -1198,11 +1397,26 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebafdf5ad1220cb59e7d17cf4d2c72015297b75b19a10472f99b89225089240" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -1211,6 +1425,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -1229,9 +1445,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" @@ -1257,12 +1473,30 @@ version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + [[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9b791c5b0717a0558888a4cf7240cea836f39a99cb342e12ce633dcaa078072" +dependencies = [ + "bitflags 1.3.2", + "libc", + "vm-memory 0.10.0", + "vmm-sys-util", +] + [[package]] name = "vhost" version = "0.7.0" @@ -1271,7 +1505,7 @@ checksum = "84f81f436bca4541f4d33172e1202882c9d437db34ed17fc6d84c8ff2bde21f5" dependencies = [ "bitflags 1.3.2", "libc", - "vm-memory", + "vm-memory 0.11.0", "vmm-sys-util", ] @@ -1285,11 +1519,11 @@ dependencies = [ "libgpiod", "log", "thiserror", - "vhost", - "vhost-user-backend", - "virtio-bindings", - "virtio-queue", - "vm-memory", + "vhost 0.7.0", + "vhost-user-backend 0.9.0", + "virtio-bindings 0.2.1", + "virtio-queue 0.8.0", + "vm-memory 0.11.0", "vmm-sys-util", ] @@ -1302,11 +1536,11 @@ dependencies = [ "libc", "log", "thiserror", - "vhost", - "vhost-user-backend", - "virtio-bindings", - "virtio-queue", - "vm-memory", + "vhost 0.7.0", + "vhost-user-backend 0.9.0", + "virtio-bindings 0.2.1", + "virtio-queue 0.8.0", + "vm-memory 0.11.0", "vmm-sys-util", ] @@ -1322,11 +1556,11 @@ dependencies = [ "rand", "tempfile", "thiserror", - "vhost", - "vhost-user-backend", - "virtio-bindings", - "virtio-queue", - "vm-memory", + "vhost 0.7.0", + "vhost-user-backend 0.9.0", + "virtio-bindings 0.2.1", + "virtio-queue 0.8.0", + "vm-memory 0.11.0", "vmm-sys-util", ] @@ -1341,11 +1575,26 @@ dependencies = [ "num_enum", "tempfile", "thiserror", - "vhost", - "vhost-user-backend", - "virtio-bindings", - "virtio-queue", - "vm-memory", + "vhost 0.7.0", + "vhost-user-backend 0.9.0", + "virtio-bindings 0.2.1", + "virtio-queue 0.8.0", + "vm-memory 0.11.0", + "vmm-sys-util", +] + +[[package]] +name = "vhost-user-backend" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f237b91db4ac339d639fb43398b52d785fa51e3c7760ac9425148863c1f4303" +dependencies = [ + "libc", + "log", + "vhost 0.6.0", + "virtio-bindings 0.1.0", + "virtio-queue 0.7.1", + "vm-memory 0.10.0", "vmm-sys-util", ] @@ -1357,10 +1606,31 @@ checksum = "a5d3b7affe04f61d19b03c5db823287855789b687218fec139699a0c7f7f2790" dependencies = [ "libc", "log", - "vhost", - "virtio-bindings", - "virtio-queue", - "vm-memory", + "vhost 0.7.0", + "virtio-bindings 0.2.1", + "virtio-queue 0.8.0", + "vm-memory 0.11.0", + "vmm-sys-util", +] + +[[package]] +name = "vhost-user-sound" +version = "0.1.0" +dependencies = [ + "bindgen 0.64.0", + "clap", + "env_logger", + "libspa", + "libspa-sys", + "log", + "pipewire", + "pipewire-sys", + "serial_test 1.0.0", + "thiserror", + "vhost 0.6.0", + "vhost-user-backend 0.8.0", + "virtio-bindings 0.2.1", + "vm-memory 0.10.0", "vmm-sys-util", ] @@ -1377,23 +1647,41 @@ dependencies = [ "log", "serde", "serde_yaml", - "serial_test", + "serial_test 2.0.0", "thiserror", - "vhost", - "vhost-user-backend", - "virtio-bindings", - "virtio-queue", + "vhost 0.7.0", + "vhost-user-backend 0.9.0", + "virtio-bindings 0.2.1", + "virtio-queue 0.8.0", "virtio-vsock", - "vm-memory", + "vm-memory 0.11.0", "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.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c18d7b74098a946470ea265b5bacbbf877abc3373021388454de0d47735a5b98" +[[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" @@ -1401,20 +1689,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91aebb1df33db33cbf04d4c2445e4f78d0b0c8e65acfd16a4ee95ef63ca252f8" dependencies = [ "log", - "virtio-bindings", - "vm-memory", + "virtio-bindings 0.2.1", + "vm-memory 0.11.0", + "vmm-sys-util", +] + +[[package]] +name = "virtio-queue" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35aca00da06841bd99162c381ec65893cace23ca0fb89254302cfe4bec4c300f" +dependencies = [ + "log", + "virtio-bindings 0.2.1", + "vm-memory 0.12.0", "vmm-sys-util", ] [[package]] name = "virtio-vsock" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d1d0c0db339e03dc275e86e5de2654ed94b351f02d405a3a0260dfc1b839f" +dependencies = [ + "virtio-bindings 0.2.1", + "virtio-queue 0.9.0", + "vm-memory 0.12.0", +] + +[[package]] +name = "vm-memory" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb198c4dd87bf0b4f6b5d8cb41284fca13763a5a1a7e5b8a7ccce45e46d4cf73" +checksum = "688a70366615b45575a424d9c665561c1b5ab2224d494f706b6a6812911a827c" dependencies = [ - "virtio-bindings", - "virtio-queue", - "vm-memory", + "arc-swap", + "libc", + "winapi", ] [[package]] @@ -1428,6 +1739,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "vm-memory" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77c7a0891cbac53618f5f6eec650ed1dc4f7e506bbe14877aff49d94b8408b0" +dependencies = [ + "libc", + "thiserror", + "winapi", +] + [[package]] name = "vmm-sys-util" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index 633fa4d3..b5e90fcf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,5 +5,6 @@ members = [ "crates/i2c", "crates/rng", "crates/scsi", + "crates/sound", "crates/vsock", ] diff --git a/README.md b/README.md index 018cc197..9057b2ea 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) +- [Sound](https://github.com/rust-vmm/vhost-device/blob/main/crates/sound/README.md) - [VSOCK](https://github.com/rust-vmm/vhost-device/blob/main/crates/vsock/README.md) ## Testing and Code Coverage diff --git a/crates/sound/CHANGELOG.md b/crates/sound/CHANGELOG.md new file mode 100644 index 00000000..d471959d --- /dev/null +++ b/crates/sound/CHANGELOG.md @@ -0,0 +1,3 @@ +# Upcoming Release + +- First initial daemon implementation. diff --git a/crates/sound/Cargo.toml b/crates/sound/Cargo.toml new file mode 100644 index 00000000..6b6af768 --- /dev/null +++ b/crates/sound/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "vhost-user-sound" +version = "0.1.0" +authors = ["Manos Pitsidianakis "] +description = "A virtio-sound device using the vhost-user protocol." +repository = "https://github.com/rust-vmm/vhost-device" +readme = "README.md" +keywords = ["vhost", "sound", "virtio-sound", "virtio-snd", "vhost-user", "virtio"] +license = "Apache-2.0 OR BSD-3-Clause" +edition = "2018" + +[features] +default = ["null-backend"] +null-backend = [] +pw-backend = ["pipewire", "libspa", "pipewire-sys", "libspa-sys", "bindgen"] + +[dependencies] +bindgen = { version = "0.64.0", optional = true } +clap = { version = "4.1", features = ["derive"] } +env_logger = "0.10" +libspa = { version = "0.6.0", optional = true } +libspa-sys = { version = "0.6.0", optional = true } +log = "0.4" +pipewire = { version = "0.6.0", optional = true } +pipewire-sys = { version = "0.6.0", optional = true } +thiserror = "1.0" +vhost = { version = "0.6", features = ["vhost-user-slave"] } +vhost-user-backend = "0.8" +virtio-bindings = "0.2" +vm-memory = "0.10" +vmm-sys-util = "0.11" + +[dev-dependencies] +serial_test = "1.0" diff --git a/crates/sound/LICENSE-APACHE b/crates/sound/LICENSE-APACHE new file mode 120000 index 00000000..1cd601d0 --- /dev/null +++ b/crates/sound/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/crates/sound/LICENSE-BSD-3-Clause b/crates/sound/LICENSE-BSD-3-Clause new file mode 120000 index 00000000..a60f1af6 --- /dev/null +++ b/crates/sound/LICENSE-BSD-3-Clause @@ -0,0 +1 @@ +../../LICENSE-BSD-3-Clause \ No newline at end of file diff --git a/crates/sound/README.md b/crates/sound/README.md new file mode 100644 index 00000000..faea312e --- /dev/null +++ b/crates/sound/README.md @@ -0,0 +1,48 @@ +# vhost-user-sound + + +## Synopsis + vhost-user-sound --socket --backend + +## Description + A virtio-sound device using the vhost-user protocol. + +## Options + +```text + --socket + vhost-user Unix domain socket path + + --backend + audio backend to be used (supported: null) + + -h, --help + Print help + + -V, --version + Print version +``` + +## Examples + +Launch the backend on the host machine: + +```shell +host# vhost-user-sound --socket /tmp/snd.sock --backend null +``` + +With QEMU, you can add a `virtio` device that uses the backend's socket with the following flags: + +```text +-chardev socket,id=vsnd,path=/tmp/snd.sock \ +-device vhost-user-snd-pci,chardev=vsnd,id=snd +``` + +## 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/sound/rustfmt.toml b/crates/sound/rustfmt.toml new file mode 100644 index 00000000..c6f0942d --- /dev/null +++ b/crates/sound/rustfmt.toml @@ -0,0 +1,7 @@ +edition = "2018" +format_generated_files = false +format_code_in_doc_comments = true +format_strings = true +imports_granularity = "Crate" +group_imports = "StdExternalCrate" +wrap_comments = true diff --git a/crates/sound/src/audio_backends.rs b/crates/sound/src/audio_backends.rs new file mode 100644 index 00000000..c8ad6331 --- /dev/null +++ b/crates/sound/src/audio_backends.rs @@ -0,0 +1,30 @@ +// Manos Pitsidianakis +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +#[cfg(feature = "null-backend")] +mod null; + +#[cfg(feature = "pw-backend")] +mod pipewire; + +#[cfg(feature = "null-backend")] +use self::null::NullBackend; +#[cfg(feature = "pw-backend")] +use self::pipewire::PwBackend; +use crate::{Error, Result, SoundRequest}; + +pub trait AudioBackend { + fn write(&self, req: &SoundRequest) -> Result<()>; + + fn read(&self, req: &mut SoundRequest) -> Result<()>; +} + +pub fn alloc_audio_backend(name: String) -> Result> { + match name.as_str() { + #[cfg(feature = "null-backend")] + "null" => Ok(Box::new(NullBackend::new())), + #[cfg(feature = "pw-backend")] + "pipewire" => Ok(Box::new(PwBackend::new())), + _ => Err(Error::AudioBackendNotSupported), + } +} diff --git a/crates/sound/src/audio_backends/null.rs b/crates/sound/src/audio_backends/null.rs new file mode 100644 index 00000000..e12c36d3 --- /dev/null +++ b/crates/sound/src/audio_backends/null.rs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use super::AudioBackend; +use crate::{Error, Result, SoundRequest}; + +pub struct NullBackend {} + +impl NullBackend { + pub fn new() -> Self { + NullBackend {} + } +} + +impl AudioBackend for NullBackend { + fn write(&self, _req: &SoundRequest) -> Result<()> { + Ok(()) + } + + fn read(&self, req: &mut SoundRequest) -> Result<()> { + let buf = req.data_slice().ok_or(Error::SoundReqMissingData)?; + let zero_mem = vec![0u8; buf.len()]; + + buf.copy_from(&zero_mem); + + Ok(()) + } +} diff --git a/crates/sound/src/audio_backends/pipewire.rs b/crates/sound/src/audio_backends/pipewire.rs new file mode 100644 index 00000000..a9393653 --- /dev/null +++ b/crates/sound/src/audio_backends/pipewire.rs @@ -0,0 +1,28 @@ +// Pipewire backend device +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use super::AudioBackend; +use crate::{Error, Result, SoundRequest}; + +pub struct PwBackend {} + +impl PwBackend { + pub fn new() -> Self { + PwBackend {} + } +} + +impl AudioBackend for PwBackend { + fn write(&self, _req: &SoundRequest) -> Result<()> { + Ok(()) + } + + fn read(&self, req: &mut SoundRequest) -> Result<()> { + let buf = req.data_slice().ok_or(Error::SoundReqMissingData)?; + let zero_mem = vec![0u8; buf.len()]; + + buf.copy_from(&zero_mem); + + Ok(()) + } +} diff --git a/crates/sound/src/device.rs b/crates/sound/src/device.rs new file mode 100644 index 00000000..9313dc91 --- /dev/null +++ b/crates/sound/src/device.rs @@ -0,0 +1,215 @@ +// Manos Pitsidianakis +// Stefano Garzarella +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use std::{io::Result as IoResult, sync::RwLock, u16, u32, u64, u8}; + +use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures}; +use vhost_user_backend::{VhostUserBackend, VringRwLock}; +use virtio_bindings::bindings::{ + virtio_config::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1}, + virtio_ring::{VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC}, +}; +use vm_memory::{ByteValued, GuestMemoryAtomic, GuestMemoryMmap}; +use vmm_sys_util::{ + epoll::EventSet, + eventfd::{EventFd, EFD_NONBLOCK}, +}; + +use crate::{ + audio_backends::{alloc_audio_backend, AudioBackend}, + virtio_sound::*, + Error, Result, SoundConfig, +}; + +struct VhostUserSoundThread { + mem: Option>, + event_idx: bool, + queue_indexes: Vec, +} + +impl VhostUserSoundThread { + pub fn new(mut queue_indexes: Vec) -> Result { + queue_indexes.sort(); + + Ok(VhostUserSoundThread { + event_idx: false, + mem: None, + queue_indexes, + }) + } + + fn queues_per_thread(&self) -> u64 { + let mut queues_per_thread = 0u64; + + for idx in self.queue_indexes.iter() { + queues_per_thread |= 1u64 << idx + } + + queues_per_thread + } + + fn set_event_idx(&mut self, enabled: bool) { + self.event_idx = enabled; + } + + fn update_memory(&mut self, mem: GuestMemoryAtomic) -> IoResult<()> { + self.mem = Some(mem); + Ok(()) + } + + fn handle_event(&self, device_event: u16, vrings: &[VringRwLock]) -> IoResult { + let vring = &vrings[device_event as usize]; + let queue_idx = self.queue_indexes[device_event as usize]; + + match queue_idx { + CONTROL_QUEUE_IDX => self.process_control(vring), + EVENT_QUEUE_IDX => self.process_event(vring), + TX_QUEUE_IDX => self.process_tx(vring), + RX_QUEUE_IDX => self.process_rx(vring), + _ => Err(Error::HandleUnknownEvent.into()), + } + } + + fn process_control(&self, _vring: &VringRwLock) -> IoResult { + Ok(false) + } + + fn process_event(&self, _vring: &VringRwLock) -> IoResult { + Ok(false) + } + + fn process_tx(&self, _vring: &VringRwLock) -> IoResult { + Ok(false) + } + + fn process_rx(&self, _vring: &VringRwLock) -> IoResult { + Ok(false) + } +} + +pub struct VhostUserSoundBackend { + threads: Vec>, + virtio_cfg: VirtioSoundConfig, + exit_event: EventFd, + _audio_backend: RwLock>, +} + +impl VhostUserSoundBackend { + pub fn new(config: SoundConfig) -> Result { + let threads = if config.multi_thread { + vec![ + RwLock::new(VhostUserSoundThread::new(vec![ + CONTROL_QUEUE_IDX, + EVENT_QUEUE_IDX, + ])?), + RwLock::new(VhostUserSoundThread::new(vec![TX_QUEUE_IDX])?), + RwLock::new(VhostUserSoundThread::new(vec![RX_QUEUE_IDX])?), + ] + } else { + vec![RwLock::new(VhostUserSoundThread::new(vec![ + CONTROL_QUEUE_IDX, + EVENT_QUEUE_IDX, + TX_QUEUE_IDX, + RX_QUEUE_IDX, + ])?)] + }; + + let audio_backend = alloc_audio_backend(config.audio_backend_name)?; + + Ok(Self { + threads, + virtio_cfg: VirtioSoundConfig { + jacks: 0.into(), + streams: 1.into(), + chmaps: 0.into(), + }, + exit_event: EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?, + _audio_backend: RwLock::new(audio_backend), + }) + } + + pub fn send_exit_event(&self) { + self.exit_event.write(1).unwrap(); + } +} + +impl VhostUserBackend for VhostUserSoundBackend { + fn num_queues(&self) -> usize { + NUM_QUEUES as usize + } + + fn max_queue_size(&self) -> usize { + 256 + } + + fn features(&self) -> u64 { + 1 << VIRTIO_F_VERSION_1 + | 1 << VIRTIO_F_NOTIFY_ON_EMPTY + | 1 << VIRTIO_RING_F_INDIRECT_DESC + | 1 << VIRTIO_RING_F_EVENT_IDX + | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits() + } + + fn protocol_features(&self) -> VhostUserProtocolFeatures { + VhostUserProtocolFeatures::CONFIG + } + + fn set_event_idx(&self, enabled: bool) { + for thread in self.threads.iter() { + thread.write().unwrap().set_event_idx(enabled); + } + } + + fn update_memory(&self, mem: GuestMemoryAtomic) -> IoResult<()> { + for thread in self.threads.iter() { + thread.write().unwrap().update_memory(mem.clone())?; + } + + Ok(()) + } + + fn handle_event( + &self, + device_event: u16, + evset: EventSet, + vrings: &[VringRwLock], + thread_id: usize, + ) -> IoResult { + if evset != EventSet::IN { + return Err(Error::HandleEventNotEpollIn.into()); + } + + self.threads[thread_id] + .read() + .unwrap() + .handle_event(device_event, vrings) + } + + fn get_config(&self, offset: u32, size: u32) -> Vec { + let offset = offset as usize; + let size = size as usize; + + let buf = self.virtio_cfg.as_slice(); + + if offset + size > buf.len() { + return Vec::new(); + } + + buf[offset..offset + size].to_vec() + } + + fn queues_per_thread(&self) -> Vec { + let mut vec = Vec::with_capacity(self.threads.len()); + + for thread in self.threads.iter() { + vec.push(thread.read().unwrap().queues_per_thread()) + } + + vec + } + + fn exit_event(&self, _thread_index: usize) -> Option { + self.exit_event.try_clone().ok() + } +} diff --git a/crates/sound/src/lib.rs b/crates/sound/src/lib.rs new file mode 100644 index 00000000..a6f7989d --- /dev/null +++ b/crates/sound/src/lib.rs @@ -0,0 +1,119 @@ +// Manos Pitsidianakis +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +pub mod audio_backends; +pub mod device; +pub mod virtio_sound; + +use std::{ + io::{Error as IoError, ErrorKind}, + sync::Arc, +}; + +use log::{info, warn}; +use thiserror::Error as ThisError; +use vhost::{vhost_user, vhost_user::Listener}; +use vhost_user_backend::VhostUserDaemon; +use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap, VolatileSlice}; + +use crate::device::VhostUserSoundBackend; + +pub type Result = std::result::Result; + +/// Custom error types +#[derive(Debug, ThisError)] +pub enum Error { + #[error("Failed to handle event other than EPOLLIN event")] + HandleEventNotEpollIn, + #[error("Failed to handle unknown event")] + HandleUnknownEvent, + #[error("Failed to create a new EventFd")] + EventFdCreate(IoError), + #[error("Request missing data buffer")] + SoundReqMissingData, + #[error("Audio backend not supported")] + AudioBackendNotSupported, +} + +impl std::convert::From for IoError { + fn from(e: Error) -> Self { + IoError::new(ErrorKind::Other, e) + } +} + +#[derive(Debug, Clone)] +/// This structure is the public API through which an external program +/// is allowed to configure the backend. +pub struct SoundConfig { + /// vhost-user Unix domain socket + socket: String, + /// use multiple threads to hanlde the virtqueues + multi_thread: bool, + /// audio backend name + audio_backend_name: String, +} + +impl SoundConfig { + /// Create a new instance of the SoundConfig struct, containing the + /// parameters to be fed into the sound-backend server. + pub fn new(socket: String, multi_thread: bool, audio_backend_name: String) -> Self { + Self { + socket, + multi_thread, + audio_backend_name, + } + } + + /// Return the path of the unix domain socket which is listening to + /// requests from the guest. + pub fn get_socket_path(&self) -> String { + String::from(&self.socket) + } +} + +pub type SoundBitmap = (); + +#[derive(Debug)] +pub struct SoundRequest<'a> { + data_slice: Option>, +} + +impl<'a> SoundRequest<'a> { + pub fn data_slice(&self) -> Option<&VolatileSlice<'a, SoundBitmap>> { + self.data_slice.as_ref() + } +} + +/// This is the public API through which an external program starts the +/// vhost-user-sound backend server. +pub fn start_backend_server(config: SoundConfig) { + let listener = Listener::new(config.get_socket_path(), true).unwrap(); + let backend = Arc::new(VhostUserSoundBackend::new(config).unwrap()); + + let mut daemon = VhostUserDaemon::new( + String::from("vhost-user-sound"), + 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.send_exit_event(); +} diff --git a/crates/sound/src/main.rs b/crates/sound/src/main.rs new file mode 100644 index 00000000..11bed61b --- /dev/null +++ b/crates/sound/src/main.rs @@ -0,0 +1,67 @@ +// Manos Pitsidianakis +// Stefano Garzarella +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause +use std::convert::TryFrom; + +use clap::Parser; +use vhost_user_sound::{start_backend_server, Error, Result, SoundConfig}; + +#[derive(Parser, Debug)] +#[clap(version, about, long_about = None)] +struct SoundArgs { + /// vhost-user Unix domain socket path. + #[clap(long)] + socket: String, + /// audio backend to be used (supported: null) + #[clap(long)] + backend: String, +} + +impl TryFrom for SoundConfig { + type Error = Error; + + fn try_from(cmd_args: SoundArgs) -> Result { + let socket = cmd_args.socket.trim().to_string(); + let backend = cmd_args.backend.trim().to_string(); + + Ok(SoundConfig::new(socket, false, backend)) + } +} + +fn main() { + env_logger::init(); + + let config = SoundConfig::try_from(SoundArgs::parse()).unwrap(); + + loop { + start_backend_server(config.clone()); + } +} + +#[cfg(test)] +mod tests { + use serial_test::serial; + + use super::*; + + impl SoundArgs { + fn from_args(socket: &str) -> Self { + SoundArgs { + socket: socket.to_string(), + backend: "null".to_string(), + } + } + } + + #[test] + #[serial] + fn test_vsock_config_setup() { + let args = SoundArgs::from_args("/tmp/vhost-sound.socket"); + + let config = SoundConfig::try_from(args); + assert!(config.is_ok()); + + let config = config.unwrap(); + assert_eq!(config.get_socket_path(), "/tmp/vhost-sound.socket"); + } +} diff --git a/crates/sound/src/virtio_sound.rs b/crates/sound/src/virtio_sound.rs new file mode 100644 index 00000000..075861da --- /dev/null +++ b/crates/sound/src/virtio_sound.rs @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause +use vm_memory::{ByteValued, Le32, Le64}; + +// virtqueues + +pub const CONTROL_QUEUE_IDX: u16 = 0; +pub const EVENT_QUEUE_IDX: u16 = 1; +pub const TX_QUEUE_IDX: u16 = 2; +pub const RX_QUEUE_IDX: u16 = 3; +pub const NUM_QUEUES: u16 = 4; + +// jack control request types + +pub const VIRTIO_SND_R_JACK_INFO: u32 = 1; +pub const VIRTIO_SND_R_JACK_REMAP: u32 = 2; + +// PCM control request types + +pub const VIRTIO_SND_R_PCM_INFO: u32 = 0x0100; +pub const VIRTIO_SND_R_PCM_SET_PARAMS: u32 = 0x0101; +pub const VIRTIO_SND_R_PCM_PREPARE: u32 = 0x0102; +pub const VIRTIO_SND_R_PCM_RELEASE: u32 = 0x0103; +pub const VIRTIO_SND_R_PCM_START: u32 = 0x0104; +pub const VIRTIO_SND_R_PCM_STOP: u32 = 0x0105; + +// channel map control request types + +pub const VIRTIO_SND_R_CHMAP_INFO: u32 = 0x0200; + +// jack event types + +pub const VIRTIO_SND_EVT_JACK_CONNECTED: u32 = 0x1000; +pub const VIRTIO_SND_EVT_JACK_DISCONNECTED: u32 = 0x1001; + +// PCM event types + +pub const VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED: u32 = 0x1100; +pub const VIRTIO_SND_EVT_PCM_XRUN: u32 = 0x1101; + +// common status codes + +pub const VIRTIO_SND_S_OK: u32 = 0x8000; +pub const VIRTIO_SND_S_BAD_MSG: u32 = 0x8001; +pub const VIRTIO_SND_S_NOT_SUPP: u32 = 0x8002; +pub const VIRTIO_SND_S_IO_ERR: u32 = 0x8003; + +// device data flow directions + +pub const VIRTIO_SND_D_OUTPUT: u32 = 0; +pub const VIRTIO_SND_D_INPUT: u32 = 1; + +// supported jack features + +pub const VIRTIO_SND_JACK_F_REMAP: u32 = 0; + +// supported PCM stream features + +pub const VIRTIO_SND_PCM_F_SHMEM_HOST: u8 = 0; +pub const VIRTIO_SND_PCM_F_SHMEM_GUEST: u8 = 1; +pub const VIRTIO_SND_PCM_F_MSG_POLLING: u8 = 2; +pub const VIRTIO_SND_PCM_F_EVT_SHMEM_PERIODS: u8 = 3; +pub const VIRTIO_SND_PCM_F_EVT_XRUNS: u8 = 4; + +// supported PCM sample formats + +pub const VIRTIO_SND_PCM_FMT_IMA_ADPCM: u8 = 0; +pub const VIRTIO_SND_PCM_FMT_MU_LAW: u8 = 1; +pub const VIRTIO_SND_PCM_FMT_A_LAW: u8 = 2; +pub const VIRTIO_SND_PCM_FMT_S8: u8 = 3; +pub const VIRTIO_SND_PCM_FMT_U8: u8 = 4; +pub const VIRTIO_SND_PCM_FMT_S16: u8 = 5; +pub const VIRTIO_SND_PCM_FMT_U16: u8 = 6; +pub const VIRTIO_SND_PCM_FMT_S18_3: u8 = 7; +pub const VIRTIO_SND_PCM_FMT_U18_3: u8 = 8; +pub const VIRTIO_SND_PCM_FMT_S20_3: u8 = 9; +pub const VIRTIO_SND_PCM_FMT_U20_3: u8 = 10; +pub const VIRTIO_SND_PCM_FMT_S24_3: u8 = 11; +pub const VIRTIO_SND_PCM_FMT_U24_3: u8 = 12; +pub const VIRTIO_SND_PCM_FMT_S20: u8 = 13; +pub const VIRTIO_SND_PCM_FMT_U20: u8 = 14; +pub const VIRTIO_SND_PCM_FMT_S24: u8 = 15; +pub const VIRTIO_SND_PCM_FMT_U24: u8 = 16; +pub const VIRTIO_SND_PCM_FMT_S32: u8 = 17; +pub const VIRTIO_SND_PCM_FMT_U32: u8 = 18; +pub const VIRTIO_SND_PCM_FMT_FLOAT: u8 = 19; +pub const VIRTIO_SND_PCM_FMT_FLOAT64: u8 = 20; +// digital formats (width / physical width) +pub const VIRTIO_SND_PCM_FMT_DSD_U8: u8 = 21; +pub const VIRTIO_SND_PCM_FMT_DSD_U16: u8 = 22; +pub const VIRTIO_SND_PCM_FMT_DSD_U32: u8 = 23; +pub const VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME: u8 = 24; + +// supported PCM frame rates + +pub const VIRTIO_SND_PCM_RATE_5512: u8 = 0; +pub const VIRTIO_SND_PCM_RATE_8000: u8 = 1; +pub const VIRTIO_SND_PCM_RATE_11025: u8 = 2; +pub const VIRTIO_SND_PCM_RATE_16000: u8 = 3; +pub const VIRTIO_SND_PCM_RATE_22050: u8 = 4; +pub const VIRTIO_SND_PCM_RATE_32000: u8 = 5; +pub const VIRTIO_SND_PCM_RATE_44100: u8 = 6; +pub const VIRTIO_SND_PCM_RATE_48000: u8 = 7; +pub const VIRTIO_SND_PCM_RATE_64000: u8 = 8; +pub const VIRTIO_SND_PCM_RATE_88200: u8 = 9; +pub const VIRTIO_SND_PCM_RATE_96000: u8 = 10; +pub const VIRTIO_SND_PCM_RATE_176400: u8 = 11; +pub const VIRTIO_SND_PCM_RATE_192000: u8 = 12; +pub const VIRTIO_SND_PCM_RATE_384000: u8 = 13; + +// standard channel position definition + +pub const VIRTIO_SND_CHMAP_NONE: u8 = 0; /* undefined */ +pub const VIRTIO_SND_CHMAP_NA: u8 = 1; /* silent */ +pub const VIRTIO_SND_CHMAP_MONO: u8 = 2; /* mono stream */ +pub const VIRTIO_SND_CHMAP_FL: u8 = 3; /* front left */ +pub const VIRTIO_SND_CHMAP_FR: u8 = 4; /* front right */ +pub const VIRTIO_SND_CHMAP_RL: u8 = 5; /* rear left */ +pub const VIRTIO_SND_CHMAP_RR: u8 = 6; /* rear right */ +pub const VIRTIO_SND_CHMAP_FC: u8 = 7; /* front center */ +pub const VIRTIO_SND_CHMAP_LFE: u8 = 8; /* low frequency (LFE) */ +pub const VIRTIO_SND_CHMAP_SL: u8 = 9; /* side left */ +pub const VIRTIO_SND_CHMAP_SR: u8 = 10; /* side right */ +pub const VIRTIO_SND_CHMAP_RC: u8 = 11; /* rear center */ +pub const VIRTIO_SND_CHMAP_FLC: u8 = 12; /* front left center */ +pub const VIRTIO_SND_CHMAP_FRC: u8 = 13; /* front right center */ +pub const VIRTIO_SND_CHMAP_RLC: u8 = 14; /* rear left center */ +pub const VIRTIO_SND_CHMAP_RRC: u8 = 15; /* rear right center */ +pub const VIRTIO_SND_CHMAP_FLW: u8 = 16; /* front left wide */ +pub const VIRTIO_SND_CHMAP_FRW: u8 = 17; /* front right wide */ +pub const VIRTIO_SND_CHMAP_FLH: u8 = 18; /* front left high */ +pub const VIRTIO_SND_CHMAP_FCH: u8 = 19; /* front center high */ +pub const VIRTIO_SND_CHMAP_FRH: u8 = 20; /* front right high */ +pub const VIRTIO_SND_CHMAP_TC: u8 = 21; /* top center */ +pub const VIRTIO_SND_CHMAP_TFL: u8 = 22; /* top front left */ +pub const VIRTIO_SND_CHMAP_TFR: u8 = 23; /* top front right */ +pub const VIRTIO_SND_CHMAP_TFC: u8 = 24; /* top front center */ +pub const VIRTIO_SND_CHMAP_TRL: u8 = 25; /* top rear left */ +pub const VIRTIO_SND_CHMAP_TRR: u8 = 26; /* top rear right */ +pub const VIRTIO_SND_CHMAP_TRC: u8 = 27; /* top rear center */ +pub const VIRTIO_SND_CHMAP_TFLC: u8 = 28; /* top front left center */ +pub const VIRTIO_SND_CHMAP_TFRC: u8 = 29; /* top front right center */ +pub const VIRTIO_SND_CHMAP_TSL: u8 = 34; /* top side left */ +pub const VIRTIO_SND_CHMAP_TSR: u8 = 35; /* top side right */ +pub const VIRTIO_SND_CHMAP_LLFE: u8 = 36; /* left LFE */ +pub const VIRTIO_SND_CHMAP_RLFE: u8 = 37; /* right LFE */ +pub const VIRTIO_SND_CHMAP_BC: u8 = 38; /* bottom center */ +pub const VIRTIO_SND_CHMAP_BLC: u8 = 39; /* bottom left center */ +pub const VIRTIO_SND_CHMAP_BRC: u8 = 40; /* bottom right center */ +// maximum possible number of channels +pub const VIRTIO_SND_CHMAP_MAX_SIZE: usize = 18; + +/// Virtio Sound Configuration +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSoundConfig { + /// total number of all available jacks + pub jacks: Le32, + /// total number of all available PCM streams + pub streams: Le32, + /// total number of all available channel maps + pub chmaps: Le32, +} + +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSoundConfig {} + +/// Virtio Sound Request / Response common header +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSoundHeader { + /// request type / response status + pub code: Le32, +} +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSoundHeader {} + +/// Virtio Sound event notification +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSoundEvent { + /// PCM stream event type + pub hdr: VirtioSoundHeader, + /// PCM stream identifier from 0 to streams - 1 + pub data: Le32, +} +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSoundEvent {} + +/// Virtio Sound request information about any kind of configuration item +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSoundQueryInfo { + /// item request type (VIRTIO_SND_R_*_INFO) + pub hdr: VirtioSoundHeader, + /// starting identifier for the item + pub start_id: Le32, + /// number of items for which information is requested + pub count: Le32, + /// size of the structure containing information for one item + pub size: Le32, +} +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSoundQueryInfo {} + +/// Virtio Sound response common information header +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSoundInfo { + /// function group node identifier + pub hda_fn_nid: Le32, +} +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSoundInfo {} + +/// Jack control request / Jack common header +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSoundJackHeader { + /// jack request type (VIRTIO_SND_R_JACK_*) + pub hdr: VirtioSoundHeader, + /// jack identifier + pub jack_id: Le32, +} +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSoundJackHeader {} + +/// Jack response information about available jacks +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSoundJackInfo { + /// jack response header type + pub hdr: VirtioSoundInfo, + /// supported feature bit map (VIRTIO_SND_JACK_F_XXX) + pub feature: Le32, + /// pin default configuration value + pub hda_reg_defconf: Le32, + /// pin capabilities value + pub hda_reg_caps: Le32, + /// current jack connection status + pub connected: u8, + pub padding: [u8; 7], +} +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSoundJackInfo {} + +///If the VIRTIO_SND_JACK_F_REMAP feature bit is set in the jack information +/// Remap control request +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSoundJackRemap { + pub hdr: VirtioSoundJackHeader, /* .code = VIRTIO_SND_R_JACK_REMAP */ + pub association: Le32, + pub sequence: Le32, +} +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSoundJackRemap {} + +/// PCM control request / PCM common header +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSoundPcmHeader { + pub hdr: VirtioSoundHeader, + pub stream_id: Le32, +} +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSoundPcmHeader {} + +/// PCM response information +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSoundPcmInfo { + pub hdr: VirtioSoundInfo, + pub features: Le32, /* 1 << VIRTIO_SND_PCM_F_XXX */ + pub formats: Le64, /* 1 << VIRTIO_SND_PCM_FMT_XXX */ + pub rates: Le64, /* 1 << VIRTIO_SND_PCM_RATE_XXX */ + pub direction: u8, + pub channels_min: u8, + pub channels_max: u8, + + pub padding: [u8; 5], +} +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSoundPcmInfo {} + +/// Set selected stream parameters for the specified stream ID +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSndPcmSetParams { + pub hdr: VirtioSoundPcmHeader, + pub buffer_bytes: Le32, + pub period_bytes: Le32, + pub features: Le32, /* 1 << VIRTIO_SND_PCM_F_XXX */ + pub channels: u8, + pub format: u8, + pub rate: u8, + pub padding: u8, +} +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSndPcmSetParams {} + +/// PCM I/O header +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSoundPcmXfer { + pub stream_id: Le32, +} +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSoundPcmXfer {} + +/// PCM I/O status +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSoundPcmStatus { + pub status: Le32, + pub latency_bytes: Le32, +} +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSoundPcmStatus {} + +/// channel maps response information +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[repr(C)] +pub struct VirtioSoundChmapInfo { + pub hdr: VirtioSoundInfo, + pub direction: u8, + pub channels: u8, + pub positions: [u8; VIRTIO_SND_CHMAP_MAX_SIZE], +} +// SAFETY: The layout of the structure is fixed and can be initialized by +// reading its content from byte array. +unsafe impl ByteValued for VirtioSoundChmapInfo {} From 0d8bea4a9c1937eaf560e6def1fd82fa61da7332 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 4 Jul 2023 17:52:01 +0300 Subject: [PATCH 82/85] sound: add Stream, ControlMessage and other types Signed-off-by: Manos Pitsidianakis --- Cargo.lock | 1 + crates/sound/Cargo.toml | 1 + crates/sound/src/audio_backends.rs | 1 + crates/sound/src/audio_backends/null.rs | 2 + crates/sound/src/device.rs | 50 ++++- crates/sound/src/lib.rs | 163 +++++++++++++- crates/sound/src/stream.rs | 269 ++++++++++++++++++++++++ crates/sound/src/virtio_sound.rs | 4 +- 8 files changed, 478 insertions(+), 13 deletions(-) create mode 100644 crates/sound/src/stream.rs diff --git a/Cargo.lock b/Cargo.lock index 4a71d80f..a3999692 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1630,6 +1630,7 @@ dependencies = [ "vhost 0.6.0", "vhost-user-backend 0.8.0", "virtio-bindings 0.2.1", + "virtio-queue 0.7.1", "vm-memory 0.10.0", "vmm-sys-util", ] diff --git a/crates/sound/Cargo.toml b/crates/sound/Cargo.toml index 6b6af768..20e8b04f 100644 --- a/crates/sound/Cargo.toml +++ b/crates/sound/Cargo.toml @@ -27,6 +27,7 @@ thiserror = "1.0" vhost = { version = "0.6", features = ["vhost-user-slave"] } vhost-user-backend = "0.8" virtio-bindings = "0.2" +virtio-queue = "0.7" vm-memory = "0.10" vmm-sys-util = "0.11" diff --git a/crates/sound/src/audio_backends.rs b/crates/sound/src/audio_backends.rs index c8ad6331..86859e0a 100644 --- a/crates/sound/src/audio_backends.rs +++ b/crates/sound/src/audio_backends.rs @@ -20,6 +20,7 @@ pub trait AudioBackend { } pub fn alloc_audio_backend(name: String) -> Result> { + log::trace!("allocating audio backend {}", name); match name.as_str() { #[cfg(feature = "null-backend")] "null" => Ok(Box::new(NullBackend::new())), diff --git a/crates/sound/src/audio_backends/null.rs b/crates/sound/src/audio_backends/null.rs index e12c36d3..b81f582f 100644 --- a/crates/sound/src/audio_backends/null.rs +++ b/crates/sound/src/audio_backends/null.rs @@ -13,10 +13,12 @@ impl NullBackend { impl AudioBackend for NullBackend { fn write(&self, _req: &SoundRequest) -> Result<()> { + log::trace!("NullBackend writing {:?}", _req); Ok(()) } fn read(&self, req: &mut SoundRequest) -> Result<()> { + log::trace!("NullBackend reading {:?}", req); let buf = req.data_slice().ok_or(Error::SoundReqMissingData)?; let zero_mem = vec![0u8; buf.len()]; diff --git a/crates/sound/src/device.rs b/crates/sound/src/device.rs index 9313dc91..23d69813 100644 --- a/crates/sound/src/device.rs +++ b/crates/sound/src/device.rs @@ -5,12 +5,13 @@ use std::{io::Result as IoResult, sync::RwLock, u16, u32, u64, u8}; use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures}; -use vhost_user_backend::{VhostUserBackend, VringRwLock}; +use vhost_user_backend::{VhostUserBackend, VringRwLock, VringT}; use virtio_bindings::bindings::{ virtio_config::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1}, virtio_ring::{VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC}, }; -use vm_memory::{ByteValued, GuestMemoryAtomic, GuestMemoryMmap}; +use virtio_queue::QueueOwnedT; +use vm_memory::{ByteValued, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap}; use vmm_sys_util::{ epoll::EventSet, eventfd::{EventFd, EFD_NONBLOCK}, @@ -28,6 +29,17 @@ struct VhostUserSoundThread { queue_indexes: Vec, } +#[cfg(debug_assertions)] +const fn queue_idx_as_str(q: u16) -> &'static str { + match q { + CONTROL_QUEUE_IDX => stringify!(CONTROL_QUEUE_IDX), + EVENT_QUEUE_IDX => stringify!(EVENT_QUEUE_IDX), + TX_QUEUE_IDX => stringify!(TX_QUEUE_IDX), + RX_QUEUE_IDX => stringify!(RX_QUEUE_IDX), + _ => "unknown queue idx", + } +} + impl VhostUserSoundThread { pub fn new(mut queue_indexes: Vec) -> Result { queue_indexes.sort(); @@ -59,31 +71,49 @@ impl VhostUserSoundThread { } fn handle_event(&self, device_event: u16, vrings: &[VringRwLock]) -> IoResult { + log::trace!("handle_event device_event {}", device_event); + let vring = &vrings[device_event as usize]; let queue_idx = self.queue_indexes[device_event as usize]; + log::trace!( + "handle_event queue_idx {} == {}", + queue_idx, + queue_idx_as_str(queue_idx) + ); - match queue_idx { + dbg!(match queue_idx { CONTROL_QUEUE_IDX => self.process_control(vring), EVENT_QUEUE_IDX => self.process_event(vring), TX_QUEUE_IDX => self.process_tx(vring), RX_QUEUE_IDX => self.process_rx(vring), _ => Err(Error::HandleUnknownEvent.into()), - } + }) } fn process_control(&self, _vring: &VringRwLock) -> IoResult { - Ok(false) + let requests: Vec<_> = _vring + .get_mut() + .get_queue_mut() + .iter(self.mem.as_ref().unwrap().memory()) + .map_err(|_| Error::DescriptorNotFound)? + .collect(); + dbg!(&requests); + + Ok(true) } fn process_event(&self, _vring: &VringRwLock) -> IoResult { + log::trace!("process_event"); Ok(false) } fn process_tx(&self, _vring: &VringRwLock) -> IoResult { + log::trace!("process_tx"); Ok(false) } fn process_rx(&self, _vring: &VringRwLock) -> IoResult { + log::trace!("process_rx"); Ok(false) } } @@ -97,7 +127,8 @@ pub struct VhostUserSoundBackend { impl VhostUserSoundBackend { pub fn new(config: SoundConfig) -> Result { - let threads = if config.multi_thread { + log::trace!("VhostUserSoundBackend::new config {:?}", &config); + let threads = if dbg!(config.multi_thread) { vec![ RwLock::new(VhostUserSoundThread::new(vec![ CONTROL_QUEUE_IDX, @@ -152,16 +183,18 @@ impl VhostUserBackend for VhostUserSoundBackend { } fn protocol_features(&self) -> VhostUserProtocolFeatures { - VhostUserProtocolFeatures::CONFIG + VhostUserProtocolFeatures::CONFIG | VhostUserProtocolFeatures::MQ } fn set_event_idx(&self, enabled: bool) { + log::trace!("set_event_idx enabled {:?}", enabled); for thread in self.threads.iter() { thread.write().unwrap().set_event_idx(enabled); } } fn update_memory(&self, mem: GuestMemoryAtomic) -> IoResult<()> { + log::trace!("update_memory"); for thread in self.threads.iter() { thread.write().unwrap().update_memory(mem.clone())?; } @@ -176,6 +209,7 @@ impl VhostUserBackend for VhostUserSoundBackend { vrings: &[VringRwLock], thread_id: usize, ) -> IoResult { + log::trace!("handle_event device_event {}", device_event); if evset != EventSet::IN { return Err(Error::HandleEventNotEpollIn.into()); } @@ -187,6 +221,7 @@ impl VhostUserBackend for VhostUserSoundBackend { } fn get_config(&self, offset: u32, size: u32) -> Vec { + log::trace!("get_config offset {} size {}", offset, size); let offset = offset as usize; let size = size as usize; @@ -210,6 +245,7 @@ impl VhostUserBackend for VhostUserSoundBackend { } fn exit_event(&self, _thread_index: usize) -> Option { + log::trace!("exit_event"); self.exit_event.try_clone().ok() } } diff --git a/crates/sound/src/lib.rs b/crates/sound/src/lib.rs index a6f7989d..ed9a3d05 100644 --- a/crates/sound/src/lib.rs +++ b/crates/sound/src/lib.rs @@ -3,41 +3,194 @@ pub mod audio_backends; pub mod device; +pub mod stream; pub mod virtio_sound; use std::{ + convert::TryFrom, io::{Error as IoError, ErrorKind}, sync::Arc, }; use log::{info, warn}; +pub use stream::Stream; use thiserror::Error as ThisError; use vhost::{vhost_user, vhost_user::Listener}; -use vhost_user_backend::VhostUserDaemon; -use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap, VolatileSlice}; +use vhost_user_backend::{VhostUserDaemon, VringRwLock, VringT}; +use virtio_sound::*; +use vm_memory::{ + ByteValued, Bytes, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap, Le32, + VolatileSlice, +}; use crate::device::VhostUserSoundBackend; +pub const SUPPORTED_FORMATS: u64 = 1 << VIRTIO_SND_PCM_FMT_U8 + | 1 << VIRTIO_SND_PCM_FMT_S16 + | 1 << VIRTIO_SND_PCM_FMT_S24 + | 1 << VIRTIO_SND_PCM_FMT_S32; + +pub const SUPPORTED_RATES: u64 = 1 << VIRTIO_SND_PCM_RATE_8000 + | 1 << VIRTIO_SND_PCM_RATE_11025 + | 1 << VIRTIO_SND_PCM_RATE_16000 + | 1 << VIRTIO_SND_PCM_RATE_22050 + | 1 << VIRTIO_SND_PCM_RATE_32000 + | 1 << VIRTIO_SND_PCM_RATE_44100 + | 1 << VIRTIO_SND_PCM_RATE_48000; + +use virtio_queue::DescriptorChain; +pub type SoundDescriptorChain = DescriptorChain>>; pub type Result = std::result::Result; /// Custom error types #[derive(Debug, ThisError)] pub enum Error { + #[error("Notification send failed")] + SendNotificationFailed, + #[error("Descriptor not found")] + DescriptorNotFound, + #[error("Descriptor read failed")] + DescriptorReadFailed, + #[error("Descriptor write failed")] + DescriptorWriteFailed, #[error("Failed to handle event other than EPOLLIN event")] HandleEventNotEpollIn, #[error("Failed to handle unknown event")] HandleUnknownEvent, + #[error("Invalid control message code {0}")] + InvalidControlMessage(u32), #[error("Failed to create a new EventFd")] EventFdCreate(IoError), #[error("Request missing data buffer")] SoundReqMissingData, #[error("Audio backend not supported")] AudioBackendNotSupported, + #[error("Invalid virtio_snd_hdr size, expected: {0}, found: {1}")] + UnexpectedSoundHeaderSize(usize, u32), + #[error("Received unexpected write only descriptor at index {0}")] + UnexpectedWriteOnlyDescriptor(usize), + #[error("Received unexpected readable descriptor at index {0}")] + UnexpectedReadableDescriptor(usize), + #[error("Invalid descriptor count {0}")] + UnexpectedDescriptorCount(usize), + #[error("Invalid descriptor size, expected: {0}, found: {1}")] + UnexpectedDescriptorSize(usize, u32), + #[error("Protocol or device error: {0}")] + Stream(stream::Error), } -impl std::convert::From for IoError { +impl From for IoError { fn from(e: Error) -> Self { - IoError::new(ErrorKind::Other, e) + Self::new(ErrorKind::Other, e) + } +} + +impl From for Error { + fn from(val: stream::Error) -> Self { + Self::Stream(val) + } +} + +#[derive(Debug)] +pub struct InvalidControlMessage(u32); + +impl std::fmt::Display for InvalidControlMessage { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "Invalid control message code {}", self.0) + } +} + +impl From for crate::Error { + fn from(val: InvalidControlMessage) -> Self { + Self::InvalidControlMessage(val.0) + } +} + +#[derive(Copy, Debug, Clone, Eq, PartialEq)] +#[repr(u32)] +pub enum ControlMessageKind { + JackInfo = 1, + JackRemap = 2, + PcmInfo = 0x0100, + PcmSetParams = 0x0101, + PcmPrepare = 0x0102, + PcmRelease = 0x0103, + PcmStart = 0x0104, + PcmStop = 0x0105, + ChmapInfo = 0x0200, +} + +impl TryFrom for ControlMessageKind { + type Error = InvalidControlMessage; + + fn try_from(val: Le32) -> std::result::Result { + Ok(match u32::from(val) { + VIRTIO_SND_R_JACK_INFO => Self::JackInfo, + VIRTIO_SND_R_JACK_REMAP => Self::JackRemap, + VIRTIO_SND_R_PCM_INFO => Self::PcmInfo, + VIRTIO_SND_R_PCM_SET_PARAMS => Self::PcmSetParams, + VIRTIO_SND_R_PCM_PREPARE => Self::PcmPrepare, + VIRTIO_SND_R_PCM_RELEASE => Self::PcmRelease, + VIRTIO_SND_R_PCM_START => Self::PcmStart, + VIRTIO_SND_R_PCM_STOP => Self::PcmStop, + VIRTIO_SND_R_CHMAP_INFO => Self::ChmapInfo, + other => return Err(InvalidControlMessage(other)), + }) + } +} + +pub struct ControlMessage { + pub kind: ControlMessageKind, + pub code: u32, + pub desc_chain: SoundDescriptorChain, + pub descriptor: virtio_queue::Descriptor, + pub vring: VringRwLock, +} + +impl std::fmt::Debug for ControlMessage { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_struct(stringify!(ControlMessage)) + .field("kind", &self.kind) + .field("code", &self.code) + .finish() + } +} + +impl Drop for ControlMessage { + fn drop(&mut self) { + log::trace!( + "dropping ControlMessage {:?} reply = {}", + self.kind, + match self.code { + crate::virtio_sound::VIRTIO_SND_S_OK => "VIRTIO_SND_S_OK", + crate::virtio_sound::VIRTIO_SND_S_BAD_MSG => "VIRTIO_SND_S_BAD_MSG", + crate::virtio_sound::VIRTIO_SND_S_NOT_SUPP => "VIRTIO_SND_S_NOT_SUPP", + crate::virtio_sound::VIRTIO_SND_S_IO_ERR => "VIRTIO_SND_S_IO_ERR", + _ => "other", + } + ); + let resp = VirtioSoundHeader { + code: self.code.into(), + }; + + if let Err(err) = self + .desc_chain + .memory() + .write_obj(resp, self.descriptor.addr()) + { + log::error!("Error::DescriptorWriteFailed: {}", err); + return; + } + if self + .vring + .add_used(self.desc_chain.head_index(), resp.as_slice().len() as u32) + .is_err() + { + log::error!("Couldn't add used"); + } + if self.vring.signal_used_queue().is_err() { + log::error!("Couldn't signal used queue"); + } } } @@ -87,6 +240,7 @@ impl<'a> SoundRequest<'a> { /// This is the public API through which an external program starts the /// vhost-user-sound backend server. pub fn start_backend_server(config: SoundConfig) { + log::trace!("Using config {:?}", &config); let listener = Listener::new(config.get_socket_path(), true).unwrap(); let backend = Arc::new(VhostUserSoundBackend::new(config).unwrap()); @@ -97,6 +251,7 @@ pub fn start_backend_server(config: SoundConfig) { ) .unwrap(); + log::trace!("Starting daemon"); daemon.start(listener).unwrap(); match daemon.wait() { diff --git a/crates/sound/src/stream.rs b/crates/sound/src/stream.rs new file mode 100644 index 00000000..febc0f80 --- /dev/null +++ b/crates/sound/src/stream.rs @@ -0,0 +1,269 @@ +// Manos Pitsidianakis +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause + +use thiserror::Error as ThisError; +use vm_memory::{Le32, Le64}; + +use crate::{virtio_sound::*, SUPPORTED_FORMATS, SUPPORTED_RATES}; + +/// Stream errors. +#[derive(Debug, ThisError)] +pub enum Error { + #[error("Guest driver request an invalid stream state transition from {0} to {1}.")] + InvalidStateTransition(PCMState, PCMState), + #[error("Guest requested an invalid stream id: {0}")] + InvalidStreamId(u32), +} + +type Result = std::result::Result; + +/// PCM stream state machine. +/// +/// ## 5.14.6.6.1 PCM Command Lifecycle +/// +/// A PCM stream has the following command lifecycle: +/// +/// - `SET PARAMETERS` +/// +/// The driver negotiates the stream parameters (format, transport, etc) with +/// the device. +/// +/// Possible valid transitions: `SET PARAMETERS`, `PREPARE`. +/// +/// - `PREPARE` +/// +/// The device prepares the stream (allocates resources, etc). +/// +/// Possible valid transitions: `SET PARAMETERS`, `PREPARE`, `START`, +/// `RELEASE`. Output only: the driver transfers data for pre-buffing. +/// +/// - `START` +/// +/// The device starts the stream (unmute, putting into running state, etc). +/// +/// Possible valid transitions: `STOP`. +/// The driver transfers data to/from the stream. +/// +/// - `STOP` +/// +/// The device stops the stream (mute, putting into non-running state, etc). +/// +/// Possible valid transitions: `START`, `RELEASE`. +/// +/// - `RELEASE` +/// +/// The device releases the stream (frees resources, etc). +/// +/// Possible valid transitions: `SET PARAMETERS`, `PREPARE`. +/// +/// ```text +/// +---------------+ +---------+ +---------+ +-------+ +-------+ +/// | SetParameters | | Prepare | | Release | | Start | | Stop | +/// +---------------+ +---------+ +---------+ +-------+ +-------+ +/// | | | | | +/// |- | | | | +/// || | | | | +/// |< | | | | +/// | | | | | +/// |------------->| | | | +/// | | | | | +/// |<-------------| | | | +/// | | | | | +/// | |- | | | +/// | || | | | +/// | |< | | | +/// | | | | | +/// | |--------------------->| | +/// | | | | | +/// | |---------->| | | +/// | | | | | +/// | | | |-------->| +/// | | | | | +/// | | | |<--------| +/// | | | | | +/// | | |<-------------------| +/// | | | | | +/// |<-------------------------| | | +/// | | | | | +/// | |<----------| | | +/// ``` +#[derive(Debug, Default, Copy, Clone)] +pub enum PCMState { + #[default] + #[doc(alias = "VIRTIO_SND_R_PCM_SET_PARAMS")] + SetParameters, + #[doc(alias = "VIRTIO_SND_R_PCM_PREPARE")] + Prepare, + #[doc(alias = "VIRTIO_SND_R_PCM_RELEASE")] + Release, + #[doc(alias = "VIRTIO_SND_R_PCM_START")] + Start, + #[doc(alias = "VIRTIO_SND_R_PCM_STOP")] + Stop, +} + +macro_rules! set_new_state { + ($new_state_fn:ident, $new_state:expr, $($valid_source_states:tt)*) => { + pub fn $new_state_fn(&mut self) -> Result<()> { + if !matches!(self, $($valid_source_states)*) { + return Err(Error::InvalidStateTransition(*self, $new_state)); + } + *self = $new_state; + Ok(()) + } + }; +} + +impl PCMState { + pub fn new() -> Self { + Self::SetParameters + } + + set_new_state!( + set_parameters, + Self::SetParameters, + Self::SetParameters | Self::Prepare | Self::Release + ); + + set_new_state!( + prepare, + Self::Prepare, + Self::SetParameters | Self::Prepare | Self::Release + ); + + set_new_state!(start, Self::Start, Self::Prepare | Self::Stop); + + set_new_state!(stop, Self::Stop, Self::Start); + + set_new_state!(release, Self::Release, Self::Prepare | Self::Stop); +} + +impl std::fmt::Display for PCMState { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + use PCMState::*; + match *self { + SetParameters => { + write!(fmt, "VIRTIO_SND_R_PCM_SET_PARAMS") + } + Prepare => { + write!(fmt, "VIRTIO_SND_R_PCM_PREPARE") + } + Release => { + write!(fmt, "VIRTIO_SND_R_PCM_RELEASE") + } + Start => { + write!(fmt, "VIRTIO_SND_R_PCM_START") + } + Stop => { + write!(fmt, "VIRTIO_SND_R_PCM_STOP") + } + } + } +} + +/// Internal state of a PCM stream of the VIRTIO Sound device. +#[derive(Debug)] +pub struct Stream { + pub id: usize, + pub params: PcmParams, + pub formats: Le64, + pub rates: Le64, + pub direction: u8, + pub channels_min: u8, + pub channels_max: u8, + pub state: PCMState, +} + +impl Default for Stream { + fn default() -> Self { + Self { + id: 0, + direction: VIRTIO_SND_D_OUTPUT, + formats: SUPPORTED_FORMATS.into(), + rates: SUPPORTED_RATES.into(), + params: PcmParams::default(), + channels_min: 1, + channels_max: 6, + state: Default::default(), + } + } +} + +impl Stream { + #[inline] + pub fn supports_format(&self, format: u8) -> bool { + let formats: u64 = self.formats.into(); + (formats & (1_u64 << format)) != 0 + } + + #[inline] + pub fn supports_rate(&self, rate: u8) -> bool { + let rates: u64 = self.rates.into(); + (rates & (1_u64 << rate)) != 0 + } +} + +/// Stream params +#[derive(Debug)] +pub struct PcmParams { + pub features: Le32, + /// size of hardware buffer in bytes + pub buffer_bytes: Le32, + /// size of hardware period in bytes + pub period_bytes: Le32, + pub channels: u8, + pub format: u8, + pub rate: u8, +} + +impl Default for PcmParams { + fn default() -> Self { + Self { + features: 0.into(), + buffer_bytes: 8192.into(), + period_bytes: 4096.into(), + channels: 1, + format: VIRTIO_SND_PCM_FMT_S16, + rate: VIRTIO_SND_PCM_RATE_44100, + } + } +} + +impl PcmParams { + pub fn sample_bytes(&self) -> usize { + match self.format { + crate::virtio_sound::VIRTIO_SND_PCM_FMT_IMA_ADPCM + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_MU_LAW + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_A_LAW + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_S8 + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_U8 => 1, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_S16 => 2, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_U16 => 2, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_S18_3 + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_U18_3 + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_S20_3 + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_U20_3 + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_S24_3 + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_U24_3 => 3, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_S20 => 3, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_U20 => 3, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_S24 + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_U24 + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_S32 + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_U32 => 4, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_FLOAT => 4, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_FLOAT64 => 8, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_DSD_U8 + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_DSD_U16 + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_DSD_U32 + | crate::virtio_sound::VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME => { + todo!() + } + _ => unreachable!(), + } + } + + pub fn frame(&self) -> usize { + self.sample_bytes() * self.channels as usize + } +} diff --git a/crates/sound/src/virtio_sound.rs b/crates/sound/src/virtio_sound.rs index 075861da..838afe96 100644 --- a/crates/sound/src/virtio_sound.rs +++ b/crates/sound/src/virtio_sound.rs @@ -46,8 +46,8 @@ pub const VIRTIO_SND_S_IO_ERR: u32 = 0x8003; // device data flow directions -pub const VIRTIO_SND_D_OUTPUT: u32 = 0; -pub const VIRTIO_SND_D_INPUT: u32 = 1; +pub const VIRTIO_SND_D_OUTPUT: u8 = 0; +pub const VIRTIO_SND_D_INPUT: u8 = 1; // supported jack features From f91c4de0c04ba8a990fa51a4aff2f25d60c28309 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 4 Jul 2023 17:52:08 +0300 Subject: [PATCH 83/85] sound: Add CTRL queue handler Signed-off-by: Manos Pitsidianakis --- crates/sound/src/audio_backends.rs | 37 +- crates/sound/src/audio_backends/null.rs | 25 +- crates/sound/src/audio_backends/pipewire.rs | 23 +- crates/sound/src/device.rs | 483 +++++++++++++++++--- 4 files changed, 473 insertions(+), 95 deletions(-) diff --git a/crates/sound/src/audio_backends.rs b/crates/sound/src/audio_backends.rs index 86859e0a..3f0a9389 100644 --- a/crates/sound/src/audio_backends.rs +++ b/crates/sound/src/audio_backends.rs @@ -7,25 +7,50 @@ mod null; #[cfg(feature = "pw-backend")] mod pipewire; +use std::sync::{Arc, RwLock}; + #[cfg(feature = "null-backend")] use self::null::NullBackend; #[cfg(feature = "pw-backend")] use self::pipewire::PwBackend; -use crate::{Error, Result, SoundRequest}; +use crate::{device::ControlMessage, stream::Stream, Error, Result}; pub trait AudioBackend { - fn write(&self, req: &SoundRequest) -> Result<()>; + fn write(&self, stream_id: u32) -> Result<()>; + + fn read(&self, stream_id: u32) -> Result<()>; + + fn set_parameters(&self, _stream_id: u32, _: ControlMessage) -> Result<()> { + Ok(()) + } + + fn prepare(&self, _stream_id: u32) -> Result<()> { + Ok(()) + } - fn read(&self, req: &mut SoundRequest) -> Result<()>; + fn release(&self, _stream_id: u32, _: ControlMessage) -> Result<()> { + Ok(()) + } + + fn start(&self, _stream_id: u32) -> Result<()> { + Ok(()) + } + + fn stop(&self, _stream_id: u32) -> Result<()> { + Ok(()) + } } -pub fn alloc_audio_backend(name: String) -> Result> { +pub fn alloc_audio_backend( + name: String, + streams: Arc>>, +) -> Result> { log::trace!("allocating audio backend {}", name); match name.as_str() { #[cfg(feature = "null-backend")] - "null" => Ok(Box::new(NullBackend::new())), + "null" => Ok(Box::new(NullBackend::new(streams))), #[cfg(feature = "pw-backend")] - "pipewire" => Ok(Box::new(PwBackend::new())), + "pipewire" => Ok(Box::new(PwBackend::new(streams))), _ => Err(Error::AudioBackendNotSupported), } } diff --git a/crates/sound/src/audio_backends/null.rs b/crates/sound/src/audio_backends/null.rs index b81f582f..f925cc71 100644 --- a/crates/sound/src/audio_backends/null.rs +++ b/crates/sound/src/audio_backends/null.rs @@ -1,29 +1,28 @@ // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause +use std::sync::{Arc, RwLock}; + use super::AudioBackend; -use crate::{Error, Result, SoundRequest}; +use crate::{Result, Stream}; -pub struct NullBackend {} +pub struct NullBackend { + streams: Arc>>, +} impl NullBackend { - pub fn new() -> Self { - NullBackend {} + pub fn new(streams: Arc>>) -> Self { + Self { streams } } } impl AudioBackend for NullBackend { - fn write(&self, _req: &SoundRequest) -> Result<()> { - log::trace!("NullBackend writing {:?}", _req); + fn write(&self, stream_id: u32) -> Result<()> { + log::trace!("NullBackend write stream_id {}", stream_id); Ok(()) } - fn read(&self, req: &mut SoundRequest) -> Result<()> { - log::trace!("NullBackend reading {:?}", req); - let buf = req.data_slice().ok_or(Error::SoundReqMissingData)?; - let zero_mem = vec![0u8; buf.len()]; - - buf.copy_from(&zero_mem); - + fn read(&self, _id: u32) -> Result<()> { + log::trace!("NullBackend read stream_id {}", _id); Ok(()) } } diff --git a/crates/sound/src/audio_backends/pipewire.rs b/crates/sound/src/audio_backends/pipewire.rs index a9393653..e98c1c76 100644 --- a/crates/sound/src/audio_backends/pipewire.rs +++ b/crates/sound/src/audio_backends/pipewire.rs @@ -1,28 +1,29 @@ // Pipewire backend device // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause +use std::sync::{Arc, RwLock}; + use super::AudioBackend; -use crate::{Error, Result, SoundRequest}; +use crate::{Result, Stream}; -pub struct PwBackend {} +pub struct PwBackend { + streams: Arc>>, +} impl PwBackend { - pub fn new() -> Self { - PwBackend {} + pub fn new(streams: Arc>>) -> Self { + Self { streams } } } impl AudioBackend for PwBackend { - fn write(&self, _req: &SoundRequest) -> Result<()> { + fn write(&self, stream_id: u32) -> Result<()> { + log::trace!("PipewireBackend write stream_id {}", stream_id); Ok(()) } - fn read(&self, req: &mut SoundRequest) -> Result<()> { - let buf = req.data_slice().ok_or(Error::SoundReqMissingData)?; - let zero_mem = vec![0u8; buf.len()]; - - buf.copy_from(&zero_mem); - + fn read(&self, _id: u32) -> Result<()> { + log::trace!("PipewireBackend read stream_id {}", _id); Ok(()) } } diff --git a/crates/sound/src/device.rs b/crates/sound/src/device.rs index 23d69813..904b95f0 100644 --- a/crates/sound/src/device.rs +++ b/crates/sound/src/device.rs @@ -2,16 +2,22 @@ // Stefano Garzarella // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause -use std::{io::Result as IoResult, sync::RwLock, u16, u32, u64, u8}; +use std::{ + convert::TryFrom, + io::Result as IoResult, + sync::{Arc, RwLock}, +}; use vhost::vhost_user::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures}; use vhost_user_backend::{VhostUserBackend, VringRwLock, VringT}; -use virtio_bindings::bindings::{ - virtio_config::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1}, +use virtio_bindings::{ + bindings::virtio_config::{VIRTIO_F_NOTIFY_ON_EMPTY, VIRTIO_F_VERSION_1}, virtio_ring::{VIRTIO_RING_F_EVENT_IDX, VIRTIO_RING_F_INDIRECT_DESC}, }; -use virtio_queue::QueueOwnedT; -use vm_memory::{ByteValued, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap}; +use virtio_queue::{DescriptorChain, QueueOwnedT}; +use vm_memory::{ + ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap, +}; use vmm_sys_util::{ epoll::EventSet, eventfd::{EventFd, EFD_NONBLOCK}, @@ -19,35 +25,35 @@ use vmm_sys_util::{ use crate::{ audio_backends::{alloc_audio_backend, AudioBackend}, + stream::{Error as StreamError, Stream}, virtio_sound::*, - Error, Result, SoundConfig, + ControlMessageKind, Error, Result, SoundConfig, }; struct VhostUserSoundThread { mem: Option>, event_idx: bool, queue_indexes: Vec, + streams: Arc>>, + streams_no: usize, } -#[cfg(debug_assertions)] -const fn queue_idx_as_str(q: u16) -> &'static str { - match q { - CONTROL_QUEUE_IDX => stringify!(CONTROL_QUEUE_IDX), - EVENT_QUEUE_IDX => stringify!(EVENT_QUEUE_IDX), - TX_QUEUE_IDX => stringify!(TX_QUEUE_IDX), - RX_QUEUE_IDX => stringify!(RX_QUEUE_IDX), - _ => "unknown queue idx", - } -} +type SoundDescriptorChain = DescriptorChain>>; impl VhostUserSoundThread { - pub fn new(mut queue_indexes: Vec) -> Result { + pub fn new( + mut queue_indexes: Vec, + streams: Arc>>, + streams_no: usize, + ) -> Result { queue_indexes.sort(); Ok(VhostUserSoundThread { event_idx: false, mem: None, queue_indexes, + streams, + streams_no, }) } @@ -70,36 +76,296 @@ impl VhostUserSoundThread { Ok(()) } - fn handle_event(&self, device_event: u16, vrings: &[VringRwLock]) -> IoResult { - log::trace!("handle_event device_event {}", device_event); - + fn handle_event( + &self, + device_event: u16, + vrings: &[VringRwLock], + audio_backend: &RwLock>, + ) -> IoResult { let vring = &vrings[device_event as usize]; let queue_idx = self.queue_indexes[device_event as usize]; - log::trace!( - "handle_event queue_idx {} == {}", - queue_idx, - queue_idx_as_str(queue_idx) - ); - - dbg!(match queue_idx { - CONTROL_QUEUE_IDX => self.process_control(vring), - EVENT_QUEUE_IDX => self.process_event(vring), - TX_QUEUE_IDX => self.process_tx(vring), - RX_QUEUE_IDX => self.process_rx(vring), - _ => Err(Error::HandleUnknownEvent.into()), - }) + 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(); + match queue_idx { + CONTROL_QUEUE_IDX => self.process_control(vring, audio_backend), + EVENT_QUEUE_IDX => self.process_event(vring), + TX_QUEUE_IDX => self.process_tx(vring, audio_backend), + RX_QUEUE_IDX => self.process_rx(vring, audio_backend), + _ => Err(Error::HandleUnknownEvent.into()), + }?; + if !vring.enable_notification().unwrap() { + break; + } + } + } else { + // Without EVENT_IDX, a single call is enough. + match queue_idx { + CONTROL_QUEUE_IDX => self.process_control(vring, audio_backend), + EVENT_QUEUE_IDX => self.process_event(vring), + TX_QUEUE_IDX => self.process_tx(vring, audio_backend), + RX_QUEUE_IDX => self.process_rx(vring, audio_backend), + _ => Err(Error::HandleUnknownEvent.into()), + }?; + } + Ok(false) } - fn process_control(&self, _vring: &VringRwLock) -> IoResult { - let requests: Vec<_> = _vring + fn process_control( + &self, + vring: &VringRwLock, + audio_backend: &RwLock>, + ) -> IoResult { + let requests: Vec = vring .get_mut() .get_queue_mut() .iter(self.mem.as_ref().unwrap().memory()) .map_err(|_| Error::DescriptorNotFound)? .collect(); - dbg!(&requests); - Ok(true) + if requests.is_empty() { + return Ok(true); + } + + // Reply to some requests right away, and defer others to the audio backend (for + // example PcmRelease needs to complete all I/O before replying. + // + // Mark `any` as true if we need to reply to any request right away so we can + // signal the queue as used. + let mut any = false; + + for desc_chain in requests { + let descriptors: Vec<_> = desc_chain.clone().collect(); + if descriptors.len() < 2 { + return Err(Error::UnexpectedDescriptorCount(descriptors.len()).into()); + } + + // Request descriptor. + let desc_request = descriptors[0]; + if desc_request.is_write_only() { + return Err(Error::UnexpectedWriteOnlyDescriptor(0).into()); + } + + let request = desc_chain + .memory() + .read_obj::(desc_request.addr()) + .map_err(|_| Error::DescriptorReadFailed)?; + + // Keep track of bytes that will be written in the VQ. + let mut used_len = 0; + + // Reply header descriptor. + let desc_hdr = descriptors[1]; + if !desc_hdr.is_write_only() { + return Err(Error::UnexpectedReadableDescriptor(1).into()); + } + + let mut resp = VirtioSoundHeader { + code: VIRTIO_SND_S_OK.into(), + }; + + let code = ControlMessageKind::try_from(request.code).map_err(Error::from)?; + match code { + ControlMessageKind::JackInfo => { + resp.code = VIRTIO_SND_S_NOT_SUPP.into(); + } + ControlMessageKind::JackRemap => { + resp.code = VIRTIO_SND_S_NOT_SUPP.into(); + } + ControlMessageKind::PcmInfo => { + if descriptors.len() != 3 { + log::error!("a PCM_INFO request should have three descriptors total."); + return Err(Error::UnexpectedDescriptorCount(descriptors.len()).into()); + } else if !descriptors[2].is_write_only() { + log::error!( + "a PCM_INFO request should have a writeable descriptor for the info \ + payload response after the header status response" + ); + return Err(Error::UnexpectedReadableDescriptor(2).into()); + } + + let request = desc_chain + .memory() + .read_obj::(desc_request.addr()) + .map_err(|_| Error::DescriptorReadFailed)?; + + let start_id = u32::from(request.start_id) as usize; + let count = u32::from(request.count) as usize; + let streams = self.streams.read().unwrap(); + if streams.len() <= start_id || streams.len() <= start_id + count { + resp.code = VIRTIO_SND_S_BAD_MSG.into(); + } else { + let desc_response = descriptors[2]; + + let mut buf = vec![]; + let mut p: VirtioSoundPcmInfo; + + for s in streams + .iter() + .skip(u32::from(request.start_id) as usize) + .take(u32::from(request.count) as usize) + { + p = VirtioSoundPcmInfo::default(); + p.hdr.hda_fn_nid = 0.into(); + p.features = s.params.features; + p.formats = s.formats; + p.rates = s.rates; + p.direction = s.direction; + p.channels_min = s.channels_min; + p.channels_max = s.channels_max; + buf.extend_from_slice(p.as_slice()); + } + desc_chain + .memory() + .write_slice(&buf, desc_response.addr()) + .map_err(|_| Error::DescriptorWriteFailed)?; + used_len += desc_response.len(); + } + } + ControlMessageKind::PcmSetParams => { + let request = desc_chain + .memory() + .read_obj::(desc_request.addr()) + .map_err(|_| Error::DescriptorReadFailed)?; + let stream_id: u32 = request.hdr.stream_id.into(); + + if stream_id as usize >= self.streams_no { + log::error!("{}", Error::from(StreamError::InvalidStreamId(stream_id))); + resp.code = VIRTIO_SND_S_BAD_MSG.into(); + } else { + let b = audio_backend.read().unwrap(); + b.set_parameters( + stream_id, + ControlMessage { + kind: code, + code: VIRTIO_SND_S_OK, + desc_chain, + descriptor: desc_hdr, + vring: vring.clone(), + }, + ) + .unwrap(); + + // PcmSetParams needs check valid formats/rates; the audio backend will + // reply when it drops the ControlMessage. + continue; + } + } + ControlMessageKind::PcmPrepare => { + let request = desc_chain + .memory() + .read_obj::(desc_request.addr()) + .map_err(|_| Error::DescriptorReadFailed)?; + let stream_id = request.stream_id.into(); + + if stream_id as usize >= self.streams_no { + log::error!("{}", Error::from(StreamError::InvalidStreamId(stream_id))); + resp.code = VIRTIO_SND_S_BAD_MSG.into(); + } else { + let b = audio_backend.write().unwrap(); + b.prepare(stream_id).unwrap(); + } + } + ControlMessageKind::PcmRelease => { + let request = desc_chain + .memory() + .read_obj::(desc_request.addr()) + .map_err(|_| Error::DescriptorReadFailed)?; + let stream_id = request.stream_id.into(); + + if stream_id as usize >= self.streams_no { + log::error!("{}", Error::from(StreamError::InvalidStreamId(stream_id))); + resp.code = VIRTIO_SND_S_BAD_MSG.into(); + } else { + let b = audio_backend.write().unwrap(); + b.release( + stream_id, + ControlMessage { + kind: code, + code: VIRTIO_SND_S_OK, + desc_chain, + descriptor: desc_hdr, + vring: vring.clone(), + }, + ) + .unwrap(); + + // PcmRelease needs to flush IO messages; the audio backend will reply when + // it drops the ControlMessage. + continue; + } + } + ControlMessageKind::PcmStart => { + let request = desc_chain + .memory() + .read_obj::(desc_request.addr()) + .map_err(|_| Error::DescriptorReadFailed)?; + let stream_id = request.stream_id.into(); + + if stream_id as usize >= self.streams_no { + log::error!("{}", Error::from(StreamError::InvalidStreamId(stream_id))); + resp.code = VIRTIO_SND_S_BAD_MSG.into(); + } else { + let b = audio_backend.write().unwrap(); + b.start(stream_id).unwrap(); + } + } + ControlMessageKind::PcmStop => { + let request = desc_chain + .memory() + .read_obj::(desc_request.addr()) + .map_err(|_| Error::DescriptorReadFailed)?; + let stream_id = request.stream_id.into(); + + if stream_id as usize >= self.streams_no { + log::error!("{}", Error::from(StreamError::InvalidStreamId(stream_id))); + resp.code = VIRTIO_SND_S_BAD_MSG.into(); + } else { + let b = audio_backend.write().unwrap(); + b.stop(stream_id).unwrap(); + } + } + ControlMessageKind::ChmapInfo => { + resp.code = VIRTIO_SND_S_NOT_SUPP.into(); + } + } + log::trace!( + "returned {} for ctrl msg {:?}", + match u32::from(resp.code) { + v if v == VIRTIO_SND_S_OK => "OK", + v if v == VIRTIO_SND_S_BAD_MSG => "BAD_MSG", + v if v == VIRTIO_SND_S_NOT_SUPP => "NOT_SUPP", + v if v == VIRTIO_SND_S_IO_ERR => "IO_ERR", + _ => unreachable!(), + }, + code + ); + desc_chain + .memory() + .write_obj(resp, desc_hdr.addr()) + .map_err(|_| Error::DescriptorWriteFailed)?; + used_len += desc_hdr.len(); + + if vring.add_used(desc_chain.head_index(), used_len).is_err() { + log::error!("Couldn't return used descriptors to the ring"); + } + any |= true; + } + + // In which cases can happen that we get here and any is false? + // PCM_RELEASE and PCM_SET_PARAMS need to be handled asynchronously, therefore + // it will be their responsibility to `signal_used_queue`. + + // Send notification if any request was processed + if any && vring.signal_used_queue().is_err() { + log::error!("Couldn't signal used queue"); + } + + Ok(!any) } fn process_event(&self, _vring: &VringRwLock) -> IoResult { @@ -107,12 +373,20 @@ impl VhostUserSoundThread { Ok(false) } - fn process_tx(&self, _vring: &VringRwLock) -> IoResult { + fn process_tx( + &self, + _vring: &VringRwLock, + _audio_backend: &RwLock>, + ) -> IoResult { log::trace!("process_tx"); Ok(false) } - fn process_rx(&self, _vring: &VringRwLock) -> IoResult { + fn process_rx( + &self, + _vring: &VringRwLock, + _audio_backend: &RwLock>, + ) -> IoResult { log::trace!("process_rx"); Ok(false) } @@ -122,31 +396,58 @@ pub struct VhostUserSoundBackend { threads: Vec>, virtio_cfg: VirtioSoundConfig, exit_event: EventFd, - _audio_backend: RwLock>, + audio_backend: RwLock>, } impl VhostUserSoundBackend { pub fn new(config: SoundConfig) -> Result { + let streams = vec![ + Stream { + id: 0, + direction: VIRTIO_SND_D_OUTPUT, + ..Stream::default() + }, + Stream { + id: 1, + direction: VIRTIO_SND_D_INPUT, + ..Stream::default() + }, + ]; + let streams_no = streams.len(); + let streams = Arc::new(RwLock::new(streams)); log::trace!("VhostUserSoundBackend::new config {:?}", &config); - let threads = if dbg!(config.multi_thread) { + let threads = if config.multi_thread { vec![ - RwLock::new(VhostUserSoundThread::new(vec![ - CONTROL_QUEUE_IDX, - EVENT_QUEUE_IDX, - ])?), - RwLock::new(VhostUserSoundThread::new(vec![TX_QUEUE_IDX])?), - RwLock::new(VhostUserSoundThread::new(vec![RX_QUEUE_IDX])?), + RwLock::new(VhostUserSoundThread::new( + vec![CONTROL_QUEUE_IDX, EVENT_QUEUE_IDX], + streams.clone(), + streams_no, + )?), + RwLock::new(VhostUserSoundThread::new( + vec![TX_QUEUE_IDX], + streams.clone(), + streams_no, + )?), + RwLock::new(VhostUserSoundThread::new( + vec![RX_QUEUE_IDX], + streams.clone(), + streams_no, + )?), ] } else { - vec![RwLock::new(VhostUserSoundThread::new(vec![ - CONTROL_QUEUE_IDX, - EVENT_QUEUE_IDX, - TX_QUEUE_IDX, - RX_QUEUE_IDX, - ])?)] + vec![RwLock::new(VhostUserSoundThread::new( + vec![ + CONTROL_QUEUE_IDX, + EVENT_QUEUE_IDX, + TX_QUEUE_IDX, + RX_QUEUE_IDX, + ], + streams.clone(), + streams_no, + )?)] }; - let audio_backend = alloc_audio_backend(config.audio_backend_name)?; + let audio_backend = alloc_audio_backend(config.audio_backend_name, streams)?; Ok(Self { threads, @@ -156,7 +457,7 @@ impl VhostUserSoundBackend { chmaps: 0.into(), }, exit_event: EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?, - _audio_backend: RwLock::new(audio_backend), + audio_backend: RwLock::new(audio_backend), }) } @@ -171,7 +472,8 @@ impl VhostUserBackend for VhostUserSoundBackend { } fn max_queue_size(&self) -> usize { - 256 + // TODO: Investigate if an alternative value makes any difference. + 64 } fn features(&self) -> u64 { @@ -187,14 +489,12 @@ impl VhostUserBackend for VhostUserSoundBackend { } fn set_event_idx(&self, enabled: bool) { - log::trace!("set_event_idx enabled {:?}", enabled); for thread in self.threads.iter() { thread.write().unwrap().set_event_idx(enabled); } } fn update_memory(&self, mem: GuestMemoryAtomic) -> IoResult<()> { - log::trace!("update_memory"); for thread in self.threads.iter() { thread.write().unwrap().update_memory(mem.clone())?; } @@ -209,19 +509,18 @@ impl VhostUserBackend for VhostUserSoundBackend { vrings: &[VringRwLock], thread_id: usize, ) -> IoResult { - log::trace!("handle_event device_event {}", device_event); if evset != EventSet::IN { return Err(Error::HandleEventNotEpollIn.into()); } - self.threads[thread_id] - .read() - .unwrap() - .handle_event(device_event, vrings) + self.threads[thread_id].read().unwrap().handle_event( + device_event, + vrings, + &self.audio_backend, + ) } fn get_config(&self, offset: u32, size: u32) -> Vec { - log::trace!("get_config offset {} size {}", offset, size); let offset = offset as usize; let size = size as usize; @@ -245,7 +544,61 @@ impl VhostUserBackend for VhostUserSoundBackend { } fn exit_event(&self, _thread_index: usize) -> Option { - log::trace!("exit_event"); self.exit_event.try_clone().ok() } } + +pub struct ControlMessage { + pub kind: ControlMessageKind, + pub code: u32, + pub desc_chain: SoundDescriptorChain, + pub descriptor: virtio_queue::Descriptor, + pub vring: VringRwLock, +} + +impl std::fmt::Debug for ControlMessage { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_struct(stringify!(ControlMessage)) + .field("kind", &self.kind) + .field("code", &self.code) + .finish() + } +} + +impl Drop for ControlMessage { + fn drop(&mut self) { + log::trace!( + "dropping ControlMessage {:?} reply = {}", + self.kind, + match self.code { + crate::virtio_sound::VIRTIO_SND_S_OK => "VIRTIO_SND_S_OK", + crate::virtio_sound::VIRTIO_SND_S_BAD_MSG => "VIRTIO_SND_S_BAD_MSG", + crate::virtio_sound::VIRTIO_SND_S_NOT_SUPP => "VIRTIO_SND_S_NOT_SUPP", + crate::virtio_sound::VIRTIO_SND_S_IO_ERR => "VIRTIO_SND_S_IO_ERR", + _ => "other", + } + ); + let resp = VirtioSoundHeader { + code: self.code.into(), + }; + + if let Err(err) = self + .desc_chain + .memory() + .write_obj(resp, self.descriptor.addr()) + { + log::error!("Error::DescriptorWriteFailed: {}", err); + return; + } + if self + .vring + .add_used(self.desc_chain.head_index(), resp.as_slice().len() as u32) + .is_err() + { + log::error!("Couldn't add used"); + } + if self.vring.signal_used_queue().is_err() { + log::error!("Couldn't signal used queue"); + } + } +} From 10437550b2864a4726a2f5b4ebb5ca57ccaa3beb Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 4 Jul 2023 17:52:13 +0300 Subject: [PATCH 84/85] sound: Add TX queue handler Signed-off-by: Manos Pitsidianakis --- crates/sound/src/audio_backends/null.rs | 1 + crates/sound/src/audio_backends/pipewire.rs | 1 + crates/sound/src/device.rs | 123 +++++++++++++++++++- crates/sound/src/lib.rs | 42 +++++-- crates/sound/src/stream.rs | 38 +++++- 5 files changed, 189 insertions(+), 16 deletions(-) diff --git a/crates/sound/src/audio_backends/null.rs b/crates/sound/src/audio_backends/null.rs index f925cc71..66edb5e6 100644 --- a/crates/sound/src/audio_backends/null.rs +++ b/crates/sound/src/audio_backends/null.rs @@ -18,6 +18,7 @@ impl NullBackend { impl AudioBackend for NullBackend { fn write(&self, stream_id: u32) -> Result<()> { log::trace!("NullBackend write stream_id {}", stream_id); + _ = std::mem::take(&mut self.streams.write().unwrap()[stream_id as usize].buffers); Ok(()) } diff --git a/crates/sound/src/audio_backends/pipewire.rs b/crates/sound/src/audio_backends/pipewire.rs index e98c1c76..c051c084 100644 --- a/crates/sound/src/audio_backends/pipewire.rs +++ b/crates/sound/src/audio_backends/pipewire.rs @@ -19,6 +19,7 @@ impl PwBackend { impl AudioBackend for PwBackend { fn write(&self, stream_id: u32) -> Result<()> { log::trace!("PipewireBackend write stream_id {}", stream_id); + _ = std::mem::take(&mut self.streams.write().unwrap()[stream_id as usize].buffers); Ok(()) } diff --git a/crates/sound/src/device.rs b/crates/sound/src/device.rs index 904b95f0..7985e918 100644 --- a/crates/sound/src/device.rs +++ b/crates/sound/src/device.rs @@ -3,8 +3,10 @@ // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause use std::{ + collections::BTreeSet, convert::TryFrom, io::Result as IoResult, + mem::size_of, sync::{Arc, RwLock}, }; @@ -25,9 +27,9 @@ use vmm_sys_util::{ use crate::{ audio_backends::{alloc_audio_backend, AudioBackend}, - stream::{Error as StreamError, Stream}, + stream::{Buffer, Error as StreamError, Stream}, virtio_sound::*, - ControlMessageKind, Error, Result, SoundConfig, + ControlMessageKind, Error, IOMessage, Result, SoundConfig, }; struct VhostUserSoundThread { @@ -375,10 +377,121 @@ impl VhostUserSoundThread { fn process_tx( &self, - _vring: &VringRwLock, - _audio_backend: &RwLock>, + vring: &VringRwLock, + audio_backend: &RwLock>, ) -> IoResult { - log::trace!("process_tx"); + let requests: Vec = vring + .get_mut() + .get_queue_mut() + .iter(self.mem.as_ref().unwrap().memory()) + .map_err(|_| Error::DescriptorNotFound)? + .collect(); + + if requests.is_empty() { + return Ok(true); + } + + #[derive(Copy, Clone, PartialEq, Debug)] + enum TxState { + Ready, + WaitingBufferForStreamId(u32), + Done, + } + + let mut stream_ids = BTreeSet::default(); + + for desc_chain in requests { + let mut state = TxState::Ready; + let mut buffers = vec![]; + let descriptors: Vec<_> = desc_chain.clone().collect(); + let message = Arc::new(IOMessage { + vring: vring.clone(), + status: VIRTIO_SND_S_OK.into(), + desc_chain: desc_chain.clone(), + descriptor: descriptors.last().cloned().unwrap(), + }); + for (_, descriptor) in descriptors.iter().enumerate() { + match state { + TxState::Done => { + return Err(Error::UnexpectedDescriptorCount(descriptors.len()).into()); + } + TxState::Ready if descriptor.is_write_only() => { + if descriptor.len() as usize != size_of::() { + return Err(Error::UnexpectedDescriptorSize( + size_of::(), + descriptor.len(), + ) + .into()); + } + state = TxState::Done; + } + TxState::WaitingBufferForStreamId(stream_id) if descriptor.is_write_only() => { + if descriptor.len() as usize != size_of::() { + return Err(Error::UnexpectedDescriptorSize( + size_of::(), + descriptor.len(), + ) + .into()); + } + let mut streams = self.streams.write().unwrap(); + for b in std::mem::take(&mut buffers) { + streams[stream_id as usize].buffers.push_back(b); + } + state = TxState::Done; + } + TxState::Ready + if descriptor.len() as usize != size_of::() => + { + return Err(Error::UnexpectedDescriptorSize( + size_of::(), + descriptor.len(), + ) + .into()); + } + TxState::Ready => { + let xfer = desc_chain + .memory() + .read_obj::(descriptor.addr()) + .map_err(|_| Error::DescriptorReadFailed)?; + let stream_id: u32 = xfer.stream_id.into(); + stream_ids.insert(stream_id); + + state = TxState::WaitingBufferForStreamId(stream_id); + } + TxState::WaitingBufferForStreamId(stream_id) + if descriptor.len() as usize == size_of::() => + { + return Err(Error::UnexpectedDescriptorSize( + u32::from( + self.streams.read().unwrap()[stream_id as usize] + .params + .buffer_bytes, + ) as usize, + descriptor.len(), + ) + .into()); + } + TxState::WaitingBufferForStreamId(_stream_id) => { + let mut buf = vec![0; descriptor.len() as usize]; + let bytes_read = desc_chain + .memory() + .read(&mut buf, descriptor.addr()) + .map_err(|_| Error::DescriptorReadFailed)?; + buf.truncate(bytes_read); + + buffers.push(Buffer::new(buf, Arc::clone(&message))); + } + } + } + } + + if !stream_ids.is_empty() { + let b = audio_backend.write().unwrap(); + for id in stream_ids { + b.write(id).unwrap(); + } + } + Ok(false) } diff --git a/crates/sound/src/lib.rs b/crates/sound/src/lib.rs index ed9a3d05..f3d6d510 100644 --- a/crates/sound/src/lib.rs +++ b/crates/sound/src/lib.rs @@ -20,7 +20,6 @@ use vhost_user_backend::{VhostUserDaemon, VringRwLock, VringT}; use virtio_sound::*; use vm_memory::{ ByteValued, Bytes, GuestMemoryAtomic, GuestMemoryLoadGuard, GuestMemoryMmap, Le32, - VolatileSlice, }; use crate::device::VhostUserSoundBackend; @@ -224,16 +223,39 @@ impl SoundConfig { } } -pub type SoundBitmap = (); - -#[derive(Debug)] -pub struct SoundRequest<'a> { - data_slice: Option>, +pub struct IOMessage { + status: std::sync::atomic::AtomicU32, + desc_chain: SoundDescriptorChain, + descriptor: virtio_queue::Descriptor, + vring: VringRwLock, } -impl<'a> SoundRequest<'a> { - pub fn data_slice(&self) -> Option<&VolatileSlice<'a, SoundBitmap>> { - self.data_slice.as_ref() +impl Drop for IOMessage { + fn drop(&mut self) { + log::trace!("dropping IOMessage"); + let resp = VirtioSoundPcmStatus { + status: self.status.load(std::sync::atomic::Ordering::SeqCst).into(), + latency_bytes: 0.into(), + }; + + if let Err(err) = self + .desc_chain + .memory() + .write_obj(resp, self.descriptor.addr()) + { + log::error!("Error::DescriptorWriteFailed: {}", err); + return; + } + if self + .vring + .add_used(self.desc_chain.head_index(), resp.as_slice().len() as u32) + .is_err() + { + log::error!("Couldn't add used"); + } + if self.vring.signal_used_queue().is_err() { + log::error!("Couldn't signal used queue"); + } } } @@ -247,7 +269,7 @@ pub fn start_backend_server(config: SoundConfig) { let mut daemon = VhostUserDaemon::new( String::from("vhost-user-sound"), backend.clone(), - GuestMemoryAtomic::new(GuestMemoryMmap::::new()), + GuestMemoryAtomic::new(GuestMemoryMmap::new()), ) .unwrap(); diff --git a/crates/sound/src/stream.rs b/crates/sound/src/stream.rs index febc0f80..b0aa0041 100644 --- a/crates/sound/src/stream.rs +++ b/crates/sound/src/stream.rs @@ -1,10 +1,12 @@ // Manos Pitsidianakis // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause +use std::{collections::VecDeque, sync::Arc}; + use thiserror::Error as ThisError; use vm_memory::{Le32, Le64}; -use crate::{virtio_sound::*, SUPPORTED_FORMATS, SUPPORTED_RATES}; +use crate::{virtio_sound::*, IOMessage, SUPPORTED_FORMATS, SUPPORTED_RATES}; /// Stream errors. #[derive(Debug, ThisError)] @@ -172,6 +174,7 @@ pub struct Stream { pub channels_min: u8, pub channels_max: u8, pub state: PCMState, + pub buffers: VecDeque, } impl Default for Stream { @@ -185,6 +188,7 @@ impl Default for Stream { channels_min: 1, channels_max: 6, state: Default::default(), + buffers: VecDeque::new(), } } } @@ -267,3 +271,35 @@ impl PcmParams { self.sample_bytes() * self.channels as usize } } + +pub struct Buffer { + pub bytes: Vec, + pub pos: usize, + pub message: Arc, +} + +impl std::fmt::Debug for Buffer { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_struct(stringify!(Buffer)) + .field("bytes", &self.bytes.len()) + .field("pos", &self.pos) + .field("message", &Arc::as_ptr(&self.message)) + .finish() + } +} + +impl Buffer { + pub fn new(bytes: Vec, message: Arc) -> Self { + Self { + bytes, + pos: 0, + message, + } + } +} + +impl Drop for Buffer { + fn drop(&mut self) { + log::trace!("dropping buffer {:?}", self); + } +} From 81a03fb2935c5016b391f753fc5e65e6c91cface Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 4 Jul 2023 17:52:23 +0300 Subject: [PATCH 85/85] sound: add ALSA backend Signed-off-by: Manos Pitsidianakis --- Cargo.lock | 36 +- crates/sound/Cargo.toml | 4 +- crates/sound/src/audio_backends.rs | 6 + crates/sound/src/audio_backends/alsa.rs | 555 ++++++++++++++++++++++++ 4 files changed, 599 insertions(+), 2 deletions(-) create mode 100644 crates/sound/src/audio_backends/alsa.rs diff --git a/Cargo.lock b/Cargo.lock index a3999692..9737fc04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,6 +22,28 @@ dependencies = [ "memchr", ] +[[package]] +name = "alsa" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8512c9117059663fb5606788fbca3619e2a91dac0e3fe516242eab1fa6be5e44" +dependencies = [ + "alsa-sys", + "bitflags 1.3.2", + "libc", + "nix 0.24.3", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "anstream" version = "0.3.2" @@ -771,6 +793,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "nix" version = "0.26.2" @@ -945,7 +978,7 @@ dependencies = [ "libc", "libspa", "libspa-sys", - "nix", + "nix 0.26.2", "once_cell", "pipewire-sys", "thiserror", @@ -1617,6 +1650,7 @@ dependencies = [ name = "vhost-user-sound" version = "0.1.0" dependencies = [ + "alsa", "bindgen 0.64.0", "clap", "env_logger", diff --git a/crates/sound/Cargo.toml b/crates/sound/Cargo.toml index 20e8b04f..05c15a39 100644 --- a/crates/sound/Cargo.toml +++ b/crates/sound/Cargo.toml @@ -10,11 +10,13 @@ license = "Apache-2.0 OR BSD-3-Clause" edition = "2018" [features] -default = ["null-backend"] +default = ["null-backend", "alsa-backend", "pw-backend"] null-backend = [] +alsa-backend = ["dep:alsa"] pw-backend = ["pipewire", "libspa", "pipewire-sys", "libspa-sys", "bindgen"] [dependencies] +alsa = { version = "0.7", optional = true } bindgen = { version = "0.64.0", optional = true } clap = { version = "4.1", features = ["derive"] } env_logger = "0.10" diff --git a/crates/sound/src/audio_backends.rs b/crates/sound/src/audio_backends.rs index 3f0a9389..950cd3fe 100644 --- a/crates/sound/src/audio_backends.rs +++ b/crates/sound/src/audio_backends.rs @@ -1,6 +1,8 @@ // Manos Pitsidianakis // SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause +#[cfg(feature = "alsa-backend")] +mod alsa; #[cfg(feature = "null-backend")] mod null; @@ -9,6 +11,8 @@ mod pipewire; use std::sync::{Arc, RwLock}; +#[cfg(feature = "alsa-backend")] +use self::alsa::AlsaBackend; #[cfg(feature = "null-backend")] use self::null::NullBackend; #[cfg(feature = "pw-backend")] @@ -51,6 +55,8 @@ pub fn alloc_audio_backend( "null" => Ok(Box::new(NullBackend::new(streams))), #[cfg(feature = "pw-backend")] "pipewire" => Ok(Box::new(PwBackend::new(streams))), + #[cfg(feature = "alsa-backend")] + "alsa" => Ok(Box::new(AlsaBackend::new(streams))), _ => Err(Error::AudioBackendNotSupported), } } diff --git a/crates/sound/src/audio_backends/alsa.rs b/crates/sound/src/audio_backends/alsa.rs new file mode 100644 index 00000000..3917612a --- /dev/null +++ b/crates/sound/src/audio_backends/alsa.rs @@ -0,0 +1,555 @@ +/// Alsa backend +// +// Manos Pitsidianakis +// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause +use std::{ + convert::{TryFrom, TryInto}, + sync::mpsc::{channel, Receiver, Sender}, + sync::{Arc, Mutex, RwLock}, + thread, + thread::sleep, + time::Duration, +}; + +use alsa::{ + pcm::{Access, Format, HwParams, State, PCM}, + Direction, PollDescriptors, ValueOr, +}; +use virtio_queue::Descriptor; +use vm_memory::Bytes; + +use super::AudioBackend; +use crate::{ + device::ControlMessage, + stream::Stream, + virtio_sound::{ + VirtioSndPcmSetParams, VIRTIO_SND_D_INPUT, VIRTIO_SND_D_OUTPUT, VIRTIO_SND_S_BAD_MSG, + VIRTIO_SND_S_NOT_SUPP, + }, + Result as CrateResult, +}; + +type AResult = std::result::Result; + +#[derive(Clone, Debug)] +pub struct AlsaBackend { + sender: Arc>>, +} + +#[derive(Debug)] +enum AlsaAction { + SetParameters(usize, ControlMessage), + Prepare(usize), + Release(usize, ControlMessage), + Start(usize), + Stop(usize), + Write(usize), + Read(usize), +} + +fn update_pcm( + pcm_: &Arc>, + stream_id: usize, + streams: &RwLock>, +) -> AResult<()> { + *pcm_.lock().unwrap() = { + let streams = streams.read().unwrap(); + let s = &streams[stream_id]; + let pcm = PCM::new( + "default", + match s.direction { + d if d == VIRTIO_SND_D_OUTPUT => Direction::Playback, + d if d == VIRTIO_SND_D_INPUT => Direction::Capture, + other => panic!("Invalid virtio-sound stream: {}", other), + }, + false, + )?; + + { + let hwp = HwParams::any(&pcm)?; + hwp.set_channels(s.params.channels.into())?; + hwp.set_rate( + match s.params.rate { + crate::virtio_sound::VIRTIO_SND_PCM_RATE_5512 => 5512, + crate::virtio_sound::VIRTIO_SND_PCM_RATE_8000 => 8000, + crate::virtio_sound::VIRTIO_SND_PCM_RATE_11025 => 11025, + crate::virtio_sound::VIRTIO_SND_PCM_RATE_16000 => 16000, + crate::virtio_sound::VIRTIO_SND_PCM_RATE_22050 => 22050, + crate::virtio_sound::VIRTIO_SND_PCM_RATE_32000 => 32000, + crate::virtio_sound::VIRTIO_SND_PCM_RATE_44100 => 44100, + crate::virtio_sound::VIRTIO_SND_PCM_RATE_48000 => 48000, + crate::virtio_sound::VIRTIO_SND_PCM_RATE_64000 => 64000, + crate::virtio_sound::VIRTIO_SND_PCM_RATE_88200 => 88200, + crate::virtio_sound::VIRTIO_SND_PCM_RATE_96000 => 96000, + crate::virtio_sound::VIRTIO_SND_PCM_RATE_176400 => 176400, + crate::virtio_sound::VIRTIO_SND_PCM_RATE_192000 => 192000, + crate::virtio_sound::VIRTIO_SND_PCM_RATE_384000 => 384000, + _ => 44100, + }, + ValueOr::Nearest, + )?; + hwp.set_format(match s.params.format { + crate::virtio_sound::VIRTIO_SND_PCM_FMT_IMA_ADPCM => Format::ImaAdPCM, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_MU_LAW => Format::MuLaw, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_A_LAW => Format::ALaw, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_S8 => Format::S8, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_U8 => Format::U8, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_S16 => Format::s16(), + crate::virtio_sound::VIRTIO_SND_PCM_FMT_U16 => Format::r#u16(), + crate::virtio_sound::VIRTIO_SND_PCM_FMT_S18_3 => Format::S183LE, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_U18_3 => Format::U183LE, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_S20_3 => Format::S203LE, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_U20_3 => Format::U203LE, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_S24_3 => Format::S24LE, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_U24_3 => Format::U24LE, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_S20 => Format::s20_3(), + crate::virtio_sound::VIRTIO_SND_PCM_FMT_U20 => Format::u20_3(), + crate::virtio_sound::VIRTIO_SND_PCM_FMT_S24 => Format::s24(), + crate::virtio_sound::VIRTIO_SND_PCM_FMT_U24 => Format::u24(), + crate::virtio_sound::VIRTIO_SND_PCM_FMT_S32 => Format::s32(), + crate::virtio_sound::VIRTIO_SND_PCM_FMT_U32 => Format::r#u32(), + crate::virtio_sound::VIRTIO_SND_PCM_FMT_FLOAT => Format::float(), + crate::virtio_sound::VIRTIO_SND_PCM_FMT_FLOAT64 => Format::float64(), + crate::virtio_sound::VIRTIO_SND_PCM_FMT_DSD_U8 => Format::DSDU8, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_DSD_U16 => Format::DSDU16LE, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_DSD_U32 => Format::DSDU32LE, + crate::virtio_sound::VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME => { + Format::iec958_subframe() + } + _ => Format::Unknown, + })?; + + hwp.set_access(Access::RWInterleaved)?; + + // > A period is the number of frames in between each hardware interrupt. + // - https://www.alsa-project.org/wiki/FramesPeriods + + // FIXME + if let Err(err) = hwp.set_buffer_size(4096) { + log::error!("Could not set buffer size of 4096 bytes: {}.", err,); + } + pcm.hw_params(&hwp)?; + } + + // FIXME + //if let Err(err) = hwp.set_period_size(_, ValueOr::Greater) { + // log::error!( + // "Could not set period size: {}", + // err, + // ); + // } + //} + + /* + // { + // Make sure we don't start the stream too early + let hwp = pcm.hw_params_current()?; + let swp = pcm.sw_params_current()?; + //swp.set_start_threshold(hwp.get_buffer_size()?)?; + //swp.set_stop_threshold(hwp.get_buffer_size()?)?; + pcm.sw_params(&swp)?; + } + */ + pcm + }; + Ok(()) +} + +fn write_samples_direct( + pcm: &alsa::PCM, + stream: &mut Stream, + mmap: &mut alsa::direct::pcm::MmapPlayback, +) -> AResult { + while mmap.avail() > 0 { + // Write samples to DMA area from iterator + // + let Some(buffer) = stream.buffers.front_mut() else { + return Ok(false); + }; + let mut iter = buffer.bytes[buffer.pos..].iter().cloned(); + let frames = mmap.write(&mut iter); + let written_bytes = pcm.frames_to_bytes(frames); + if let Ok(written_bytes) = usize::try_from(written_bytes) { + buffer.pos += written_bytes; + } + if buffer.pos >= buffer.bytes.len() { + stream.buffers.pop_front(); + } + } + match mmap.status().state() { + State::Running => { + return Ok(false); + } // All fine + State::Prepared => { + //log::trace!("Starting audio output stream"); + //p.start()? + } + State::XRun => { + log::trace!("Underrun in audio output stream!"); + pcm.prepare()? + } + State::Suspended => { + //log::trace!("Resuming audio output stream"); + //p.resume()? + } + n => panic!("Unexpected pcm state {:?}", n), + } + Ok(true) // Call us again, please, there might be more data to write +} + +fn write_samples_io( + p: &alsa::PCM, + + stream: &mut Stream, + io: &mut alsa::pcm::IO, +) -> AResult { + macro_rules! avail { + () => {{ + (match p.avail_update() { + Ok(n) => n, + Err(err) => { + log::trace!("Recovering from {}", err); + p.recover(err.errno() as std::os::raw::c_int, true)?; + p.avail_update()? + } + }) as usize + }}; + } + + loop { + let avail = avail!(); + if avail == 0 { + break; + } + let written = io.mmap(avail, |buf| { + let Some(buffer) = stream.buffers.front_mut() else { + return 0; + }; + let mut iter = buffer.bytes[buffer.pos..].iter().cloned(); + + let mut written_bytes = 0; + for (sample, byte) in buf.iter_mut().zip(&mut iter) { + *sample = byte; + written_bytes += 1; + } + buffer.pos += written_bytes as usize; + if buffer.pos >= buffer.bytes.len() { + stream.buffers.pop_front(); + } + p.bytes_to_frames(written_bytes) + .try_into() + .unwrap_or_default() + })?; + if written == 0 { + break; + }; + } + + match p.state() { + State::Suspended | State::Running => Ok(false), // All fine + State::Prepared => { + //p.start()?; + Ok(false) + } + State::XRun => Ok(true), // Recover from this in next round + n => panic!("Unexpected pcm state {:?}", n), + } +} + +fn alsa_worker( + pcm: Arc>, + streams: Arc>>, + receiver: &Receiver, + stream_id: usize, +) -> AResult<()> { + loop { + let Ok(do_write) = receiver.recv() else { + return Ok(()); + }; + if do_write { + loop { + if matches!(receiver.try_recv(), Ok(false)) { + break; + } + + let mut fds = { + let lck = pcm.lock().unwrap(); + if matches!(lck.state(), State::Running | State::Prepared | State::XRun) { + let mut mmap = lck.direct_mmap_playback::().ok(); + + if let Some(ref mut mmap) = mmap { + if write_samples_direct( + &lck, + &mut streams.write().unwrap()[stream_id], + mmap, + )? { + continue; + } + } else { + let mut io = lck.io_bytes(); + // Direct mode unavailable, use alsa-lib's mmap emulation instead + if write_samples_io( + &lck, + &mut streams.write().unwrap()[stream_id], + &mut io, + )? { + continue; + } + } + lck.get()? + } else { + drop(lck); + sleep(Duration::from_millis(500)); + continue; + } + }; + // Nothing to do, let's sleep until woken up by the kernel. + alsa::poll::poll(&mut fds, 100)?; + } + } + } +} + +impl AlsaBackend { + pub fn new(streams: Arc>>) -> Self { + let (sender, receiver) = channel(); + let sender = Arc::new(Mutex::new(sender)); + + thread::spawn(move || { + if let Err(err) = Self::run(streams, receiver) { + log::error!("Main thread exited with error: {}", err); + } + }); + + Self { sender } + } + + fn run( + streams: Arc>>, + receiver: Receiver, + ) -> std::result::Result<(), Box> { + let streams_no: usize; + + let (mut pcms, senders) = { + let lck = streams.read().unwrap(); + streams_no = lck.len(); + let mut vec = Vec::with_capacity(streams_no); + let mut senders = Vec::with_capacity(streams_no); + for i in 0..streams_no { + let (sender, receiver) = channel(); + let pcm = Arc::new(Mutex::new(PCM::new("default", Direction::Playback, false)?)); + + let mtx = Arc::clone(&pcm); + let streams = Arc::clone(&streams); + thread::spawn(move || { + // TODO: exponential backoff? send fatal error to daemon? + while let Err(err) = alsa_worker(mtx.clone(), streams.clone(), &receiver, i) { + log::error!( + "Worker thread exited with error: {}, sleeping for 500ms", + err + ); + sleep(Duration::from_millis(500)); + } + }); + + senders.push(sender); + vec.push(pcm); + } + (vec, senders) + }; + for (i, pcm) in pcms.iter_mut().enumerate() { + update_pcm(pcm, i, &streams)?; + } + + while let Ok(action) = receiver.recv() { + match action { + AlsaAction::Read(_) => {} + AlsaAction::Write(stream_id) => { + if stream_id >= streams_no { + log::error!( + "Received Write action for stream id {} but there are only {} PCM \ + streams.", + stream_id, + pcms.len() + ); + continue; + }; + if matches!( + streams.write().unwrap()[stream_id].state, + crate::stream::PCMState::Start | crate::stream::PCMState::Prepare + ) { + senders[stream_id].send(true).unwrap(); + } + } + AlsaAction::Start(stream_id) => { + if stream_id >= streams_no { + log::error!( + "Received Start action for stream id {} but there are only {} PCM \ + streams.", + stream_id, + pcms.len() + ); + continue; + }; + + let start_result = streams.write().unwrap()[stream_id].state.start(); + if let Err(err) = start_result { + log::error!("Stream {} start {}", stream_id, err); + } else { + let pcm = &pcms[stream_id]; + let lck = pcm.lock().unwrap(); + match lck.state() { + State::Running => {} + _ => lck.start()?, + } + } + } + AlsaAction::Stop(stream_id) => { + if stream_id >= streams_no { + log::error!( + "Received Stop action for stream id {} but there are only {} PCM \ + streams.", + stream_id, + pcms.len() + ); + continue; + }; + let stop_result = streams.write().unwrap()[stream_id].state.stop(); + if let Err(err) = stop_result { + log::error!("Stream {} stop {}", stream_id, err); + } + } + AlsaAction::Prepare(stream_id) => { + if stream_id >= streams_no { + log::error!( + "Received Prepare action for stream id {} but there are only {} PCM \ + streams.", + stream_id, + pcms.len() + ); + continue; + }; + let prepare_result = streams.write().unwrap()[stream_id].state.prepare(); + if let Err(err) = prepare_result { + log::error!("Stream {} prepare {}", stream_id, err); + } else { + let pcm = &pcms[stream_id]; + let lck = pcm.lock().unwrap(); + match lck.state() { + State::Running => {} + _ => lck.prepare()?, + } + } + } + AlsaAction::Release(stream_id, mut msg) => { + if stream_id >= streams_no { + log::error!( + "Received Release action for stream id {} but there are only {} PCM \ + streams.", + stream_id, + pcms.len() + ); + msg.code = VIRTIO_SND_S_BAD_MSG; + continue; + }; + let release_result = streams.write().unwrap()[stream_id].state.release(); + if let Err(err) = release_result { + log::error!("Stream {} release {}", stream_id, err); + msg.code = VIRTIO_SND_S_BAD_MSG; + } else { + senders[stream_id].send(false).unwrap(); + let mut streams = streams.write().unwrap(); + std::mem::take(&mut streams[stream_id].buffers); + } + } + AlsaAction::SetParameters(stream_id, mut msg) => { + if stream_id >= streams_no { + log::error!( + "Received SetParameters action for stream id {} but there are only {} \ + PCM streams.", + stream_id, + pcms.len() + ); + msg.code = VIRTIO_SND_S_BAD_MSG; + continue; + }; + let descriptors: Vec = msg.desc_chain.clone().collect(); + let desc_request = &descriptors[0]; + let request = msg + .desc_chain + .memory() + .read_obj::(desc_request.addr()) + .unwrap(); + { + let mut streams = streams.write().unwrap(); + let st = &mut streams[stream_id]; + if let Err(err) = st.state.set_parameters() { + log::error!("Stream {} set_parameters {}", stream_id, err); + msg.code = VIRTIO_SND_S_BAD_MSG; + continue; + } else if !st.supports_format(request.format) + || !st.supports_rate(request.rate) + { + msg.code = VIRTIO_SND_S_NOT_SUPP; + continue; + } else { + st.params.features = request.features; + st.params.buffer_bytes = request.buffer_bytes; + st.params.period_bytes = request.period_bytes; + st.params.channels = request.channels; + st.params.format = request.format; + st.params.rate = request.rate; + st.params.period_bytes = request.period_bytes; + st.params.buffer_bytes = request.buffer_bytes; + } + } + drop(msg); + update_pcm(&pcms[stream_id], stream_id, &streams)?; + } + } + } + + Ok(()) + } +} + +macro_rules! send_action { + ($($fn_name:ident $action:tt),+$(,)?) => { + $( + fn $fn_name(&self, id: u32) -> CrateResult<()> { + self.sender + .lock() + .unwrap() + .send(AlsaAction::$action(id as usize)) + .unwrap(); + Ok(()) + } + )* + }; + ($(ctrl $fn_name:ident $action:tt),+$(,)?) => { + $( + fn $fn_name(&self, id: u32, msg: ControlMessage) -> CrateResult<()> { + self.sender + .lock() + .unwrap() + .send(AlsaAction::$action(id as usize, msg)) + .unwrap(); + Ok(()) + } + )* + } +} + +impl AudioBackend for AlsaBackend { + send_action! { + write Write, + read Read, + prepare Prepare, + start Start, + stop Stop, + } + send_action! { + ctrl set_parameters SetParameters, + ctrl release Release, + } +}