From a66d733da8010c732979041cd602cfceab7f587b Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Tue, 18 Jul 2023 07:27:51 +0200 Subject: rust: support running Rust documentation tests as KUnit ones Rust has documentation tests: these are typically examples of usage of any item (e.g. function, struct, module...). They are very convenient because they are just written alongside the documentation. For instance: /// Sums two numbers. /// /// ``` /// assert_eq!(mymod::f(10, 20), 30); /// ``` pub fn f(a: i32, b: i32) -> i32 { a + b } In userspace, the tests are collected and run via `rustdoc`. Using the tool as-is would be useful already, since it allows to compile-test most tests (thus enforcing they are kept in sync with the code they document) and run those that do not depend on in-kernel APIs. However, by transforming the tests into a KUnit test suite, they can also be run inside the kernel. Moreover, the tests get to be compiled as other Rust kernel objects instead of targeting userspace. On top of that, the integration with KUnit means the Rust support gets to reuse the existing testing facilities. For instance, the kernel log would look like: KTAP version 1 1..1 KTAP version 1 # Subtest: rust_doctests_kernel 1..59 # rust_doctest_kernel_build_assert_rs_0.location: rust/kernel/build_assert.rs:13 ok 1 rust_doctest_kernel_build_assert_rs_0 # rust_doctest_kernel_build_assert_rs_1.location: rust/kernel/build_assert.rs:56 ok 2 rust_doctest_kernel_build_assert_rs_1 # rust_doctest_kernel_init_rs_0.location: rust/kernel/init.rs:122 ok 3 rust_doctest_kernel_init_rs_0 ... # rust_doctest_kernel_types_rs_2.location: rust/kernel/types.rs:150 ok 59 rust_doctest_kernel_types_rs_2 # rust_doctests_kernel: pass:59 fail:0 skip:0 total:59 # Totals: pass:59 fail:0 skip:0 total:59 ok 1 rust_doctests_kernel Therefore, add support for running Rust documentation tests in KUnit. Some other notes about the current implementation and support follow. The transformation is performed by a couple scripts written as Rust hostprogs. Tests using the `?` operator are also supported as usual, e.g.: /// ``` /// # use kernel::{spawn_work_item, workqueue}; /// spawn_work_item!(workqueue::system(), || pr_info!("x"))?; /// # Ok::<(), Error>(()) /// ``` The tests are also compiled with Clippy under `CLIPPY=1`, just like normal code, thus also benefitting from extra linting. The names of the tests are currently automatically generated. This allows to reduce the burden for documentation writers, while keeping them fairly stable for bisection. This is an improvement over the `rustdoc`-generated names, which include the line number; but ideally we would like to get `rustdoc` to provide the Rust item path and a number (for multiple examples in a single documented Rust item). In order for developers to easily see from which original line a failed doctests came from, a KTAP diagnostic line is printed to the log, containing the location (file and line) of the original test (i.e. instead of the location in the generated Rust file): # rust_doctest_kernel_types_rs_2.location: rust/kernel/types.rs:150 This line follows the syntax for declaring test metadata in the proposed KTAP v2 spec [1], which may be used for the proposed KUnit test attributes API [2]. Thus hopefully this will make migration easier later on (suggested by David [3]). The original line in that test attribute is figured out by providing an anchor (suggested by Boqun [4]). The original file is found by walking the filesystem, checking directory prefixes to reduce the amount of combinations to check, and it is only done once per file. Ambiguities are detected and reported. A notable difference from KUnit C tests is that the Rust tests appear to assert using the usual `assert!` and `assert_eq!` macros from the Rust standard library (`core`). We provide a custom version that forwards the call to KUnit instead. Importantly, these macros do not require passing context, unlike the KUnit C ones (i.e. `struct kunit *`). This makes them easier to use, and readers of the documentation do not need to care about which testing framework is used. In addition, it may allow us to test third-party code more easily in the future. However, a current limitation is that KUnit does not support assertions in other tasks. Thus we presently simply print an error to the kernel log if an assertion actually failed. This should be revisited to properly fail the test, perhaps saving the context somewhere else, or letting KUnit handle it. Link: https://lore.kernel.org/lkml/20230420205734.1288498-1-rmoar@google.com/ [1] Link: https://lore.kernel.org/linux-kselftest/20230707210947.1208717-1-rmoar@google.com/ [2] Link: https://lore.kernel.org/rust-for-linux/CABVgOSkOLO-8v6kdAGpmYnZUb+LKOX0CtYCo-Bge7r_2YTuXDQ@mail.gmail.com/ [3] Link: https://lore.kernel.org/rust-for-linux/ZIps86MbJF%2FiGIzd@boqun-archlinux/ [4] Signed-off-by: Miguel Ojeda Reviewed-by: David Gow Signed-off-by: Shuah Khan --- rust/helpers.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'rust/helpers.c') diff --git a/rust/helpers.c b/rust/helpers.c index bb594da56137..49a5e1a4f0ae 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -18,6 +18,7 @@ * accidentally exposed. */ +#include #include #include #include @@ -135,6 +136,12 @@ void rust_helper_put_task_struct(struct task_struct *t) } EXPORT_SYMBOL_GPL(rust_helper_put_task_struct); +struct kunit *rust_helper_kunit_get_current_test(void) +{ + return kunit_get_current_test(); +} +EXPORT_SYMBOL_GPL(rust_helper_kunit_get_current_test); + /* * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type * as the Rust `usize` type, so we can use it in contexts where Rust -- cgit v1.2.3-70-g09d2 From 917b2e00b90f7a0f21ab3f28e7cf165825a7aba7 Mon Sep 17 00:00:00 2001 From: Ariel Miculas Date: Wed, 26 Apr 2023 23:49:23 +0300 Subject: rust: helpers: sort includes alphabetically in rust/helpers.c Sort the #include directives of rust/helpers.c alphabetically and add a comment specifying this. The reason for this is to improve readability and to be consistent with the other files with a similar approach within 'rust/'. Suggested-by: Miguel Ojeda Link: https://github.com/Rust-for-Linux/linux/issues/1003 Signed-off-by: Ariel Miculas Reviewed-by: Martin Rodriguez Reboredo Reviewed-by: Gary Guo Link: https://lore.kernel.org/r/20230426204923.16195-1-amiculas@cisco.com Signed-off-by: Miguel Ojeda --- rust/helpers.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'rust/helpers.c') diff --git a/rust/helpers.c b/rust/helpers.c index bb594da56137..f946f2ea640a 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -16,16 +16,18 @@ * * All symbols are exported as GPL-only to guarantee no GPL-only feature is * accidentally exposed. + * + * Sorted alphabetically. */ #include #include #include #include -#include #include -#include +#include #include +#include #include __noreturn void rust_helper_BUG(void) -- cgit v1.2.3-70-g09d2 From 08ab786556ff177086ce93b26daf2a58edd10968 Mon Sep 17 00:00:00 2001 From: Aakash Sen Sharma Date: Tue, 13 Jun 2023 01:13:11 +0530 Subject: rust: bindgen: upgrade to 0.65.1 In LLVM 16, anonymous items may return names like `(unnamed union at ..)` rather than empty names [1], which breaks Rust-enabled builds because bindgen assumed an empty name instead of detecting them via `clang_Cursor_isAnonymous` [2]: $ make rustdoc LLVM=1 CLIPPY=1 -j$(nproc) RUSTC L rust/core.o BINDGEN rust/bindings/bindings_generated.rs BINDGEN rust/bindings/bindings_helpers_generated.rs BINDGEN rust/uapi/uapi_generated.rs thread 'main' panicked at '"ftrace_branch_data_union_(anonymous_at__/_/include/linux/compiler_types_h_146_2)" is not a valid Ident', .../proc-macro2-1.0.24/src/fallback.rs:693:9 ... thread 'main' panicked at '"ftrace_branch_data_union_(anonymous_at__/_/include/linux/compiler_types_h_146_2)" is not a valid Ident', .../proc-macro2-1.0.24/src/fallback.rs:693:9 ... This was fixed in bindgen 0.62.0. Therefore, upgrade bindgen to a more recent version, 0.65.1, to support LLVM 16. Since bindgen 0.58.0 changed the `--{white,black}list-*` flags to `--{allow,block}list-*` [3], update them on our side too. In addition, bindgen 0.61.0 moved its CLI utility into a binary crate called `bindgen-cli` [4]. Thus update the installation command in the Quick Start guide. Moreover, bindgen 0.61.0 changed the default functionality to bind `size_t` to `usize` [5] and added the `--no-size_t-is-usize` flag to not bind `size_t` as `usize`. Then bindgen 0.65.0 removed the `--size_t-is-usize` flag [6]. Thus stop passing the flag to bindgen. Finally, bindgen 0.61.0 added support for the `noreturn` attribute (in its different forms) [7]. Thus remove the infinite loop in our Rust panic handler after calling `BUG()`, since bindgen now correctly generates a `BUG()` binding that returns `!` instead of `()`. Link: https://github.com/llvm/llvm-project/commit/19e984ef8f49bc3ccced15621989fa9703b2cd5b [1] Link: https://github.com/rust-lang/rust-bindgen/pull/2319 [2] Link: https://github.com/rust-lang/rust-bindgen/pull/1990 [3] Link: https://github.com/rust-lang/rust-bindgen/pull/2284 [4] Link: https://github.com/rust-lang/rust-bindgen/commit/cc78b6fdb6e829e5fb8fa1639f2182cb49333569 [5] Link: https://github.com/rust-lang/rust-bindgen/pull/2408 [6] Link: https://github.com/rust-lang/rust-bindgen/issues/2094 [7] Signed-off-by: Aakash Sen Sharma Closes: https://github.com/Rust-for-Linux/linux/issues/1013 Tested-by: Ariel Miculas Reviewed-by: Gary Guo Link: https://lore.kernel.org/r/20230612194311.24826-1-aakashsensharma@gmail.com [ Reworded commit message. Mentioned the `bindgen-cli` binary crate change, linked to it and updated the Quick Start guide. Re-added a deleted "as" word in a code comment and reflowed comment to respect the maximum length. ] Signed-off-by: Miguel Ojeda --- Documentation/process/changes.rst | 2 +- Documentation/rust/quick-start.rst | 2 +- rust/Makefile | 6 +++--- rust/helpers.c | 15 +++++++-------- rust/kernel/lib.rs | 3 --- scripts/min-tool-version.sh | 2 +- 6 files changed, 13 insertions(+), 17 deletions(-) (limited to 'rust/helpers.c') diff --git a/Documentation/process/changes.rst b/Documentation/process/changes.rst index 1382bccc8818..0bbd040f6a55 100644 --- a/Documentation/process/changes.rst +++ b/Documentation/process/changes.rst @@ -32,7 +32,7 @@ you probably needn't concern yourself with pcmciautils. GNU C 5.1 gcc --version Clang/LLVM (optional) 11.0.0 clang --version Rust (optional) 1.71.1 rustc --version -bindgen (optional) 0.56.0 bindgen --version +bindgen (optional) 0.65.1 bindgen --version GNU make 3.82 make --version bash 4.2 bash --version binutils 2.25 ld -v diff --git a/Documentation/rust/quick-start.rst b/Documentation/rust/quick-start.rst index 58a183bb90b1..2cdf75eeae1d 100644 --- a/Documentation/rust/quick-start.rst +++ b/Documentation/rust/quick-start.rst @@ -98,7 +98,7 @@ the ``bindgen`` tool. A particular version is required. Install it via (note that this will download and build the tool from source):: - cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen + cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli ``bindgen`` needs to find a suitable ``libclang`` in order to work. If it is not found (or a different ``libclang`` than the one found should be used), diff --git a/rust/Makefile b/rust/Makefile index b278908c19e5..c3dfb3f6071b 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -300,7 +300,7 @@ quiet_cmd_bindgen = BINDGEN $@ $(BINDGEN) $< $(bindgen_target_flags) \ --use-core --with-derive-default --ctypes-prefix core::ffi --no-layout-tests \ --no-debug '.*' \ - --size_t-is-usize -o $@ -- $(bindgen_c_flags_final) -DMODULE \ + -o $@ -- $(bindgen_c_flags_final) -DMODULE \ $(bindgen_target_cflags) $(bindgen_target_extra) $(obj)/bindings/bindings_generated.rs: private bindgen_target_flags = \ @@ -320,8 +320,8 @@ $(obj)/uapi/uapi_generated.rs: $(src)/uapi/uapi_helper.h \ # given it is `libclang`; but for consistency, future Clang changes and/or # a potential future GCC backend for `bindgen`, we disable it too. $(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_flags = \ - --blacklist-type '.*' --whitelist-var '' \ - --whitelist-function 'rust_helper_.*' + --blocklist-type '.*' --allowlist-var '' \ + --allowlist-function 'rust_helper_.*' $(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_cflags = \ -I$(objtree)/$(obj) -Wno-missing-prototypes -Wno-missing-declarations $(obj)/bindings/bindings_helpers_generated.rs: private bindgen_target_extra = ; \ diff --git a/rust/helpers.c b/rust/helpers.c index f946f2ea640a..ebd69490127b 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -138,19 +138,18 @@ void rust_helper_put_task_struct(struct task_struct *t) EXPORT_SYMBOL_GPL(rust_helper_put_task_struct); /* - * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type - * as the Rust `usize` type, so we can use it in contexts where Rust - * expects a `usize` like slice (array) indices. `usize` is defined to be - * the same as C's `uintptr_t` type (can hold any pointer) but not - * necessarily the same as `size_t` (can hold the size of any single - * object). Most modern platforms use the same concrete integer type for + * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can + * use it in contexts where Rust expects a `usize` like slice (array) indices. + * `usize` is defined to be the same as C's `uintptr_t` type (can hold any + * pointer) but not necessarily the same as `size_t` (can hold the size of any + * single object). Most modern platforms use the same concrete integer type for * both of them, but in case we find ourselves on a platform where * that's not true, fail early instead of risking ABI or * integer-overflow issues. * * If your platform fails this assertion, it means that you are in - * danger of integer-overflow bugs (even if you attempt to remove - * `--size_t-is-usize`). It may be easiest to change the kernel ABI on + * danger of integer-overflow bugs (even if you attempt to add + * `--no-size_t-is-usize`). It may be easiest to change the kernel ABI on * your platform such that `size_t` matches `uintptr_t` (i.e., to increase * `size_t`, because `uintptr_t` has to be at least as big as `size_t`). */ diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 85b261209977..d59041ff5ff2 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -93,7 +93,4 @@ fn panic(info: &core::panic::PanicInfo<'_>) -> ! { pr_emerg!("{}\n", info); // SAFETY: FFI call. unsafe { bindings::BUG() }; - // Bindgen currently does not recognize `__noreturn` so `BUG` returns `()` - // instead of `!`. See . - loop {} } diff --git a/scripts/min-tool-version.sh b/scripts/min-tool-version.sh index 8da3b53fe36a..d65ab8bfeaf4 100755 --- a/scripts/min-tool-version.sh +++ b/scripts/min-tool-version.sh @@ -34,7 +34,7 @@ rustc) echo 1.71.1 ;; bindgen) - echo 0.56.0 + echo 0.65.1 ;; *) echo "$1: unknown tool" >&2 -- cgit v1.2.3-70-g09d2