Skip to content

Add XArray abstraction #1019

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 37 commits into
base: rust-dev
Choose a base branch
from
Draft
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
520a56a
rust: allocator: Prevents mis-aligned allocation
fbq Jun 13, 2023
3d8d9bc
rust: make `UnsafeCell` the outer type in `Opaque`
Darksonn Jun 14, 2023
7671527
rust: types: make `Opaque` be `!Unpin`
BennoLossin Jun 30, 2023
0ad204c
kbuild: rust_is_available: remove -v option
masahir0y Jun 16, 2023
70a61eb
kbuild: rust_is_available: fix version check when CC has multiple arg…
ruscur Jun 16, 2023
a250bde
docs: rust: add paragraph about finding a suitable `libclang`
ojeda Jun 16, 2023
00383f7
kbuild: rust_is_available: print docs reference
ojeda Jun 16, 2023
8b2e73a
kbuild: rust_is_available: add check for `bindgen` invocation
ojeda Jun 16, 2023
eb9199d
kbuild: rust_is_available: check that environment variables are set
ojeda Jun 16, 2023
6bed841
kbuild: rust_is_available: fix confusion when a version appears in th…
ojeda Jun 16, 2023
600eb66
kbuild: rust_is_available: normalize version matching
ojeda Jun 16, 2023
6d4220b
kbuild: rust_is_available: handle failures calling `$RUSTC`/`$BINDGEN`
ojeda Jun 16, 2023
10452fc
kbuild: rust_is_available: check that output looks as expected
ojeda Jun 16, 2023
6a4e6dc
kbuild: rust_is_available: add test suite
ojeda Jun 16, 2023
f2f2787
rust: alloc: Add realloc and alloc_zeroed to the GlobalAlloc impl
fbq Jun 25, 2023
53dd54f
rust: bindgen: upgrade to 0.65.1
Shinyzenith Jun 12, 2023
02ddf5a
rust: init: make doctests compilable/testable
ojeda Jun 14, 2023
276f14d
rust: str: make doctests compilable/testable
ojeda Jun 14, 2023
c30db85
rust: sync: make doctests compilable/testable
ojeda Jun 14, 2023
4009de1
rust: types: make doctests compilable/testable
ojeda Jun 14, 2023
e9e95cd
rust: support running Rust documentation tests as KUnit ones
ojeda Jun 14, 2023
eff7a66
MAINTAINERS: add Rust KUnit files to the KUnit entry
ojeda Jun 14, 2023
72fbe4a
rust: macros: fix redefine const_name in `vtable`
cqs21 Jun 26, 2023
8e87a0d
rust: macros: add `paste!` proc macro
nbdd0121 Jun 28, 2023
7c6e564
arm64: rust: Enable Rust support for AArch64
JamieCunliffe Jun 6, 2023
522ba05
arm64: rust: Enable PAC support for Rust.
JamieCunliffe Jun 6, 2023
b1555e1
arm64: Restrict Rust support to little endian only.
JamieCunliffe Jun 6, 2023
19b33fc
rust: bindgen: update installation instructions
matthewtgilbride Jun 29, 2023
c1c510f
rust: xarray: Add an abstraction for XArray
asahilina Jun 27, 2023
91f4147
rust: xarray: add rudimentary doctest
matthewtgilbride Jun 27, 2023
6c19acb
rust: xarray: add `get_mutable` and `find_mut`
matthewtgilbride Jun 27, 2023
3016eac
rust: xarray: add reservation mechanism
asahilina Jun 27, 2023
90ddba2
Why does this cuase NULL pointer dereference?
matthewtgilbride Jul 2, 2023
702746d
Just me thrashing about - figuring out NPE
matthewtgilbride Jul 4, 2023
976067b
rust: delete `ForeignOwnable::borrow_mut`
Darksonn Jul 6, 2023
ac520c2
rust: add improved version of `ForeignOwnable::borrow_mut`
Darksonn Jul 10, 2023
642ff49
rust: xarray: figure out pin and get_scoped
matthewtgilbride Jul 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Documentation/process/changes.rst
Original file line number Diff line number Diff line change
@@ -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.68.2 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
1 change: 1 addition & 0 deletions Documentation/rust/arch-support.rst
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ support corresponds to ``S`` values in the ``MAINTAINERS`` file.
============ ================ ==============================================
Architecture Level of support Constraints
============ ================ ==============================================
``arm64`` Maintained Little Endian only.
``um`` Maintained ``x86_64`` only.
``x86`` Maintained ``x86_64`` only.
============ ================ ==============================================
19 changes: 18 additions & 1 deletion Documentation/rust/quick-start.rst
Original file line number Diff line number Diff line change
@@ -98,7 +98,24 @@ 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also discovered this yesterday. Could you send this patch directly to the mailing list?

@ojeda probably should take this as a fix.

Copy link
Member

@ojeda ojeda Jun 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is https://rust-for-linux.zulipchat.com/#narrow/stream/291565-Help/topic/Build.20errors/near/365685550 (applied at the rebased ojeda@698129c), i.e. I was going to fix it directly when we take the upgrade to 0.65.1.


``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),
the process can be tweaked using the environment variables understood by
``clang-sys`` (the Rust bindings crate that ``bindgen`` uses to access
``libclang``):

* ``LLVM_CONFIG_PATH`` can be pointed to an ``llvm-config`` executable.

* Or ``LIBCLANG_PATH`` can be pointed to a ``libclang`` shared library
or to the directory containing it.

* Or ``CLANG_PATH`` can be pointed to a ``clang`` executable.

For details, please see ``clang-sys``'s documentation at:

https://github.com/KyleMayes/clang-sys#environment-variables


Requirements: Developing
2 changes: 2 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
@@ -11323,6 +11323,8 @@ W: https://google.github.io/kunit-docs/third_party/kernel/docs/
F: Documentation/dev-tools/kunit/
F: include/kunit/
F: lib/kunit/
F: rust/kernel/kunit.rs
F: scripts/rustdoc_test_*
F: tools/testing/kunit/

KERNEL USERMODE HELPER
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -561,7 +561,6 @@ KBUILD_CFLAGS := -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs \
-std=gnu11
KBUILD_CPPFLAGS := -D__KERNEL__
KBUILD_RUSTFLAGS := $(rust_common_flags) \
--target=$(objtree)/scripts/target.json \
-Cpanic=abort -Cembed-bitcode=n -Clto=n \
-Cforce-unwind-tables=n -Ccodegen-units=1 \
-Csymbol-mangling-version=v0 \
@@ -1289,7 +1288,7 @@ prepare0: archprepare
# All the preparing..
prepare: prepare0
ifdef CONFIG_RUST
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/rust_is_available.sh -v
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/rust_is_available.sh
$(Q)$(MAKE) $(build)=rust
endif

@@ -1823,7 +1822,7 @@ $(DOC_TARGETS):
# "Is Rust available?" target
PHONY += rustavailable
rustavailable:
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/rust_is_available.sh -v && echo "Rust is available!"
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/rust_is_available.sh && echo "Rust is available!"

# Documentation target
#
1 change: 1 addition & 0 deletions arch/arm64/Kconfig
Original file line number Diff line number Diff line change
@@ -217,6 +217,7 @@ config ARM64
select HAVE_FUNCTION_ARG_ACCESS_API
select MMU_GATHER_RCU_TABLE_FREE
select HAVE_RSEQ
select HAVE_RUST if CPU_LITTLE_ENDIAN
select HAVE_STACKPROTECTOR
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_KPROBES
4 changes: 4 additions & 0 deletions arch/arm64/Makefile
Original file line number Diff line number Diff line change
@@ -41,6 +41,8 @@ KBUILD_CFLAGS += -mgeneral-regs-only \
KBUILD_CFLAGS += $(call cc-disable-warning, psabi)
KBUILD_AFLAGS += $(compat_vdso)

KBUILD_RUSTFLAGS += --target aarch64-unknown-none -C target-feature="-neon,-fp-armv8"

KBUILD_CFLAGS += $(call cc-option,-mabi=lp64)
KBUILD_AFLAGS += $(call cc-option,-mabi=lp64)

@@ -65,7 +67,9 @@ endif

ifeq ($(CONFIG_ARM64_BTI_KERNEL),y)
KBUILD_CFLAGS += -mbranch-protection=pac-ret+bti
KBUILD_RUSTFLAGS += -Z branch-protection=bti,pac-ret
else ifeq ($(CONFIG_ARM64_PTR_AUTH_KERNEL),y)
KBUILD_RUSTFLAGS += -Z branch-protection=pac-ret
ifeq ($(CONFIG_CC_HAS_BRANCH_PROT_PAC_RET),y)
KBUILD_CFLAGS += -mbranch-protection=pac-ret
else
1 change: 1 addition & 0 deletions arch/x86/Makefile
Original file line number Diff line number Diff line change
@@ -68,6 +68,7 @@ export BITS
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53383
#
KBUILD_CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx
KBUILD_RUSTFLAGS += --target=$(objtree)/scripts/target.json
KBUILD_RUSTFLAGS += -Ctarget-feature=-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2

ifeq ($(CONFIG_X86_KERNEL_IBT),y)
13 changes: 13 additions & 0 deletions lib/Kconfig.debug
Original file line number Diff line number Diff line change
@@ -2911,6 +2911,19 @@ config RUST_BUILD_ASSERT_ALLOW

If unsure, say N.

config RUST_KERNEL_KUNIT_TEST
bool "KUnit test for the `kernel` crate" if !KUNIT_ALL_TESTS
depends on RUST && KUNIT=y
default KUNIT_ALL_TESTS
help
This builds the documentation tests of the `kernel` crate
as KUnit tests.

For more information on KUnit and unit tests in general,
please refer to the KUnit documentation in Documentation/dev-tools/kunit/.

If unsure, say N.

endmenu # "Rust"

endmenu # Kernel hacking
2 changes: 2 additions & 0 deletions rust/.gitignore
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@

bindings_generated.rs
bindings_helpers_generated.rs
doctests_kernel_generated.rs
doctests_kernel_generated_kunit.c
uapi_generated.rs
exports_*_generated.h
doc/
41 changes: 37 additions & 4 deletions rust/Makefile
Original file line number Diff line number Diff line change
@@ -27,6 +27,12 @@ endif

obj-$(CONFIG_RUST) += exports.o

always-$(CONFIG_RUST_KERNEL_KUNIT_TEST) += doctests_kernel_generated.rs
always-$(CONFIG_RUST_KERNEL_KUNIT_TEST) += doctests_kernel_generated_kunit.c

obj-$(CONFIG_RUST_KERNEL_KUNIT_TEST) += doctests_kernel_generated.o
obj-$(CONFIG_RUST_KERNEL_KUNIT_TEST) += doctests_kernel_generated_kunit.o

# Avoids running `$(RUSTC)` for the sysroot when it may not be available.
ifdef CONFIG_RUST

@@ -39,9 +45,11 @@ ifeq ($(quiet),silent_)
cargo_quiet=-q
rust_test_quiet=-q
rustdoc_test_quiet=--test-args -q
rustdoc_test_kernel_quiet=>/dev/null
else ifeq ($(quiet),quiet_)
rust_test_quiet=-q
rustdoc_test_quiet=--test-args -q
rustdoc_test_kernel_quiet=>/dev/null
else
cargo_quiet=--verbose
endif
@@ -157,6 +165,27 @@ quiet_cmd_rustdoc_test = RUSTDOC T $<
-L$(objtree)/$(obj)/test --output $(objtree)/$(obj)/doc \
--crate-name $(subst rusttest-,,$@) $<

quiet_cmd_rustdoc_test_kernel = RUSTDOC TK $<
cmd_rustdoc_test_kernel = \
rm -rf $(objtree)/$(obj)/test/doctests/kernel; \
mkdir -p $(objtree)/$(obj)/test/doctests/kernel; \
OBJTREE=$(abspath $(objtree)) \
$(RUSTDOC) --test $(rust_flags) \
@$(objtree)/include/generated/rustc_cfg \
-L$(objtree)/$(obj) --extern alloc --extern kernel \
--extern build_error --extern macros \
--extern bindings --extern uapi \
--no-run --crate-name kernel -Zunstable-options \
--test-builder $(objtree)/scripts/rustdoc_test_builder \
$< $(rustdoc_test_kernel_quiet); \
$(objtree)/scripts/rustdoc_test_gen

%/doctests_kernel_generated.rs %/doctests_kernel_generated_kunit.c: \
$(src)/kernel/lib.rs $(obj)/kernel.o \
$(objtree)/scripts/rustdoc_test_builder \
$(objtree)/scripts/rustdoc_test_gen FORCE
$(call if_changed,rustdoc_test_kernel)

# We cannot use `-Zpanic-abort-tests` because some tests are dynamic,
# so for the moment we skip `-Cpanic=abort`.
quiet_cmd_rustc_test = RUSTC T $<
@@ -262,6 +291,7 @@ bindgen_skip_c_flags := -mno-fp-ret-in-387 -mpreferred-stack-boundary=% \

# Derived from `scripts/Makefile.clang`.
BINDGEN_TARGET_x86 := x86_64-linux-gnu
BINDGEN_TARGET_arm64 := aarch64-linux-gnu
BINDGEN_TARGET := $(BINDGEN_TARGET_$(SRCARCH))

# All warnings are inhibited since GCC builds are very experimental,
@@ -300,7 +330,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 +350,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 = ; \
@@ -393,8 +423,11 @@ $(obj)/core.o: private skip_clippy = 1
$(obj)/core.o: private skip_flags = -Dunreachable_pub
$(obj)/core.o: private rustc_objcopy = $(foreach sym,$(redirect-intrinsics),--redefine-sym $(sym)=__rust$(sym))
$(obj)/core.o: private rustc_target_flags = $(core-cfgs)
$(obj)/core.o: $(RUST_LIB_SRC)/core/src/lib.rs scripts/target.json FORCE
$(obj)/core.o: $(RUST_LIB_SRC)/core/src/lib.rs FORCE
$(call if_changed_dep,rustc_library)
ifeq ($(ARCH),x86_64)
$(obj)/core.o: scripts/target.json
endif

$(obj)/compiler_builtins.o: private rustc_objcopy = -w -W '__*'
$(obj)/compiler_builtins.o: $(src)/compiler_builtins.rs $(obj)/core.o FORCE
19 changes: 19 additions & 0 deletions rust/bindings/bindings_helper.h
Original file line number Diff line number Diff line change
@@ -6,12 +6,31 @@
* Sorted alphabetically.
*/

#include <kunit/test.h>
#include <linux/errname.h>
#include <linux/slab.h>
#include <linux/refcount.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/xarray.h>

/* `bindgen` gets confused at certain things. */
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO;
const size_t BINDINGS_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN;

const gfp_t BINDINGS_XA_FLAGS_LOCK_IRQ = XA_FLAGS_LOCK_IRQ;
const gfp_t BINDINGS_XA_FLAGS_LOCK_BH = XA_FLAGS_LOCK_BH;
const gfp_t BINDINGS_XA_FLAGS_TRACK_FREE = XA_FLAGS_TRACK_FREE;
const gfp_t BINDINGS_XA_FLAGS_ZERO_BUSY = XA_FLAGS_ZERO_BUSY;
const gfp_t BINDINGS_XA_FLAGS_ALLOC_WRAPPED = XA_FLAGS_ALLOC_WRAPPED;
const gfp_t BINDINGS_XA_FLAGS_ACCOUNT = XA_FLAGS_ACCOUNT;
const gfp_t BINDINGS_XA_FLAGS_ALLOC = XA_FLAGS_ALLOC;
const gfp_t BINDINGS_XA_FLAGS_ALLOC1 = XA_FLAGS_ALLOC1;

const xa_mark_t BINDINGS_XA_MARK_0 = XA_MARK_0;
const xa_mark_t BINDINGS_XA_MARK_1 = XA_MARK_1;
const xa_mark_t BINDINGS_XA_MARK_2 = XA_MARK_2;
const xa_mark_t BINDINGS_XA_PRESENT = XA_PRESENT;
const xa_mark_t BINDINGS_XA_MARK_MAX = XA_MARK_MAX;
const xa_mark_t BINDINGS_XA_FREE_MARK = XA_FREE_MARK;
57 changes: 50 additions & 7 deletions rust/helpers.c
Original file line number Diff line number Diff line change
@@ -18,6 +18,8 @@
* accidentally exposed.
*/

#include <linux/stddef.h>
#include <kunit/test-bug.h>
#include <linux/bug.h>
#include <linux/build_bug.h>
#include <linux/err.h>
@@ -27,6 +29,7 @@
#include <linux/spinlock.h>
#include <linux/sched/signal.h>
#include <linux/wait.h>
#include <linux/xarray.h>

__noreturn void rust_helper_BUG(void)
{
@@ -135,20 +138,60 @@ 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);
void rust_helper_xa_init_flags(struct xarray *xa, gfp_t flags)
{
xa_init_flags(xa, flags);
}
EXPORT_SYMBOL_GPL(rust_helper_xa_init_flags);

bool rust_helper_xa_empty(struct xarray *xa)
{
return xa_empty(xa);
}
EXPORT_SYMBOL_GPL(rust_helper_xa_empty);

int rust_helper_xa_alloc(struct xarray *xa, u32 *id, void *entry, struct xa_limit limit, gfp_t gfp)
{
return xa_alloc(xa, id, entry, limit, gfp);
}
EXPORT_SYMBOL_GPL(rust_helper_xa_alloc);

void rust_helper_xa_lock(struct xarray *xa)
{
xa_lock(xa);
}
EXPORT_SYMBOL_GPL(rust_helper_xa_lock);

void rust_helper_xa_unlock(struct xarray *xa)
{
xa_unlock(xa);
}
EXPORT_SYMBOL_GPL(rust_helper_xa_unlock);

int rust_helper_xa_err(void *entry)
{
return xa_err(entry);
}
EXPORT_SYMBOL_GPL(rust_helper_xa_err);

/*
* 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
* `bindgen` binds the C `size_t` type 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`).
*/
70 changes: 67 additions & 3 deletions rust/kernel/allocator.rs
Original file line number Diff line number Diff line change
@@ -9,18 +9,82 @@ use crate::bindings;

struct KernelAllocator;

impl KernelAllocator {
/// # Safety
///
/// * `ptr` can be either null or a pointer which has been allocated by this allocator.
/// * `layout` must have a non-zero size.
unsafe fn krealloc_with_flags(
&self,
ptr: *mut u8,
layout: Layout,
flags: bindings::gfp_t,
) -> *mut u8 {
// Customized layouts from `Layout::from_size_align()` can have size < align, so pads first.
let layout = layout.pad_to_align();

let mut size = layout.size();

if layout.align() > bindings::BINDINGS_ARCH_SLAB_MINALIGN {
// The alignment requirement exceeds the slab guarantee, then tries to enlarges the size
// to use the "power-of-two" size/alignment guarantee (see comments in kmalloc() for
// more information).
//
// Note that `layout.size()` (after padding) is guaranteed to be muliples of
// `layout.align()`, so `next_power_of_two` gives enough alignment guarantee.
size = size.next_power_of_two();
}

// SAFETY:
//
// * `ptr` is either null or a pointer returned from a previous k{re}alloc() by the function
// safety requirement.
//
// * `size` is greater than 0 since it's either a `layout.size()` (which cannot be zero
// according to the function safety requirement) or a result from `next_power_of_two()`.
unsafe { bindings::krealloc(ptr as *const core::ffi::c_void, size, flags) as *mut u8 }
}
}

unsafe impl GlobalAlloc for KernelAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// `krealloc()` is used instead of `kmalloc()` because the latter is
// an inline function and cannot be bound to as a result.
unsafe { bindings::krealloc(ptr::null(), layout.size(), bindings::GFP_KERNEL) as *mut u8 }
// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
// requirement.
unsafe { self.krealloc_with_flags(ptr::null_mut(), layout, bindings::GFP_KERNEL) }
}

unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
unsafe {
bindings::kfree(ptr as *const core::ffi::c_void);
}
}

unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
// SAFETY:
// * `new_size` when rounded up to the nearest multiple of `layout.align()`, will not
// overflow `isize` by the function safety requirement.
// * `layout.align()` is a proper alignment (i.e. not zero and must be a power of two).
let layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };

// SAFETY:
// * `ptr` is either null or a pointer allocated by this allocator by function safety
// requirement.
// * the size of `layout` is not zero because `new_size` is not zero by function safety
// requirement.
unsafe { self.krealloc_with_flags(ptr, layout, bindings::GFP_KERNEL) }
}

unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
// SAFETY: `ptr::null_mut()` is null and `layout` has a non-zero size by the function safety
// requirement.
unsafe {
self.krealloc_with_flags(
ptr::null_mut(),
layout,
bindings::GFP_KERNEL | bindings::__GFP_ZERO,
)
}
}
}

#[global_allocator]
25 changes: 16 additions & 9 deletions rust/kernel/init.rs
Original file line number Diff line number Diff line change
@@ -120,14 +120,23 @@
//! `slot` gets called.
//!
//! ```rust
//! use kernel::{prelude::*, init};
//! # #![allow(unreachable_pub, clippy::disallowed_names)]
//! use kernel::{prelude::*, init, types::Opaque};
//! use core::{ptr::addr_of_mut, marker::PhantomPinned, pin::Pin};
//! # mod bindings {
//! # #![allow(non_camel_case_types)]
//! # pub struct foo;
//! # pub unsafe fn init_foo(_ptr: *mut foo) {}
//! # pub unsafe fn destroy_foo(_ptr: *mut foo) {}
//! # pub unsafe fn enable_foo(_ptr: *mut foo, _flags: u32) -> i32 { 0 }
//! # }
//! # trait FromErrno {
//! # fn from_errno(errno: core::ffi::c_int) -> Error {
//! # // Dummy error that can be constructed outside the `kernel` crate.
//! # Error::from(core::fmt::Error)
//! # }
//! # }
//! # impl FromErrno for Error {}
//! /// # Invariants
//! ///
//! /// `foo` is always initialized
@@ -158,7 +167,7 @@
//! if err != 0 {
//! // Enabling has failed, first clean up the foo and then return the error.
//! bindings::destroy_foo(Opaque::raw_get(foo));
//! return Err(Error::from_kernel_errno(err));
//! return Err(Error::from_errno(err));
//! }
//!
//! // All fields of `RawFoo` have been initialized, since `_p` is a ZST.
@@ -226,8 +235,7 @@ pub mod macros;
///
/// ```rust
/// # #![allow(clippy::disallowed_names, clippy::new_ret_no_self)]
/// # use kernel::{init, pin_init, stack_pin_init, init::*, sync::Mutex, new_mutex};
/// # use macros::pin_data;
/// # use kernel::{init, macros::pin_data, pin_init, stack_pin_init, init::*, sync::Mutex, new_mutex};
/// # use core::pin::Pin;
/// #[pin_data]
/// struct Foo {
@@ -277,7 +285,7 @@ macro_rules! stack_pin_init {
///
/// # Examples
///
/// ```rust
/// ```rust,ignore
/// # #![allow(clippy::disallowed_names, clippy::new_ret_no_self)]
/// # use kernel::{init, pin_init, stack_try_pin_init, init::*, sync::Mutex, new_mutex};
/// # use macros::pin_data;
@@ -303,7 +311,7 @@ macro_rules! stack_pin_init {
/// pr_info!("a: {}", &*foo.a.lock());
/// ```
///
/// ```rust
/// ```rust,ignore
/// # #![allow(clippy::disallowed_names, clippy::new_ret_no_self)]
/// # use kernel::{init, pin_init, stack_try_pin_init, init::*, sync::Mutex, new_mutex};
/// # use macros::pin_data;
@@ -513,8 +521,7 @@ macro_rules! stack_try_pin_init {
/// For instance:
///
/// ```rust
/// # use kernel::pin_init;
/// # use macros::pin_data;
/// # use kernel::{macros::pin_data, pin_init};
/// # use core::{ptr::addr_of_mut, marker::PhantomPinned};
/// #[pin_data]
/// struct Buf {
@@ -841,7 +848,7 @@ macro_rules! init {
/// # Examples
///
/// ```rust
/// use kernel::{init::PinInit, error::Error, InPlaceInit};
/// use kernel::{init::{PinInit, zeroed}, error::Error};
/// struct BigBuf {
/// big: Box<[u8; 1024 * 1024 * 1024]>,
/// small: [u8; 1024 * 1024],
156 changes: 156 additions & 0 deletions rust/kernel/kunit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: GPL-2.0

//! KUnit-based macros for Rust unit tests.
//!
//! C header: [`include/kunit/test.h`](../../../../../include/kunit/test.h)
//!
//! Reference: <https://docs.kernel.org/dev-tools/kunit/index.html>
use core::{ffi::c_void, fmt};

/// Prints a KUnit error.
///
/// Public but hidden since it should only be used from KUnit generated code.
#[doc(hidden)]
pub fn err(args: fmt::Arguments<'_>) {
// SAFETY: The format string is null-terminated and the `%pA` specifier matches the argument we
// are passing.
#[cfg(CONFIG_PRINTK)]
unsafe {
bindings::_printk(
b"\x013%pA\0".as_ptr() as _,
&args as *const _ as *const c_void,
);
}
}

/// Prints a KUnit error.
///
/// Public but hidden since it should only be used from KUnit generated code.
#[doc(hidden)]
pub fn info(args: fmt::Arguments<'_>) {
// SAFETY: The format string is null-terminated and the `%pA` specifier matches the argument we
// are passing.
#[cfg(CONFIG_PRINTK)]
unsafe {
bindings::_printk(
b"\x016%pA\0".as_ptr() as _,
&args as *const _ as *const c_void,
);
}
}

/// Asserts that a boolean expression is `true` at runtime.
///
/// Public but hidden since it should only be used from generated tests.
///
/// Unlike the one in `core`, this one does not panic; instead, it is mapped to the KUnit
/// facilities. See [`assert!`] for more details.
#[doc(hidden)]
#[macro_export]
macro_rules! kunit_assert {
($name:literal, $condition:expr $(,)?) => {
'out: {
// Do nothing if the condition is `true`.
if $condition {
break 'out;
}

static LINE: i32 = core::line!() as i32;
static FILE: &'static $crate::str::CStr = $crate::c_str!(core::file!());
static CONDITION: &'static $crate::str::CStr = $crate::c_str!(stringify!($condition));

// SAFETY: FFI call without safety requirements.
let kunit_test = unsafe { $crate::bindings::kunit_get_current_test() };
if kunit_test.is_null() {
// The assertion failed but this task is not running a KUnit test, so we cannot call
// KUnit, but at least print an error to the kernel log. This may happen if this
// macro is called from an spawned thread in a test (see
// `scripts/rustdoc_test_gen.rs`) or if some non-test code calls this macro by
// mistake (it is hidden to prevent that).
//
// This mimics KUnit's failed assertion format.
$crate::kunit::err(format_args!(
" # {}: ASSERTION FAILED at {FILE}:{LINE}\n",
$name
));
$crate::kunit::err(format_args!(
" Expected {CONDITION} to be true, but is false\n"
));
$crate::kunit::err(format_args!(
" Failure not reported to KUnit since this is a non-KUnit task\n"
));
break 'out;
}

#[repr(transparent)]
struct Location($crate::bindings::kunit_loc);

#[repr(transparent)]
struct UnaryAssert($crate::bindings::kunit_unary_assert);

// SAFETY: There is only a static instance and in that one the pointer field points to
// an immutable C string.
unsafe impl Sync for Location {}

// SAFETY: There is only a static instance and in that one the pointer field points to
// an immutable C string.
unsafe impl Sync for UnaryAssert {}

static LOCATION: Location = Location($crate::bindings::kunit_loc {
file: FILE.as_char_ptr(),
line: LINE,
});
static ASSERTION: UnaryAssert = UnaryAssert($crate::bindings::kunit_unary_assert {
assert: $crate::bindings::kunit_assert {},
condition: CONDITION.as_char_ptr(),
expected_true: true,
});

// SAFETY:
// - FFI call.
// - The `kunit_test` pointer is valid because we got it from
// `kunit_get_current_test()` and it was not null. This means we are in a KUnit
// test, and that the pointer can be passed to KUnit functions and assertions.
// - The string pointers (`file` and `condition` above) point to null-terminated
// strings since they are `CStr`s.
// - The function pointer (`format`) points to the proper function.
// - The pointers passed will remain valid since they point to `static`s.
// - The format string is allowed to be null.
// - There are, however, problems with this: first of all, this will end up stopping
// the thread, without running destructors. While that is problematic in itself,
// it is considered UB to have what is effectively a forced foreign unwind
// with `extern "C"` ABI. One could observe the stack that is now gone from
// another thread. We should avoid pinning stack variables to prevent library UB,
// too. For the moment, given that test failures are reported immediately before the
// next test runs, that test failures should be fixed and that KUnit is explicitly
// documented as not suitable for production environments, we feel it is reasonable.
unsafe {
$crate::bindings::kunit_do_failed_assertion(
kunit_test,
core::ptr::addr_of!(LOCATION.0),
$crate::bindings::kunit_assert_type_KUNIT_ASSERTION,
core::ptr::addr_of!(ASSERTION.0.assert),
Some($crate::bindings::kunit_unary_assert_format),
core::ptr::null(),
);
}
}
};
}

/// Asserts that two expressions are equal to each other (using [`PartialEq`]).
///
/// Public but hidden since it should only be used from generated tests.
///
/// Unlike the one in `core`, this one does not panic; instead, it is mapped to the KUnit
/// facilities. See [`assert!`] for more details.
#[doc(hidden)]
#[macro_export]
macro_rules! kunit_assert_eq {
($name:literal, $left:expr, $right:expr $(,)?) => {{
// For the moment, we just forward to the expression assert because, for binary asserts,
// KUnit supports only a few types (e.g. integers).
$crate::kunit_assert!($name, $left == $right);
}};
}
6 changes: 3 additions & 3 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
@@ -34,6 +34,8 @@ mod build_assert;
pub mod error;
pub mod init;
pub mod ioctl;
#[cfg(CONFIG_KUNIT)]
pub mod kunit;
pub mod prelude;
pub mod print;
mod static_assert;
@@ -43,6 +45,7 @@ pub mod str;
pub mod sync;
pub mod task;
pub mod types;
pub mod xarray;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not urgent, but should we introduce a collections module and make xarray a submod of it?


#[doc(hidden)]
pub use bindings;
@@ -93,7 +96,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 <https://github.com/rust-lang/rust-bindgen/issues/2094>.
loop {}
}
4 changes: 3 additions & 1 deletion rust/kernel/str.rs
Original file line number Diff line number Diff line change
@@ -213,6 +213,7 @@ impl fmt::Display for CStr {
///
/// ```
/// # use kernel::c_str;
/// # use kernel::fmt;
/// # use kernel::str::CStr;
/// # use kernel::str::CString;
/// let penguin = c_str!("🐧");
@@ -241,6 +242,7 @@ impl fmt::Debug for CStr {
///
/// ```
/// # use kernel::c_str;
/// # use kernel::fmt;
/// # use kernel::str::CStr;
/// # use kernel::str::CString;
/// let penguin = c_str!("🐧");
@@ -529,7 +531,7 @@ impl fmt::Write for Formatter {
/// # Examples
///
/// ```
/// use kernel::str::CString;
/// use kernel::{str::CString, fmt};
///
/// let s = CString::try_from_fmt(fmt!("{}{}{}", "abc", 10, 20)).unwrap();
/// assert_eq!(s.as_bytes_with_nul(), "abc1020\0".as_bytes());
35 changes: 24 additions & 11 deletions rust/kernel/sync/arc.rs
Original file line number Diff line number Diff line change
@@ -73,6 +73,7 @@ mod std_vendor;
/// assert_eq!(cloned.b, 20);
///
/// // The refcount drops to zero when `cloned` goes out of scope, and the memory is freed.
/// # Ok::<(), Error>(())
/// ```
///
/// Using `Arc<T>` as the type of `self`:
@@ -98,6 +99,7 @@ mod std_vendor;
/// let obj = Arc::try_new(Example { a: 10, b: 20 })?;
/// obj.use_reference();
/// obj.take_over();
/// # Ok::<(), Error>(())
/// ```
///
/// Coercion from `Arc<Example>` to `Arc<dyn MyTrait>`:
@@ -121,6 +123,7 @@ mod std_vendor;
///
/// // `coerced` has type `Arc<dyn MyTrait>`.
/// let coerced: Arc<dyn MyTrait> = obj;
/// # Ok::<(), Error>(())
/// ```
pub struct Arc<T: ?Sized> {
ptr: NonNull<ArcInner<T>>,
@@ -232,27 +235,35 @@ impl<T: ?Sized> Arc<T> {

impl<T: 'static> ForeignOwnable for Arc<T> {
type Borrowed<'a> = ArcBorrow<'a, T>;
// Mutable access to the `Arc` does not give any extra abilities over
// immutable access.
type BorrowedMut<'a> = ArcBorrow<'a, T>;

fn into_foreign(self) -> *const core::ffi::c_void {
ManuallyDrop::new(self).ptr.as_ptr() as _
}

unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self {
// SAFETY: By the safety requirement of this function, we know that `ptr` came from
// a previous call to `Arc::into_foreign`, which guarantees that `ptr` is valid and
// holds a reference count increment that is transferrable to us.
unsafe { Self::from_inner(NonNull::new_unchecked(ptr as _)) }
}

unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> ArcBorrow<'a, T> {
// SAFETY: By the safety requirement of this function, we know that `ptr` came from
// a previous call to `Arc::into_foreign`.
let inner = NonNull::new(ptr as *mut ArcInner<T>).unwrap();
let inner = unsafe { NonNull::new_unchecked(ptr as *mut ArcInner<T>) };

// SAFETY: The safety requirements of `from_foreign` ensure that the object remains alive
// for the lifetime of the returned value. Additionally, the safety requirements of
// `ForeignOwnable::borrow_mut` ensure that no new mutable references are created.
// SAFETY: The safety requirements ensure that we will not give up our
// foreign-owned refcount while the `ArcBorrow` is still live.
unsafe { ArcBorrow::new(inner) }
}

unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self {
// SAFETY: By the safety requirement of this function, we know that `ptr` came from
// a previous call to `Arc::into_foreign`, which guarantees that `ptr` is valid and
// holds a reference count increment that is transferrable to us.
unsafe { Self::from_inner(NonNull::new(ptr as _).unwrap()) }
unsafe fn borrow_mut<'a>(ptr: *const core::ffi::c_void) -> ArcBorrow<'a, T> {
// SAFETY: The safety requirements for `borrow_mut` are a superset of the safety
// requirements for `borrow`.
unsafe { Self::borrow(ptr) }
}
}

@@ -337,7 +348,7 @@ impl<T: ?Sized> From<Pin<UniqueArc<T>>> for Arc<T> {
/// # Example
///
/// ```
/// use crate::sync::{Arc, ArcBorrow};
/// use kernel::sync::{Arc, ArcBorrow};
///
/// struct Example;
///
@@ -350,12 +361,13 @@ impl<T: ?Sized> From<Pin<UniqueArc<T>>> for Arc<T> {
///
/// // Assert that both `obj` and `cloned` point to the same underlying object.
/// assert!(core::ptr::eq(&*obj, &*cloned));
/// # Ok::<(), Error>(())
/// ```
///
/// Using `ArcBorrow<T>` as the type of `self`:
///
/// ```
/// use crate::sync::{Arc, ArcBorrow};
/// use kernel::sync::{Arc, ArcBorrow};
///
/// struct Example {
/// a: u32,
@@ -370,6 +382,7 @@ impl<T: ?Sized> From<Pin<UniqueArc<T>>> for Arc<T> {
///
/// let obj = Arc::try_new(Example { a: 10, b: 20 })?;
/// obj.as_arc_borrow().use_reference();
/// # Ok::<(), Error>(())
/// ```
pub struct ArcBorrow<'a, T: ?Sized + 'a> {
inner: NonNull<ArcInner<T>>,
1 change: 1 addition & 0 deletions rust/kernel/sync/lock/mutex.rs
Original file line number Diff line number Diff line change
@@ -63,6 +63,7 @@ macro_rules! new_mutex {
/// assert_eq!(e.c, 10);
/// assert_eq!(e.d.lock().a, 20);
/// assert_eq!(e.d.lock().b, 30);
/// # Ok::<(), Error>(())
/// ```
///
/// The following example shows how to use interior mutability to modify the contents of a struct
1 change: 1 addition & 0 deletions rust/kernel/sync/lock/spinlock.rs
Original file line number Diff line number Diff line change
@@ -61,6 +61,7 @@ macro_rules! new_spinlock {
/// assert_eq!(e.c, 10);
/// assert_eq!(e.d.lock().a, 20);
/// assert_eq!(e.d.lock().b, 30);
/// # Ok::<(), Error>(())
/// ```
///
/// The following example shows how to use interior mutability to modify the contents of a struct
126 changes: 81 additions & 45 deletions rust/kernel/types.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ use crate::init::{self, PinInit};
use alloc::boxed::Box;
use core::{
cell::UnsafeCell,
marker::PhantomData,
marker::{PhantomData, PhantomPinned},
mem::MaybeUninit,
ops::{Deref, DerefMut},
ptr::NonNull,
@@ -20,84 +20,111 @@ use core::{
/// This trait is meant to be used in cases when Rust objects are stored in C objects and
/// eventually "freed" back to Rust.
pub trait ForeignOwnable: Sized {
/// Type of values borrowed between calls to [`ForeignOwnable::into_foreign`] and
/// [`ForeignOwnable::from_foreign`].
/// Type used to immutably borrow a value that is currently foreign-owned.
type Borrowed<'a>;

/// Type used to mutably borrow a value that is currently foreign-owned.
type BorrowedMut<'a>;

/// Converts a Rust-owned object to a foreign-owned one.
///
/// The foreign representation is a pointer to void.
fn into_foreign(self) -> *const core::ffi::c_void;

/// Borrows a foreign-owned object.
/// Converts a foreign-owned object back to a Rust-owned one.
///
/// # Safety
///
/// `ptr` must have been returned by a previous call to [`ForeignOwnable::into_foreign`] for
/// which a previous matching [`ForeignOwnable::from_foreign`] hasn't been called yet.
/// Additionally, all instances (if any) of values returned by [`ForeignOwnable::borrow_mut`]
/// for this object must have been dropped.
unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> Self::Borrowed<'a>;
/// The provided pointer must have been returned by a previous call to [`into_foreign`], and it
/// must not be passed to `from_foreign` more than once.
///
/// [`into_foreign`]: Self::into_foreign
unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self;

/// Mutably borrows a foreign-owned object.
/// Borrows a foreign-owned object immutably.
///
/// This method provides a way to access a foreign-owned value from Rust immutably. It provides
/// you with exactly the same abilities as an `&Self` when the value is Rust-owned.
///
/// # Safety
///
/// `ptr` must have been returned by a previous call to [`ForeignOwnable::into_foreign`] for
/// which a previous matching [`ForeignOwnable::from_foreign`] hasn't been called yet.
/// Additionally, all instances (if any) of values returned by [`ForeignOwnable::borrow`] and
/// [`ForeignOwnable::borrow_mut`] for this object must have been dropped.
unsafe fn borrow_mut(ptr: *const core::ffi::c_void) -> ScopeGuard<Self, fn(Self)> {
// SAFETY: The safety requirements ensure that `ptr` came from a previous call to
// `into_foreign`.
ScopeGuard::new_with_data(unsafe { Self::from_foreign(ptr) }, |d| {
d.into_foreign();
})
}
/// The provided pointer must have been returned by a previous call to [`into_foreign`], and if
/// the pointer is ever passed to [`from_foreign`], then that call must happen after the end of
/// the lifetime 'a.
///
/// [`into_foreign`]: Self::into_foreign
/// [`from_foreign`]: Self::from_foreign
unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> Self::Borrowed<'a>;

/// Converts a foreign-owned object back to a Rust-owned one.
/// Borrows a foreign-owned object mutably.
///
/// This method provides a way to access a foreign-owned value from Rust mutably. It provides
/// you with exactly the same abilities as an `&mut Self` when the value is Rust-owned, except
/// that this method does not let you swap the foreign-owned object for another. (That is, it
/// does not let you change the address of the void pointer that the foreign code is storing.)
///
/// Note that for types like [`Arc`], an `&mut Arc<T>` only gives you immutable access to the
/// inner value, so this method also only provides immutable access in that case.
///
/// In the case of `Box<T>`, this method gives you the ability to modify the inner `T`, but it
/// does not let you change the box itself. That is, you cannot change which allocation the box
/// points at.
///
/// # Safety
///
/// `ptr` must have been returned by a previous call to [`ForeignOwnable::into_foreign`] for
/// which a previous matching [`ForeignOwnable::from_foreign`] hasn't been called yet.
/// Additionally, all instances (if any) of values returned by [`ForeignOwnable::borrow`] and
/// [`ForeignOwnable::borrow_mut`] for this object must have been dropped.
unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self;
/// The provided pointer must have been returned by a previous call to [`into_foreign`], and if
/// the pointer is ever passed to [`from_foreign`], then that call must happen after the end of
/// the lifetime 'a.
///
/// The lifetime 'a must not overlap with the lifetime of any other call to [`borrow`] or
/// `borrow_mut` on the same object.
///
/// [`into_foreign`]: Self::into_foreign
/// [`from_foreign`]: Self::from_foreign
/// [`borrow`]: Self::borrow
/// [`Arc`]: crate::sync::Arc
unsafe fn borrow_mut<'a>(ptr: *const core::ffi::c_void) -> Self::BorrowedMut<'a>;
}

impl<T: 'static> ForeignOwnable for Box<T> {
type Borrowed<'a> = &'a T;
type BorrowedMut<'a> = &'a mut T;

fn into_foreign(self) -> *const core::ffi::c_void {
Box::into_raw(self) as _
}

unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> &'a T {
// SAFETY: The safety requirements for this function ensure that the object is still alive,
// so it is safe to dereference the raw pointer.
// The safety requirements of `from_foreign` also ensure that the object remains alive for
// the lifetime of the returned value.
unsafe { &*ptr.cast() }
}

unsafe fn from_foreign(ptr: *const core::ffi::c_void) -> Self {
// SAFETY: The safety requirements of this function ensure that `ptr` comes from a previous
// call to `Self::into_foreign`.
unsafe { Box::from_raw(ptr as _) }
}

unsafe fn borrow<'a>(ptr: *const core::ffi::c_void) -> &'a T {
// SAFETY: The safety requirements of this method ensure that the object remains alive and
// immutable for the duration of 'a.
unsafe { &*ptr.cast() }
}

unsafe fn borrow_mut<'a>(ptr: *const core::ffi::c_void) -> &'a mut T {
// SAFETY: The safety requirements of this method ensure that the pointer is valid and that
// nothing else will access the value for the duration of 'a.
unsafe { &mut *ptr.cast_mut().cast() }
}
}

impl ForeignOwnable for () {
type Borrowed<'a> = ();
type BorrowedMut<'a> = ();

fn into_foreign(self) -> *const core::ffi::c_void {
core::ptr::NonNull::dangling().as_ptr()
}

unsafe fn borrow<'a>(_: *const core::ffi::c_void) -> Self::Borrowed<'a> {}

unsafe fn from_foreign(_: *const core::ffi::c_void) -> Self {}

unsafe fn borrow<'a>(_: *const core::ffi::c_void) -> Self::Borrowed<'a> {}
unsafe fn borrow_mut<'a>(_: *const core::ffi::c_void) -> Self::BorrowedMut<'a> {}
}

/// Runs a cleanup function/closure when dropped.
@@ -109,7 +136,7 @@ impl ForeignOwnable for () {
/// In the example below, we have multiple exit paths and we want to log regardless of which one is
/// taken:
/// ```
/// # use kernel::ScopeGuard;
/// # use kernel::types::ScopeGuard;
/// fn example1(arg: bool) {
/// let _log = ScopeGuard::new(|| pr_info!("example1 completed\n"));
///
@@ -127,7 +154,7 @@ impl ForeignOwnable for () {
/// In the example below, we want to log the same message on all early exits but a different one on
/// the main exit path:
/// ```
/// # use kernel::ScopeGuard;
/// # use kernel::types::ScopeGuard;
/// fn example2(arg: bool) {
/// let log = ScopeGuard::new(|| pr_info!("example2 returned early\n"));
///
@@ -148,7 +175,7 @@ impl ForeignOwnable for () {
/// In the example below, we need a mutable object (the vector) to be accessible within the log
/// function, so we wrap it in the [`ScopeGuard`]:
/// ```
/// # use kernel::ScopeGuard;
/// # use kernel::types::ScopeGuard;
/// fn example3(arg: bool) -> Result {
/// let mut vec =
/// ScopeGuard::new_with_data(Vec::new(), |v| pr_info!("vec had {} elements\n", v.len()));
@@ -224,17 +251,26 @@ impl<T, F: FnOnce(T)> Drop for ScopeGuard<T, F> {
///
/// This is meant to be used with FFI objects that are never interpreted by Rust code.
#[repr(transparent)]
pub struct Opaque<T>(MaybeUninit<UnsafeCell<T>>);
pub struct Opaque<T> {
value: UnsafeCell<MaybeUninit<T>>,
_pin: PhantomPinned,
}

impl<T> Opaque<T> {
/// Creates a new opaque value.
pub const fn new(value: T) -> Self {
Self(MaybeUninit::new(UnsafeCell::new(value)))
Self {
value: UnsafeCell::new(MaybeUninit::new(value)),
_pin: PhantomPinned,
}
}

/// Creates an uninitialised value.
pub const fn uninit() -> Self {
Self(MaybeUninit::uninit())
Self {
value: UnsafeCell::new(MaybeUninit::uninit()),
_pin: PhantomPinned,
}
}

/// Creates a pin-initializer from the given initializer closure.
@@ -258,15 +294,15 @@ impl<T> Opaque<T> {

/// Returns a raw pointer to the opaque data.
pub fn get(&self) -> *mut T {
UnsafeCell::raw_get(self.0.as_ptr())
UnsafeCell::get(&self.value).cast::<T>()
}

/// Gets the value behind `this`.
///
/// This function is useful to get access to the value without creating intermediate
/// references.
pub const fn raw_get(this: *const Self) -> *mut T {
UnsafeCell::raw_get(this.cast::<UnsafeCell<T>>())
UnsafeCell::raw_get(this.cast::<UnsafeCell<MaybeUninit<T>>>()).cast::<T>()
}
}

250 changes: 250 additions & 0 deletions rust/kernel/xarray.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
// SPDX-License-Identifier: GPL-2.0

//! XArray abstraction.
//!
//! C header: [`include/linux/xarray.h`](../../include/linux/xarray.h)
use crate::{
bindings,
error::{Error, Result},
prelude::*,
types::{ForeignOwnable, Opaque, ScopeGuard},
};
use core::{
marker::{PhantomData, PhantomPinned},
pin::Pin,
ptr::{addr_of_mut, NonNull},
};

/// Flags passed to `XArray::new` to configure the `XArray`.
type Flags = bindings::gfp_t;

/// Flag values passed to `XArray::new` to configure the `XArray`.
pub mod flags {
/// Use IRQ-safe locking.
pub const LOCK_IRQ: super::Flags = bindings::BINDINGS_XA_FLAGS_LOCK_IRQ;
/// Use softirq-safe locking.
pub const LOCK_BH: super::Flags = bindings::BINDINGS_XA_FLAGS_LOCK_BH;
/// Track which entries are free (distinct from None).
pub const TRACK_FREE: super::Flags = bindings::BINDINGS_XA_FLAGS_TRACK_FREE;
/// Initialize array index 0 as busy.
pub const ZERO_BUSY: super::Flags = bindings::BINDINGS_XA_FLAGS_ZERO_BUSY;
/// Use GFP_ACCOUNT for internal memory allocations.
pub const ACCOUNT: super::Flags = bindings::BINDINGS_XA_FLAGS_ACCOUNT;
/// Create an allocating `XArray` starting at index 0.
pub const ALLOC: super::Flags = bindings::BINDINGS_XA_FLAGS_ALLOC;
/// Create an allocating `XArray` starting at index 1.
pub const ALLOC1: super::Flags = bindings::BINDINGS_XA_FLAGS_ALLOC1;
}

/// An array which efficiently maps sparse integer indices to owned objects.
///
/// This is similar to a `Vec<Option<T>>`, but more efficient when there are holes in the
/// index space, and can be efficiently grown.
///
/// This structure is expected to often be used with an inner type that can be efficiently
/// cloned, such as an `Arc<T>`.
///
/// # Examples
///
/// In this example, we create a new `XArray` and demonstrate
/// rudimentary read/write operations.
Comment on lines +50 to +51
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this example is very helpful! Additionally it might be a good idea to take each example and put it on the relevant functions as well, especially the find function.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't realize that worked! I think at one point in time I could only get the kunit stuff working if the tests were on the top of the struct.

I see now that it does...what do we consider better, a long doctest at the top with interactions between different functions, or individual tests for each function? (Or both...with some repetition?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to have a examples on functions. Big examples only make sense if you want to demonstrate how things are used together. I would recommend this when the API is excessively complicated. I dont think that is the case here.

///
/// ```
/// use core::{
/// borrow::Borrow,
/// ops::{ Deref, DerefMut }
/// };
/// use kernel::{
/// pin_init,
/// sync::Arc,
/// xarray::{ XArray, flags::LOCK_IRQ }
/// };
///
/// let xarr = Box::pin_init(XArray::<Arc<usize>>::new(LOCK_IRQ)).unwrap();
/// let xarr = xarr.as_ref();
///
/// // `get` and `set` are used to read/write values.
/// assert!(xarr.get(0).is_none());
/// xarr.set(0, Arc::try_new(0)?);
/// assert_eq!(*xarr.get(0).unwrap(), 0);
///
/// // `replace` is like `set`, but returns the old value.
/// let old = xarr.replace(0, Arc::try_new(1)?)?.unwrap();
/// assert_eq!(*old, 0);
/// assert_eq!(*xarr.get(0).unwrap(), 1);
///
/// // `replace` returns `None` if there was no previous value.
/// assert!(xarr.replace(1, Arc::try_new(1)?)?.is_none());
/// assert_eq!(*xarr.get(1).unwrap(), 1);
///
/// // Similarly, `remove` returns the old value, or `None` if it didn't exist.
/// assert_eq!(*xarr.remove(0).unwrap(), 1);
/// assert!(xarr.get(0).is_none());
/// assert!(xarr.remove(0).is_none());
///
/// # Ok::<(), Error>(())
/// ```
///
/// In this example, we create a new `XArray` and demonstrate
/// reading and/or mutating values that are not `T::Borrowed<'a>: Into<T>`.
///
/// ```
/// use core::{
/// borrow::Borrow,
/// ops::{ Deref, DerefMut }
/// };
/// use kernel::xarray::{XArray, flags::LOCK_IRQ};
///
/// let xarr = Box::pin_init(XArray::<Box<usize>>::new(LOCK_IRQ)).unwrap();
/// let xarr = xarr.as_ref();
///
/// // If the type is not `T::Borrowed<'a>: Into<T>`, use `get_scoped` to access a shared reference
/// // from a closure.
/// assert!(xarr.get_scoped(0, |x| x.is_none()));
/// xarr.set(0, Box::try_new(0)?);
/// xarr.get_scoped(0, |x| assert_eq!(*x.unwrap(), 0));
///
/// // `get_scoped` also gives mutable access inside the closure.
/// xarr.get_scoped(0, |x| {
/// let mut_x = x.unwrap();
/// assert_eq!(mut_x, &0);
/// *mut_x = 1;
/// });
///
/// xarr.get_scoped(0, |x| assert_eq!(*x.unwrap(), 1));
///
/// # Ok::<(), Error>(())
/// ```
///
pub struct XArray<T: ForeignOwnable> {
xa: Opaque<bindings::xarray>,
_p: PhantomData<T>,
_q: PhantomPinned,
}

impl<T: ForeignOwnable> XArray<T> {
/// Creates a new `XArray` with the given flags.
pub fn new(flags: Flags) -> impl PinInit<Self> {
unsafe {
kernel::init::pin_init_from_closure(move |slot: *mut XArray<T>| {
bindings::xa_init_flags(Opaque::raw_get(addr_of_mut!((*slot).xa)), flags);
Ok(())
})
}
}

/// Replaces an entry with a new value, returning the old value (if any).
pub fn replace(&self, index: u64, value: T) -> Result<Option<T>> {
let new = value.into_foreign();
// SAFETY: `new` just came from into_foreign(), and we dismiss this guard if
// the xa_store operation succeeds and takes ownership of the pointer.
let guard = ScopeGuard::new(|| unsafe {
drop(T::from_foreign(new));
});

// SAFETY: `self.xa` is always valid by the type invariant, and we are storing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This invariant should be documented.

// a `T::into_foreign()` result which upholds the later invariants.
let old = unsafe {
bindings::xa_store(self.xa.get(), index, new as *mut _, bindings::GFP_KERNEL)
};

let ret = unsafe { bindings::xa_err(old) };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing SAFETY comment.

if ret != 0 {
Err(Error::from_errno(ret))
} else if old.is_null() {
guard.dismiss();
Ok(None)
} else {
guard.dismiss();
// SAFETY: The old value must have been stored by either this function or
// `alloc_limits_opt`, both of which ensure non-NULL entries are valid
// ForeignOwnable pointers.
Ok(Some(unsafe { T::from_foreign(old) }))
}
}

/// Replaces an entry with a new value, dropping the old value (if any).
pub fn set(&self, index: u64, value: T) -> Result {
self.replace(index, value)?;
Ok(())
}

/// Looks up a reference to an entry in the array, cloning it
/// and returning the cloned value to the user.
///
pub fn get(&self, index: u64) -> Option<T>
where
for<'a> T::BorrowedMut<'a>: Into<T>,
{
self.get_scoped(index, |x| x.map(|v| v.into()))
}

/// Looks up and a reference to an entry in the array, calling the user
/// provided function on the resulting `Option<&T>` to return a value
/// computed from the reference. Use this function if you need
/// (possibly mutable) access to a `&T` that is not `T::Borrowed<'a>: Into<T>`.
///
pub fn get_scoped<F, R>(&self, index: u64, f: F) -> R
where
F: FnOnce(Option<T::BorrowedMut<'_>>) -> R,
{
// SAFETY: `self.xa` is always valid by the type invariant.
unsafe { bindings::xa_lock(self.xa.get()) };

// SAFETY: `self.xa` is always valid by the type invariant.
let p = unsafe { bindings::xa_load(self.xa.get(), index) };
let t = NonNull::new(p as *mut T).map(|p| unsafe { T::borrow_mut(p.as_ptr() as _) });
let r = f(t);

// SAFETY: `self.xa` is always valid by the type invariant.
unsafe { bindings::xa_unlock(self.xa.get()) };

r
}

/// Removes and returns an entry, returning it if it existed.
pub fn remove(&self, index: u64) -> Option<T> {
let p = unsafe { bindings::xa_erase(self.xa.get(), index) };
if p.is_null() {
None
} else {
Some(unsafe { T::from_foreign(p) })
}
}
}

impl<T: ForeignOwnable> Drop for XArray<T> {
fn drop(&mut self) {
// SAFETY: `self.xa` is valid by the type invariant, and as we have the only reference to
// the `XArray` we can safely iterate its contents and drop everything.
unsafe {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this block should be split into smaller parts. that way every unsafe call is visible and can be annotated appropriately.

let mut index: core::ffi::c_ulong = 0;
let mut entry = bindings::xa_find(
self.xa.get(),
&mut index,
core::ffi::c_ulong::MAX,
bindings::BINDINGS_XA_PRESENT,
);
while !entry.is_null() {
T::from_foreign(entry);
entry = bindings::xa_find_after(
self.xa.get(),
&mut index,
core::ffi::c_ulong::MAX,
bindings::BINDINGS_XA_PRESENT,
);
}

// Locked locks are not safe to drop. Normally we would want to try_lock()/unlock() here
// for safety or something similar, but in this case xa_destroy() is guaranteed to
// acquire the lock anyway. This will deadlock if a lock guard was improperly dropped,
// but that is not UB, so it's sufficient for soundness purposes.
bindings::xa_destroy(self.xa.get());
}
}
}

// SAFETY: XArray is thread-safe and all mutation operations are internally locked.
unsafe impl<T: Send + ForeignOwnable> Send for XArray<T> {}
unsafe impl<T: Sync + ForeignOwnable> Sync for XArray<T> {}
97 changes: 97 additions & 0 deletions rust/macros/lib.rs
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ mod quote;
mod concat_idents;
mod helpers;
mod module;
mod paste;
mod pin_data;
mod pinned_drop;
mod vtable;
@@ -246,3 +247,99 @@ pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream {
pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
pinned_drop::pinned_drop(args, input)
}

/// Paste identifiers together.
///
/// Within the `paste!` macro, identifiers inside `[<` and `>]` are concatenated together to form a
/// single identifier.
///
/// This is similar to the [`paste`] crate, but with pasting feature limited to identifiers
/// (literals, lifetimes and documentation strings are not supported). There is a difference in
/// supported modifiers as well.
///
/// # Example
///
/// ```ignore
/// use kernel::macro::paste;
///
/// macro_rules! pub_no_prefix {
/// ($prefix:ident, $($newname:ident),+) => {
/// paste! {
/// $(pub(crate) const $newname: u32 = [<$prefix $newname>];)+
/// }
/// };
/// }
///
/// pub_no_prefix!(
/// binder_driver_return_protocol_,
/// BR_OK,
/// BR_ERROR,
/// BR_TRANSACTION,
/// BR_REPLY,
/// BR_DEAD_REPLY,
/// BR_TRANSACTION_COMPLETE,
/// BR_INCREFS,
/// BR_ACQUIRE,
/// BR_RELEASE,
/// BR_DECREFS,
/// BR_NOOP,
/// BR_SPAWN_LOOPER,
/// BR_DEAD_BINDER,
/// BR_CLEAR_DEATH_NOTIFICATION_DONE,
/// BR_FAILED_REPLY
/// );
///
/// assert_eq!(BR_OK, binder_driver_return_protocol_BR_OK);
/// ```
///
/// # Modifiers
///
/// For each identifier, it is possible to attach one or multiple modifiers to
/// it.
///
/// Currently supported modifiers are:
/// * `span`: change the span of concatenated identifier to the span of the specified token. By
/// default the span of the `[< >]` group is used.
/// * `lower`: change the identifier to lower case.
/// * `upper`: change the identifier to upper case.
///
/// ```ignore
/// use kernel::macro::paste;
///
/// macro_rules! pub_no_prefix {
/// ($prefix:ident, $($newname:ident),+) => {
/// kernel::macros::paste! {
/// $(pub(crate) const fn [<$newname:lower:span>]: u32 = [<$prefix $newname:span>];)+
/// }
/// };
/// }
///
/// pub_no_prefix!(
/// binder_driver_return_protocol_,
/// BR_OK,
/// BR_ERROR,
/// BR_TRANSACTION,
/// BR_REPLY,
/// BR_DEAD_REPLY,
/// BR_TRANSACTION_COMPLETE,
/// BR_INCREFS,
/// BR_ACQUIRE,
/// BR_RELEASE,
/// BR_DECREFS,
/// BR_NOOP,
/// BR_SPAWN_LOOPER,
/// BR_DEAD_BINDER,
/// BR_CLEAR_DEATH_NOTIFICATION_DONE,
/// BR_FAILED_REPLY
/// );
///
/// assert_eq!(br_ok(), binder_driver_return_protocol_BR_OK);
/// ```
///
/// [`paste`]: https://docs.rs/paste/
#[proc_macro]
pub fn paste(input: TokenStream) -> TokenStream {
let mut tokens = input.into_iter().collect();
paste::expand(&mut tokens);
tokens.into_iter().collect()
}
94 changes: 94 additions & 0 deletions rust/macros/paste.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use proc_macro::{Delimiter, Group, Ident, Spacing, Span, TokenTree};

fn concat(tokens: &[TokenTree], group_span: Span) -> TokenTree {
let mut tokens = tokens.iter();
let mut segments = Vec::new();
let mut span = None;
loop {
match tokens.next() {
None => break,
Some(TokenTree::Literal(lit)) => segments.push((lit.to_string(), lit.span())),
Some(TokenTree::Ident(ident)) => {
let mut value = ident.to_string();
if value.starts_with("r#") {
value.replace_range(0..2, "");
}
segments.push((value, ident.span()));
}
Some(TokenTree::Punct(p)) if p.as_char() == ':' => {
let Some(TokenTree::Ident(ident)) = tokens.next() else {
panic!("expected identifier as modifier");
};

let (mut value, sp) = segments.pop().expect("expected identifier before modifier");
match ident.to_string().as_str() {
// Set the overall span of concatenated token as current span
"span" => {
assert!(
span.is_none(),
"span modifier should only appear at most once"
);
span = Some(sp);
}
"lower" => value = value.to_lowercase(),
"upper" => value = value.to_uppercase(),
v => panic!("unknown modifier `{v}`"),
};
segments.push((value, sp));
}
_ => panic!("unexpected token in paste segments"),
};
}

let pasted: String = segments.into_iter().map(|x| x.0).collect();
TokenTree::Ident(Ident::new(&pasted, span.unwrap_or(group_span)))
}

pub(crate) fn expand(tokens: &mut Vec<TokenTree>) {
for token in tokens.iter_mut() {
if let TokenTree::Group(group) = token {
let delimiter = group.delimiter();
let span = group.span();
let mut stream: Vec<_> = group.stream().into_iter().collect();
// Find groups that looks like `[< A B C D >]`
if delimiter == Delimiter::Bracket
&& stream.len() >= 3
&& matches!(&stream[0], TokenTree::Punct(p) if p.as_char() == '<')
&& matches!(&stream[stream.len() - 1], TokenTree::Punct(p) if p.as_char() == '>')
{
// Replace the group with concatenated token
*token = concat(&stream[1..stream.len() - 1], span);
} else {
// Recursively expand tokens inside the group
expand(&mut stream);
let mut group = Group::new(delimiter, stream.into_iter().collect());
group.set_span(span);
*token = TokenTree::Group(group);
}
}
}

// Path segments cannot contain invisible delimiter group, so remove them if any.
for i in (0..tokens.len().saturating_sub(3)).rev() {
// Looking for a double colon
if matches!(
(&tokens[i + 1], &tokens[i + 2]),
(TokenTree::Punct(a), TokenTree::Punct(b))
if a.as_char() == ':' && a.spacing() == Spacing::Joint && b.as_char() == ':'
) {
match &tokens[i + 3] {
TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
tokens.splice(i + 3..i + 4, group.stream());
}
_ => (),
}

match &tokens[i] {
TokenTree::Group(group) if group.delimiter() == Delimiter::None => {
tokens.splice(i..i + 1, group.stream());
}
_ => (),
}
}
}
}
4 changes: 2 additions & 2 deletions rust/macros/vtable.rs
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ pub(crate) fn vtable(_attr: TokenStream, ts: TokenStream) -> TokenStream {
};

let mut body_it = body.stream().into_iter();
let mut functions = Vec::new();
let mut functions = HashSet::new();
let mut consts = HashSet::new();
while let Some(token) = body_it.next() {
match token {
@@ -37,7 +37,7 @@ pub(crate) fn vtable(_attr: TokenStream, ts: TokenStream) -> TokenStream {
// Possibly we've encountered a fn pointer type instead.
_ => continue,
};
functions.push(fn_name);
functions.insert(fn_name);
}
TokenTree::Ident(ident) if ident.to_string() == "const" => {
let const_name = match body_it.next() {
2 changes: 2 additions & 0 deletions scripts/.gitignore
Original file line number Diff line number Diff line change
@@ -5,6 +5,8 @@
/kallsyms
/module.lds
/recordmcount
/rustdoc_test_builder
/rustdoc_test_gen
/sign-file
/sorttable
/target.json
9 changes: 7 additions & 2 deletions scripts/Makefile
Original file line number Diff line number Diff line change
@@ -9,15 +9,20 @@ hostprogs-always-$(CONFIG_BUILDTIME_TABLE_SORT) += sorttable
hostprogs-always-$(CONFIG_ASN1) += asn1_compiler
hostprogs-always-$(CONFIG_MODULE_SIG_FORMAT) += sign-file
hostprogs-always-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert
always-$(CONFIG_RUST) += target.json
hostprogs-always-$(CONFIG_RUST_KERNEL_KUNIT_TEST) += rustdoc_test_builder
hostprogs-always-$(CONFIG_RUST_KERNEL_KUNIT_TEST) += rustdoc_test_gen

ifeq ($(ARCH),x86_64)
always-$(CONFIG_RUST) += target.json
filechk_rust_target = $< < include/config/auto.conf

$(obj)/target.json: scripts/generate_rust_target include/config/auto.conf FORCE
$(call filechk,rust_target)
endif

hostprogs += generate_rust_target
generate_rust_target-rust := y
rustdoc_test_builder-rust := y
rustdoc_test_gen-rust := y

HOSTCFLAGS_sorttable.o = -I$(srctree)/tools/include
HOSTLDLIBS_sorttable = -lpthread
2 changes: 1 addition & 1 deletion scripts/Makefile.build
Original file line number Diff line number Diff line change
@@ -277,7 +277,7 @@ $(obj)/%.lst: $(src)/%.c FORCE
# Compile Rust sources (.rs)
# ---------------------------------------------------------------------------

rust_allowed_features := new_uninit
rust_allowed_features := allocator_api,new_uninit

rust_common_cmd = \
RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \
4 changes: 3 additions & 1 deletion scripts/generate_rust_target.rs
Original file line number Diff line number Diff line change
@@ -148,7 +148,9 @@ fn main() {
let mut ts = TargetSpec::new();

// `llvm-target`s are taken from `scripts/Makefile.clang`.
if cfg.has("X86_64") {
if cfg.has("ARM64") {
panic!("arm64 uses the builtin rustc aarch64-unknown-none target");
} else if cfg.has("X86_64") {
ts.push("arch", "x86_64");
ts.push(
"data-layout",
2 changes: 1 addition & 1 deletion scripts/min-tool-version.sh
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ rustc)
echo 1.68.2
;;
bindgen)
echo 0.56.0
echo 0.65.1
;;
*)
echo "$1: unknown tool" >&2
233 changes: 167 additions & 66 deletions scripts/rust_is_available.sh
Original file line number Diff line number Diff line change
@@ -2,8 +2,6 @@
# SPDX-License-Identifier: GPL-2.0
#
# Tests whether a suitable Rust toolchain is available.
#
# Pass `-v` for human output and more checks (as warnings).

set -e

@@ -21,102 +19,208 @@ get_canonical_version()
echo $((100000 * $1 + 100 * $2 + $3))
}

# Print a reference to the Quick Start guide in the documentation.
print_docs_reference()
{
echo >&2 "***"
echo >&2 "*** Please see Documentation/rust/quick-start.rst for details"
echo >&2 "*** on how to set up the Rust support."
echo >&2 "***"
}

# Print an explanation about the fact that the script is meant to be called from Kbuild.
print_kbuild_explanation()
{
echo >&2 "***"
echo >&2 "*** This script is intended to be called from Kbuild."
echo >&2 "*** Please use the 'rustavailable' target to call it instead."
echo >&2 "*** Otherwise, the results may not be meaningful."
exit 1
}

# If the script fails for any reason, or if there was any warning, then
# print a reference to the documentation on exit.
warning=0
trap 'if [ $? -ne 0 ] || [ $warning -ne 0 ]; then print_docs_reference; fi' EXIT

# Check that the expected environment variables are set.
if [ -z "${RUSTC+x}" ]; then
echo >&2 "***"
echo >&2 "*** Environment variable 'RUSTC' is not set."
print_kbuild_explanation
fi

if [ -z "${BINDGEN+x}" ]; then
echo >&2 "***"
echo >&2 "*** Environment variable 'BINDGEN' is not set."
print_kbuild_explanation
fi

if [ -z "${CC+x}" ]; then
echo >&2 "***"
echo >&2 "*** Environment variable 'CC' is not set."
print_kbuild_explanation
fi

# Check that the Rust compiler exists.
if ! command -v "$RUSTC" >/dev/null; then
if [ "$1" = -v ]; then
echo >&2 "***"
echo >&2 "*** Rust compiler '$RUSTC' could not be found."
echo >&2 "***"
fi
echo >&2 "***"
echo >&2 "*** Rust compiler '$RUSTC' could not be found."
echo >&2 "***"
exit 1
fi

# Check that the Rust bindings generator exists.
if ! command -v "$BINDGEN" >/dev/null; then
if [ "$1" = -v ]; then
echo >&2 "***"
echo >&2 "*** Rust bindings generator '$BINDGEN' could not be found."
echo >&2 "***"
fi
echo >&2 "***"
echo >&2 "*** Rust bindings generator '$BINDGEN' could not be found."
echo >&2 "***"
exit 1
fi

# Check that the Rust compiler version is suitable.
#
# Non-stable and distributions' versions may have a version suffix, e.g. `-dev`.
rust_compiler_output=$( \
LC_ALL=C "$RUSTC" --version 2>/dev/null
) || rust_compiler_code=$?
if [ -n "$rust_compiler_code" ]; then
echo >&2 "***"
echo >&2 "*** Running '$RUSTC' to check the Rust compiler version failed with"
echo >&2 "*** code $rust_compiler_code. See output and docs below for details:"
echo >&2 "***"
echo >&2 "$rust_compiler_output"
echo >&2 "***"
exit 1
fi
rust_compiler_version=$( \
LC_ALL=C "$RUSTC" --version 2>/dev/null \
| head -n 1 \
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+' \
echo "$rust_compiler_output" \
| sed -nE '1s:.*rustc ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
)
if [ -z "$rust_compiler_version" ]; then
echo >&2 "***"
echo >&2 "*** Running '$RUSTC' to check the Rust compiler version did not return"
echo >&2 "*** an expected output. See output and docs below for details:"
echo >&2 "***"
echo >&2 "$rust_compiler_output"
echo >&2 "***"
exit 1
fi
rust_compiler_min_version=$($min_tool_version rustc)
rust_compiler_cversion=$(get_canonical_version $rust_compiler_version)
rust_compiler_min_cversion=$(get_canonical_version $rust_compiler_min_version)
if [ "$rust_compiler_cversion" -lt "$rust_compiler_min_cversion" ]; then
if [ "$1" = -v ]; then
echo >&2 "***"
echo >&2 "*** Rust compiler '$RUSTC' is too old."
echo >&2 "*** Your version: $rust_compiler_version"
echo >&2 "*** Minimum version: $rust_compiler_min_version"
echo >&2 "***"
fi
echo >&2 "***"
echo >&2 "*** Rust compiler '$RUSTC' is too old."
echo >&2 "*** Your version: $rust_compiler_version"
echo >&2 "*** Minimum version: $rust_compiler_min_version"
echo >&2 "***"
exit 1
fi
if [ "$1" = -v ] && [ "$rust_compiler_cversion" -gt "$rust_compiler_min_cversion" ]; then
if [ "$rust_compiler_cversion" -gt "$rust_compiler_min_cversion" ]; then
echo >&2 "***"
echo >&2 "*** Rust compiler '$RUSTC' is too new. This may or may not work."
echo >&2 "*** Your version: $rust_compiler_version"
echo >&2 "*** Expected version: $rust_compiler_min_version"
echo >&2 "***"
warning=1
fi

# Check that the Rust bindings generator is suitable.
#
# Non-stable and distributions' versions may have a version suffix, e.g. `-dev`.
rust_bindings_generator_output=$( \
LC_ALL=C "$BINDGEN" --version 2>/dev/null
) || rust_bindings_generator_code=$?
if [ -n "$rust_bindings_generator_code" ]; then
echo >&2 "***"
echo >&2 "*** Running '$BINDGEN' to check the Rust bindings generator version failed with"
echo >&2 "*** code $rust_bindings_generator_code. See output and docs below for details:"
echo >&2 "***"
echo >&2 "$rust_bindings_generator_output"
echo >&2 "***"
exit 1
fi
rust_bindings_generator_version=$( \
LC_ALL=C "$BINDGEN" --version 2>/dev/null \
| head -n 1 \
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+' \
echo "$rust_bindings_generator_output" \
| sed -nE '1s:.*bindgen ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
)
if [ -z "$rust_bindings_generator_version" ]; then
echo >&2 "***"
echo >&2 "*** Running '$BINDGEN' to check the bindings generator version did not return"
echo >&2 "*** an expected output. See output and docs below for details:"
echo >&2 "***"
echo >&2 "$rust_bindings_generator_output"
echo >&2 "***"
exit 1
fi
rust_bindings_generator_min_version=$($min_tool_version bindgen)
rust_bindings_generator_cversion=$(get_canonical_version $rust_bindings_generator_version)
rust_bindings_generator_min_cversion=$(get_canonical_version $rust_bindings_generator_min_version)
if [ "$rust_bindings_generator_cversion" -lt "$rust_bindings_generator_min_cversion" ]; then
if [ "$1" = -v ]; then
echo >&2 "***"
echo >&2 "*** Rust bindings generator '$BINDGEN' is too old."
echo >&2 "*** Your version: $rust_bindings_generator_version"
echo >&2 "*** Minimum version: $rust_bindings_generator_min_version"
echo >&2 "***"
fi
echo >&2 "***"
echo >&2 "*** Rust bindings generator '$BINDGEN' is too old."
echo >&2 "*** Your version: $rust_bindings_generator_version"
echo >&2 "*** Minimum version: $rust_bindings_generator_min_version"
echo >&2 "***"
exit 1
fi
if [ "$1" = -v ] && [ "$rust_bindings_generator_cversion" -gt "$rust_bindings_generator_min_cversion" ]; then
if [ "$rust_bindings_generator_cversion" -gt "$rust_bindings_generator_min_cversion" ]; then
echo >&2 "***"
echo >&2 "*** Rust bindings generator '$BINDGEN' is too new. This may or may not work."
echo >&2 "*** Your version: $rust_bindings_generator_version"
echo >&2 "*** Expected version: $rust_bindings_generator_min_version"
echo >&2 "***"
warning=1
fi

# Check that the `libclang` used by the Rust bindings generator is suitable.
#
# In order to do that, first invoke `bindgen` to get the `libclang` version
# found by `bindgen`. This step may already fail if, for instance, `libclang`
# is not found, thus inform the user in such a case.
bindgen_libclang_output=$( \
LC_ALL=C "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang.h 2>&1 >/dev/null
) || bindgen_libclang_code=$?
if [ -n "$bindgen_libclang_code" ]; then
echo >&2 "***"
echo >&2 "*** Running '$BINDGEN' to check the libclang version (used by the Rust"
echo >&2 "*** bindings generator) failed with code $bindgen_libclang_code. This may be caused by"
echo >&2 "*** a failure to locate libclang. See output and docs below for details:"
echo >&2 "***"
echo >&2 "$bindgen_libclang_output"
echo >&2 "***"
exit 1
fi

# `bindgen` returned successfully, thus use the output to check that the version
# of the `libclang` found by the Rust bindings generator is suitable.
#
# Unlike other version checks, note that this one does not necessarily appear
# in the first line of the output, thus no `sed` address is provided.
bindgen_libclang_version=$( \
LC_ALL=C "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_libclang.h 2>&1 >/dev/null \
| grep -F 'clang version ' \
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+' \
| head -n 1 \
echo "$bindgen_libclang_output" \
| sed -nE 's:.*clang version ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
)
if [ -z "$bindgen_libclang_version" ]; then
echo >&2 "***"
echo >&2 "*** Running '$BINDGEN' to check the libclang version (used by the Rust"
echo >&2 "*** bindings generator) did not return an expected output. See output"
echo >&2 "*** and docs below for details:"
echo >&2 "***"
echo >&2 "$bindgen_libclang_output"
echo >&2 "***"
exit 1
fi
bindgen_libclang_min_version=$($min_tool_version llvm)
bindgen_libclang_cversion=$(get_canonical_version $bindgen_libclang_version)
bindgen_libclang_min_cversion=$(get_canonical_version $bindgen_libclang_min_version)
if [ "$bindgen_libclang_cversion" -lt "$bindgen_libclang_min_cversion" ]; then
if [ "$1" = -v ]; then
echo >&2 "***"
echo >&2 "*** libclang (used by the Rust bindings generator '$BINDGEN') is too old."
echo >&2 "*** Your version: $bindgen_libclang_version"
echo >&2 "*** Minimum version: $bindgen_libclang_min_version"
echo >&2 "***"
fi
echo >&2 "***"
echo >&2 "*** libclang (used by the Rust bindings generator '$BINDGEN') is too old."
echo >&2 "*** Your version: $bindgen_libclang_version"
echo >&2 "*** Minimum version: $bindgen_libclang_min_version"
echo >&2 "***"
exit 1
fi

@@ -125,21 +229,20 @@ fi
#
# In the future, we might be able to perform a full version check, see
# https://github.com/rust-lang/rust-bindgen/issues/2138.
if [ "$1" = -v ]; then
cc_name=$($(dirname $0)/cc-version.sh "$CC" | cut -f1 -d' ')
if [ "$cc_name" = Clang ]; then
clang_version=$( \
LC_ALL=C "$CC" --version 2>/dev/null \
| sed -nE '1s:.*version ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
)
if [ "$clang_version" != "$bindgen_libclang_version" ]; then
echo >&2 "***"
echo >&2 "*** libclang (used by the Rust bindings generator '$BINDGEN')"
echo >&2 "*** version does not match Clang's. This may be a problem."
echo >&2 "*** libclang version: $bindgen_libclang_version"
echo >&2 "*** Clang version: $clang_version"
echo >&2 "***"
fi
cc_name=$($(dirname $0)/cc-version.sh $CC | cut -f1 -d' ')
if [ "$cc_name" = Clang ]; then
clang_version=$( \
LC_ALL=C $CC --version 2>/dev/null \
| sed -nE '1s:.*version ([0-9]+\.[0-9]+\.[0-9]+).*:\1:p'
)
if [ "$clang_version" != "$bindgen_libclang_version" ]; then
echo >&2 "***"
echo >&2 "*** libclang (used by the Rust bindings generator '$BINDGEN')"
echo >&2 "*** version does not match Clang's. This may be a problem."
echo >&2 "*** libclang version: $bindgen_libclang_version"
echo >&2 "*** Clang version: $clang_version"
echo >&2 "***"
warning=1
fi
fi

@@ -150,11 +253,9 @@ rustc_sysroot=$("$RUSTC" $KRUSTFLAGS --print sysroot)
rustc_src=${RUST_LIB_SRC:-"$rustc_sysroot/lib/rustlib/src/rust/library"}
rustc_src_core="$rustc_src/core/src/lib.rs"
if [ ! -e "$rustc_src_core" ]; then
if [ "$1" = -v ]; then
echo >&2 "***"
echo >&2 "*** Source code for the 'core' standard library could not be found"
echo >&2 "*** at '$rustc_src_core'."
echo >&2 "***"
fi
echo >&2 "***"
echo >&2 "*** Source code for the 'core' standard library could not be found"
echo >&2 "*** at '$rustc_src_core'."
echo >&2 "***"
exit 1
fi
346 changes: 346 additions & 0 deletions scripts/rust_is_available_test.py

Large diffs are not rendered by default.

73 changes: 73 additions & 0 deletions scripts/rustdoc_test_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-2.0

//! Test builder for `rustdoc`-generated tests.
//!
//! This script is a hack to extract the test from `rustdoc`'s output. Ideally, `rustdoc` would
//! have an option to generate this information instead, e.g. as JSON output.
//!
//! The `rustdoc`-generated test names look like `{file}_{line}_{number}`, e.g.
//! `...path_rust_kernel_sync_arc_rs_42_0`. `number` is the "test number", needed in cases like
//! a macro that expands into items with doctests is invoked several times within the same line.
//!
//! However, since these names are used for bisection in CI, the line number makes it not stable
//! at all. In the future, we would like `rustdoc` to give us the Rust item path associated with
//! the test, plus a "test number" (for cases with several examples per item) and generate a name
//! from that. For the moment, we generate ourselves a new name, `{file}_{number}` instead, in
//! the `gen` script (done there since we need to be aware of all the tests in a given file).
use std::fs::File;
use std::io::{BufWriter, Read, Write};

fn main() {
let mut stdin = std::io::stdin().lock();
let mut body = String::new();
stdin.read_to_string(&mut body).unwrap();

// Find the generated function name looking for the inner function inside `main()`.
//
// The line we are looking for looks like one of the following:
//
// ```
// fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_28_0() {
// fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_37_0() -> Result<(), impl core::fmt::Debug> {
// ```
//
// It should be unlikely that doctest code matches such lines (when code is formatted properly).
let rustdoc_function_name = body
.lines()
.find_map(|line| {
Some(
line.split_once("fn main() {")?
.1
.split_once("fn ")?
.1
.split_once("()")?
.0,
)
.filter(|x| x.chars().all(|c| c.is_alphanumeric() || c == '_'))
})
.expect("No test function found in `rustdoc`'s output.");

// Qualify `Result` to avoid the collision with our own `Result` coming from the prelude.
let body = body.replace(
&format!("{rustdoc_function_name}() -> Result<(), impl core::fmt::Debug> {{"),
&format!("{rustdoc_function_name}() -> core::result::Result<(), impl core::fmt::Debug> {{"),
);

// For tests that get generated with `Result`, like above, `rustdoc` generates an `unwrap()` on
// the return value to check there were no returned errors. Instead, we use our assert macro
// since we want to just fail the test, not panic the kernel.
//
// We save the result in a variable so that the failed assertion message looks nicer.
let body = body.replace(
&format!("}} {rustdoc_function_name}().unwrap() }}"),
&format!("}} let test_return_value = {rustdoc_function_name}(); assert!(test_return_value.is_ok()); }}"),
);

// Figure out a smaller test name based on the generated function name.
let name = rustdoc_function_name.split_once("_rust_kernel_").unwrap().1;

let path = format!("rust/test/doctests/kernel/{name}");

write!(BufWriter::new(File::create(path).unwrap()), "{body}").unwrap();
}
162 changes: 162 additions & 0 deletions scripts/rustdoc_test_gen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// SPDX-License-Identifier: GPL-2.0

//! Generates KUnit tests from saved `rustdoc`-generated tests.
//!
//! KUnit passes a context (`struct kunit *`) to each test, which should be forwarded to the other
//! KUnit functions and macros.
//!
//! However, we want to keep this as an implementation detail because:
//!
//! - Test code should not care about the implementation.
//!
//! - Documentation looks worse if it needs to carry extra details unrelated to the piece
//! being described.
//!
//! - Test code should be able to define functions and call them, without having to carry
//! the context.
//!
//! - Later on, we may want to be able to test non-kernel code (e.g. `core`, `alloc` or
//! third-party crates) which likely use the standard library `assert*!` macros.
//!
//! For this reason, instead of the passed context, `kunit_get_current_test()` is used instead
//! (i.e. `current->kunit_test`).
//!
//! Note that this means other threads/tasks potentially spawned by a given test, if failing, will
//! report the failure in the kernel log but will not fail the actual test. Saving the pointer in
//! e.g. a `static` per test does not fully solve the issue either, because currently KUnit does
//! not support assertions (only expectations) from other tasks. Thus leave that feature for
//! the future, which simplifies the code here too. We could also simply not allow `assert`s in
//! other tasks, but that seems overly constraining, and we do want to support them, eventually.
use std::io::{BufWriter, Read, Write};
use std::{fs, fs::File};

fn main() {
let mut paths = fs::read_dir("rust/test/doctests/kernel")
.unwrap()
.map(|entry| entry.unwrap().path())
.collect::<Vec<_>>();

// Sort paths for clarity.
paths.sort();

let mut rust_tests = String::new();
let mut c_test_declarations = String::new();
let mut c_test_cases = String::new();
let mut body = String::new();
let mut last_file = String::new();
let mut number = 0;
for path in paths {
// The `name` follows the `{file}_{line}_{number}` pattern (see description in
// `scripts/rustdoc_test_builder.rs`). Discard the `number`.
let name = path.file_name().unwrap().to_str().unwrap().to_string();

// Extract the `file` and the `line`, discarding the `number`.
let (file, line) = name.rsplit_once('_').unwrap().0.rsplit_once('_').unwrap();

// Generate an ID sequence ("test number") for each one in the file.
if file == last_file {
number += 1;
} else {
number = 0;
last_file = file.to_string();
}

// Generate a KUnit name (i.e. test name and C symbol) for this test.
//
// We avoid the line number, like `rustdoc` does, to make things slightly more stable for
// bisection purposes. However, to aid developers in mapping back what test failed, we will
// print a diagnostics line in the KTAP report.
let kunit_name = format!("rust_doctest_kernel_{file}_{number}");

// Read the test's text contents to dump it below.
body.clear();
File::open(path).unwrap().read_to_string(&mut body).unwrap();

let line = line.parse::<core::ffi::c_int>().unwrap();

use std::fmt::Write;
write!(
rust_tests,
r#"/// Generated `{name}` KUnit test case from a Rust documentation test.
#[no_mangle]
pub extern "C" fn {kunit_name}(__kunit_test: *mut kernel::bindings::kunit) {{
/// Overrides the usual [`assert!`] macro with one that calls KUnit instead.
#[allow(unused)]
macro_rules! assert {{
($cond:expr $(,)?) => {{{{
kernel::kunit_assert!("{kunit_name}", $cond);
}}}}
}}
/// Overrides the usual [`assert_eq!`] macro with one that calls KUnit instead.
#[allow(unused)]
macro_rules! assert_eq {{
($left:expr, $right:expr $(,)?) => {{{{
kernel::kunit_assert_eq!("{kunit_name}", $left, $right);
}}}}
}}
// Many tests need the prelude, so provide it by default.
#[allow(unused)]
use kernel::prelude::*;
// Display line number so that developers can map the test easily to the source code.
kernel::kunit::info(format_args!(" # Doctest from line {line}\n"));
{{
{body}
main();
}}
}}
"#
)
.unwrap();

write!(c_test_declarations, "void {kunit_name}(struct kunit *);\n").unwrap();
write!(c_test_cases, " KUNIT_CASE({kunit_name}),\n").unwrap();
}

let rust_tests = rust_tests.trim();
let c_test_declarations = c_test_declarations.trim();
let c_test_cases = c_test_cases.trim();

write!(
BufWriter::new(File::create("rust/doctests_kernel_generated.rs").unwrap()),
r#"//! `kernel` crate documentation tests.
const __LOG_PREFIX: &[u8] = b"rust_doctests_kernel\0";
{rust_tests}
"#
)
.unwrap();

write!(
BufWriter::new(File::create("rust/doctests_kernel_generated_kunit.c").unwrap()),
r#"/*
* `kernel` crate documentation tests.
*/
#include <kunit/test.h>
{c_test_declarations}
static struct kunit_case test_cases[] = {{
{c_test_cases}
{{ }}
}};
static struct kunit_suite test_suite = {{
.name = "rust_doctests_kernel",
.test_cases = test_cases,
}};
kunit_test_suite(test_suite);
MODULE_LICENSE("GPL");
"#
)
.unwrap();
}