diff options
| author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2026-04-20 04:28:57 +0300 |
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2026-04-20 04:28:57 +0300 |
| commit | f4b369c6fe0ceaba2da2daff8c9eb415f85926dd (patch) | |
| tree | 30465d0a429b2c224685b5d8e804bf053c4d129a /samples | |
| parent | ff14dafde15c11403fac61367a34fea08926e9ee (diff) | |
| parent | 2ca45e57ea027fffe3350ae5e21ad9cecb0dce74 (diff) | |
| download | linux-f4b369c6fe0ceaba2da2daff8c9eb415f85926dd.tar.xz | |
Merge branch 'next' into for-linus
Prepare input updates for 7.1 merge window.
Diffstat (limited to 'samples')
36 files changed, 823 insertions, 337 deletions
diff --git a/samples/Kconfig b/samples/Kconfig index 6e072a5f1ed8..5bc7c9e5a59e 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -23,11 +23,11 @@ config SAMPLE_TRACE_CUSTOM_EVENTS This builds the custom trace event example module. config SAMPLE_TRACE_PRINTK - tristate "Build trace_printk module - tests various trace_printk formats" + tristate "Build trace_printk module - tests various trace_printk formats" depends on EVENT_TRACING && m help - This builds a module that calls trace_printk() and can be used to - test various trace_printk() calls from a module. + This builds a module that calls trace_printk() and can be used to + test various trace_printk() calls from a module. config SAMPLE_FTRACE_DIRECT tristate "Build register_ftrace_direct() example" @@ -54,11 +54,11 @@ config SAMPLE_FTRACE_OPS measures the time taken to invoke one function a number of times. config SAMPLE_TRACE_ARRAY - tristate "Build sample module for kernel access to Ftrace instances" + tristate "Build sample module for kernel access to Ftrace instances" depends on EVENT_TRACING && m help - This builds a module that demonstrates the use of various APIs to - access Ftrace instances from within the kernel. + This builds a module that demonstrates the use of various APIs to + access Ftrace instances from within the kernel. config SAMPLE_KOBJECT tristate "Build kobject examples" @@ -290,11 +290,11 @@ config SAMPLE_CORESIGHT_SYSCFG configurations and easily load them into the system at runtime. config SAMPLE_KMEMLEAK - tristate "Simple test for the kernel memory leak detector" - depends on DEBUG_KMEMLEAK && m - help - Build a sample program which have explicitly leaks memory to test - kmemleak + tristate "Simple test for the kernel memory leak detector" + depends on DEBUG_KMEMLEAK && m + help + Build a sample program which have explicitly leaks memory to test + kmemleak. config SAMPLE_CGROUP bool "Build cgroup sample code" diff --git a/samples/bpf/do_hbm_test.sh b/samples/bpf/do_hbm_test.sh index 38e4599350db..7f4f722787d5 100755 --- a/samples/bpf/do_hbm_test.sh +++ b/samples/bpf/do_hbm_test.sh @@ -112,7 +112,7 @@ function start_hbm () { processArgs () { for i in $args ; do case $i in - # Support for upcomming ingress rate limiting + # Support for upcoming ingress rate limiting #in) # support for upcoming ingress rate limiting # dir="-i" # dir_name="in" diff --git a/samples/bpf/hbm.c b/samples/bpf/hbm.c index bf66277115e2..fc88d4dbdf48 100644 --- a/samples/bpf/hbm.c +++ b/samples/bpf/hbm.c @@ -5,7 +5,7 @@ * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * - * Example program for Host Bandwidth Managment + * Example program for Host Bandwidth Management * * This program loads a cgroup skb BPF program to enforce cgroup output * (egress) or input (ingress) bandwidth limits. @@ -24,7 +24,7 @@ * beyond the rate limit specified while there is available * bandwidth. Current implementation assumes there is only * NIC (eth0), but can be extended to support multiple NICs. - * Currrently only supported for egress. + * Currently only supported for egress. * -h Print this info * prog BPF program file name. Name defaults to hbm_out_kern.o */ diff --git a/samples/bpf/tcp_cong_kern.c b/samples/bpf/tcp_cong_kern.c index 2311fc9dde85..339415eac477 100644 --- a/samples/bpf/tcp_cong_kern.c +++ b/samples/bpf/tcp_cong_kern.c @@ -5,7 +5,7 @@ * License as published by the Free Software Foundation. * * BPF program to set congestion control to dctcp when both hosts are - * in the same datacenter (as deteremined by IPv6 prefix). + * in the same datacenter (as determined by IPv6 prefix). * * Use "bpftool cgroup attach $cg sock_ops $prog" to load this BPF program. */ diff --git a/samples/bpf/tracex1.bpf.c b/samples/bpf/tracex1.bpf.c index 0ab39d76ff8f..ceedf0b1d479 100644 --- a/samples/bpf/tracex1.bpf.c +++ b/samples/bpf/tracex1.bpf.c @@ -20,7 +20,7 @@ SEC("kprobe.multi/__netif_receive_skb_core*") int bpf_prog1(struct pt_regs *ctx) { /* attaches to kprobe __netif_receive_skb_core, - * looks for packets on loobpack device and prints them + * looks for packets on loopback device and prints them * (wildcard is used for avoiding symbol mismatch due to optimization) */ char devname[IFNAMSIZ]; diff --git a/samples/configfs/configfs_sample.c b/samples/configfs/configfs_sample.c index fd5d163828c5..c1b108ec4ea0 100644 --- a/samples/configfs/configfs_sample.c +++ b/samples/configfs/configfs_sample.c @@ -158,7 +158,7 @@ static void simple_child_release(struct config_item *item) kfree(to_simple_child(item)); } -static struct configfs_item_operations simple_child_item_ops = { +static const struct configfs_item_operations simple_child_item_ops = { .release = simple_child_release, }; @@ -215,7 +215,7 @@ static void simple_children_release(struct config_item *item) kfree(to_simple_children(item)); } -static struct configfs_item_operations simple_children_item_ops = { +static const struct configfs_item_operations simple_children_item_ops = { .release = simple_children_release, }; @@ -223,7 +223,7 @@ static struct configfs_item_operations simple_children_item_ops = { * Note that, since no extra work is required on ->drop_item(), * no ->drop_item() is provided. */ -static struct configfs_group_operations simple_children_group_ops = { +static const struct configfs_group_operations simple_children_group_ops = { .make_item = simple_children_make_item, }; @@ -292,7 +292,7 @@ static struct configfs_attribute *group_children_attrs[] = { * Note that, since no extra work is required on ->drop_item(), * no ->drop_item() is provided. */ -static struct configfs_group_operations group_children_group_ops = { +static const struct configfs_group_operations group_children_group_ops = { .make_group = group_children_make_group, }; diff --git a/samples/ftrace/ftrace-direct-modify.c b/samples/ftrace/ftrace-direct-modify.c index da3a9f2091f5..1ba1927b548e 100644 --- a/samples/ftrace/ftrace-direct-modify.c +++ b/samples/ftrace/ftrace-direct-modify.c @@ -176,8 +176,8 @@ asm ( " st.d $t0, $sp, 0\n" " st.d $ra, $sp, 8\n" " bl my_direct_func1\n" -" ld.d $t0, $sp, 0\n" -" ld.d $ra, $sp, 8\n" +" ld.d $ra, $sp, 0\n" +" ld.d $t0, $sp, 8\n" " addi.d $sp, $sp, 16\n" " jr $t0\n" " .size my_tramp1, .-my_tramp1\n" @@ -189,8 +189,8 @@ asm ( " st.d $t0, $sp, 0\n" " st.d $ra, $sp, 8\n" " bl my_direct_func2\n" -" ld.d $t0, $sp, 0\n" -" ld.d $ra, $sp, 8\n" +" ld.d $ra, $sp, 0\n" +" ld.d $t0, $sp, 8\n" " addi.d $sp, $sp, 16\n" " jr $t0\n" " .size my_tramp2, .-my_tramp2\n" diff --git a/samples/ftrace/ftrace-direct-multi-modify.c b/samples/ftrace/ftrace-direct-multi-modify.c index 8f7986d698d8..7a7822dfeb50 100644 --- a/samples/ftrace/ftrace-direct-multi-modify.c +++ b/samples/ftrace/ftrace-direct-multi-modify.c @@ -199,8 +199,8 @@ asm ( " move $a0, $t0\n" " bl my_direct_func1\n" " ld.d $a0, $sp, 0\n" -" ld.d $t0, $sp, 8\n" -" ld.d $ra, $sp, 16\n" +" ld.d $ra, $sp, 8\n" +" ld.d $t0, $sp, 16\n" " addi.d $sp, $sp, 32\n" " jr $t0\n" " .size my_tramp1, .-my_tramp1\n" @@ -215,8 +215,8 @@ asm ( " move $a0, $t0\n" " bl my_direct_func2\n" " ld.d $a0, $sp, 0\n" -" ld.d $t0, $sp, 8\n" -" ld.d $ra, $sp, 16\n" +" ld.d $ra, $sp, 8\n" +" ld.d $t0, $sp, 16\n" " addi.d $sp, $sp, 32\n" " jr $t0\n" " .size my_tramp2, .-my_tramp2\n" diff --git a/samples/ftrace/ftrace-direct-multi.c b/samples/ftrace/ftrace-direct-multi.c index db326c81a27d..3fe6ddaf0b69 100644 --- a/samples/ftrace/ftrace-direct-multi.c +++ b/samples/ftrace/ftrace-direct-multi.c @@ -131,8 +131,8 @@ asm ( " move $a0, $t0\n" " bl my_direct_func\n" " ld.d $a0, $sp, 0\n" -" ld.d $t0, $sp, 8\n" -" ld.d $ra, $sp, 16\n" +" ld.d $ra, $sp, 8\n" +" ld.d $t0, $sp, 16\n" " addi.d $sp, $sp, 32\n" " jr $t0\n" " .size my_tramp, .-my_tramp\n" diff --git a/samples/ftrace/ftrace-direct-too.c b/samples/ftrace/ftrace-direct-too.c index 3d0fa260332d..bf2411aa6fd7 100644 --- a/samples/ftrace/ftrace-direct-too.c +++ b/samples/ftrace/ftrace-direct-too.c @@ -143,8 +143,8 @@ asm ( " ld.d $a0, $sp, 0\n" " ld.d $a1, $sp, 8\n" " ld.d $a2, $sp, 16\n" -" ld.d $t0, $sp, 24\n" -" ld.d $ra, $sp, 32\n" +" ld.d $ra, $sp, 24\n" +" ld.d $t0, $sp, 32\n" " addi.d $sp, $sp, 48\n" " jr $t0\n" " .size my_tramp, .-my_tramp\n" diff --git a/samples/ftrace/ftrace-direct.c b/samples/ftrace/ftrace-direct.c index 956834b0d19a..5368c8c39cbb 100644 --- a/samples/ftrace/ftrace-direct.c +++ b/samples/ftrace/ftrace-direct.c @@ -124,8 +124,8 @@ asm ( " st.d $ra, $sp, 16\n" " bl my_direct_func\n" " ld.d $a0, $sp, 0\n" -" ld.d $t0, $sp, 8\n" -" ld.d $ra, $sp, 16\n" +" ld.d $ra, $sp, 8\n" +" ld.d $t0, $sp, 16\n" " addi.d $sp, $sp, 32\n" " jr $t0\n" " .size my_tramp, .-my_tramp\n" diff --git a/samples/kobject/kset-example.c b/samples/kobject/kset-example.c index 579ce150217c..d0103904e5dd 100644 --- a/samples/kobject/kset-example.c +++ b/samples/kobject/kset-example.c @@ -37,10 +37,11 @@ struct foo_obj { /* a custom attribute that works just for a struct foo_obj. */ struct foo_attribute { struct attribute attr; - ssize_t (*show)(struct foo_obj *foo, struct foo_attribute *attr, char *buf); - ssize_t (*store)(struct foo_obj *foo, struct foo_attribute *attr, const char *buf, size_t count); + ssize_t (*show)(struct foo_obj *foo, const struct foo_attribute *attr, char *buf); + ssize_t (*store)(struct foo_obj *foo, const struct foo_attribute *attr, + const char *buf, size_t count); }; -#define to_foo_attr(x) container_of(x, struct foo_attribute, attr) +#define to_foo_attr(x) container_of_const(x, struct foo_attribute, attr) /* * The default show function that must be passed to sysfs. This will be @@ -53,7 +54,7 @@ static ssize_t foo_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { - struct foo_attribute *attribute; + const struct foo_attribute *attribute; struct foo_obj *foo; attribute = to_foo_attr(attr); @@ -73,7 +74,7 @@ static ssize_t foo_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len) { - struct foo_attribute *attribute; + const struct foo_attribute *attribute; struct foo_obj *foo; attribute = to_foo_attr(attr); @@ -109,13 +110,13 @@ static void foo_release(struct kobject *kobj) /* * The "foo" file where the .foo variable is read from and written to. */ -static ssize_t foo_show(struct foo_obj *foo_obj, struct foo_attribute *attr, +static ssize_t foo_show(struct foo_obj *foo_obj, const struct foo_attribute *attr, char *buf) { return sysfs_emit(buf, "%d\n", foo_obj->foo); } -static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr, +static ssize_t foo_store(struct foo_obj *foo_obj, const struct foo_attribute *attr, const char *buf, size_t count) { int ret; @@ -128,14 +129,14 @@ static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr, } /* Sysfs attributes cannot be world-writable. */ -static struct foo_attribute foo_attribute = +static const struct foo_attribute foo_attribute = __ATTR(foo, 0664, foo_show, foo_store); /* * More complex function where we determine which variable is being accessed by * looking at the attribute for the "baz" and "bar" files. */ -static ssize_t b_show(struct foo_obj *foo_obj, struct foo_attribute *attr, +static ssize_t b_show(struct foo_obj *foo_obj, const struct foo_attribute *attr, char *buf) { int var; @@ -147,7 +148,7 @@ static ssize_t b_show(struct foo_obj *foo_obj, struct foo_attribute *attr, return sysfs_emit(buf, "%d\n", var); } -static ssize_t b_store(struct foo_obj *foo_obj, struct foo_attribute *attr, +static ssize_t b_store(struct foo_obj *foo_obj, const struct foo_attribute *attr, const char *buf, size_t count) { int var, ret; @@ -163,22 +164,37 @@ static ssize_t b_store(struct foo_obj *foo_obj, struct foo_attribute *attr, return count; } -static struct foo_attribute baz_attribute = +static const struct foo_attribute baz_attribute = __ATTR(baz, 0664, b_show, b_store); -static struct foo_attribute bar_attribute = +static const struct foo_attribute bar_attribute = __ATTR(bar, 0664, b_show, b_store); /* * Create a group of attributes so that we can create and destroy them all * at once. */ -static struct attribute *foo_default_attrs[] = { +static const struct attribute *const foo_default_attrs[] = { &foo_attribute.attr, &baz_attribute.attr, &bar_attribute.attr, NULL, /* need to NULL terminate the list of attributes */ }; -ATTRIBUTE_GROUPS(foo_default); + +static umode_t foo_default_attrs_is_visible(struct kobject *kobj, + const struct attribute *attr, + int n) +{ + /* Hide attributes with the same name as the kobject. */ + if (strcmp(kobject_name(kobj), attr->name) == 0) + return 0; + return attr->mode; +} + +static const struct attribute_group foo_default_group = { + .attrs_const = foo_default_attrs, + .is_visible_const = foo_default_attrs_is_visible, +}; +__ATTRIBUTE_GROUPS(foo_default); /* * Our own ktype for our kobjects. Here we specify our sysfs ops, the diff --git a/samples/qmi/qmi_sample_client.c b/samples/qmi/qmi_sample_client.c index b27d861f354f..d1814582319b 100644 --- a/samples/qmi/qmi_sample_client.c +++ b/samples/qmi/qmi_sample_client.c @@ -468,7 +468,7 @@ static int qmi_sample_probe(struct platform_device *pdev) return ret; sq = dev_get_platdata(&pdev->dev); - ret = kernel_connect(sample->qmi.sock, (struct sockaddr *)sq, + ret = kernel_connect(sample->qmi.sock, (struct sockaddr_unsized *)sq, sizeof(*sq), 0); if (ret < 0) { pr_err("failed to connect to remote service port\n"); diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index c376eb899b7a..c49ab9106345 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -84,6 +84,29 @@ config SAMPLE_RUST_DEBUGFS_SCOPED If unsure, say N. +config SAMPLE_RUST_DRIVER_I2C + tristate "I2C Driver" + depends on I2C=y + help + This option builds the Rust I2C driver sample. + + To compile this as a module, choose M here: + the module will be called rust_driver_i2c. + + If unsure, say N. + +config SAMPLE_RUST_I2C_CLIENT + tristate "I2C Client Registration" + depends on I2C=y + help + This option builds the Rust I2C client manual creation + sample. + + To compile this as a module, choose M here: + the module will be called rust_i2c_client. + + If unsure, say N. + config SAMPLE_RUST_DRIVER_PCI tristate "PCI Driver" depends on PCI @@ -91,7 +114,7 @@ config SAMPLE_RUST_DRIVER_PCI This option builds the Rust PCI driver sample. To compile this as a module, choose M here: - the module will be called driver_pci. + the module will be called rust_driver_pci. If unsure, say N. @@ -107,7 +130,7 @@ config SAMPLE_RUST_DRIVER_PLATFORM config SAMPLE_RUST_DRIVER_USB tristate "USB Driver" - depends on USB = y && BROKEN + depends on USB = y help This option builds the Rust USB driver sample. @@ -138,6 +161,17 @@ config SAMPLE_RUST_DRIVER_AUXILIARY If unsure, say N. +config SAMPLE_RUST_SOC + tristate "SoC Driver" + select SOC_BUS + help + This option builds the Rust SoC driver sample. + + To compile this as a module, choose M here: + the module will be called rust_soc. + + If unsure, say N. + config SAMPLE_RUST_HOSTPROGS bool "Host programs" help diff --git a/samples/rust/Makefile b/samples/rust/Makefile index cf8422f8f219..6c0aaa58cccc 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -7,12 +7,15 @@ obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o obj-$(CONFIG_SAMPLE_RUST_DEBUGFS) += rust_debugfs.o obj-$(CONFIG_SAMPLE_RUST_DEBUGFS_SCOPED) += rust_debugfs_scoped.o obj-$(CONFIG_SAMPLE_RUST_DMA) += rust_dma.o +obj-$(CONFIG_SAMPLE_RUST_DRIVER_I2C) += rust_driver_i2c.o +obj-$(CONFIG_SAMPLE_RUST_I2C_CLIENT) += rust_i2c_client.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) += rust_driver_pci.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_USB) += rust_driver_usb.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) += rust_driver_faux.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY) += rust_driver_auxiliary.o obj-$(CONFIG_SAMPLE_RUST_CONFIGFS) += rust_configfs.o +obj-$(CONFIG_SAMPLE_RUST_SOC) += rust_soc.o rust_print-y := rust_print_main.o rust_print_events.o diff --git a/samples/rust/rust_configfs.rs b/samples/rust/rust_configfs.rs index 0ccc7553ef39..a1bd9db6010d 100644 --- a/samples/rust/rust_configfs.rs +++ b/samples/rust/rust_configfs.rs @@ -3,7 +3,6 @@ //! Rust configfs sample. use kernel::alloc::flags; -use kernel::c_str; use kernel::configfs; use kernel::configfs::configfs_attrs; use kernel::new_mutex; @@ -35,7 +34,7 @@ struct Configuration { impl Configuration { fn new() -> impl PinInit<Self, Error> { try_pin_init!(Self { - message: c_str!("Hello World\n"), + message: c"Hello World\n", bar <- new_mutex!((KBox::new([0; PAGE_SIZE], flags::GFP_KERNEL)?, 0)), }) } @@ -61,7 +60,7 @@ impl kernel::InPlaceModule for RustConfigfs { try_pin_init!(Self { config <- configfs::Subsystem::new( - c_str!("rust_configfs"), item_type, Configuration::new() + c"rust_configfs", item_type, Configuration::new() ), }) } diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs index 82b61a15a34b..0963efe19f93 100644 --- a/samples/rust/rust_debugfs.rs +++ b/samples/rust/rust_debugfs.rs @@ -32,15 +32,28 @@ //! ``` use core::str::FromStr; -use core::sync::atomic::AtomicUsize; -use core::sync::atomic::Ordering; -use kernel::c_str; -use kernel::debugfs::{Dir, File}; -use kernel::new_mutex; -use kernel::prelude::*; -use kernel::sync::Mutex; - -use kernel::{acpi, device::Core, of, platform, str::CString, types::ARef}; +use kernel::{ + acpi, + debugfs::{ + Dir, + File, // + }, + device::Core, + new_mutex, + of, + platform, + prelude::*, + sizes::*, + str::CString, + sync::{ + aref::ARef, + atomic::{ + Atomic, + Relaxed, // + }, + Mutex, + }, // +}; kernel::module_platform_driver! { type: RustDebugFs, @@ -59,9 +72,13 @@ struct RustDebugFs { #[pin] _compatible: File<CString>, #[pin] - counter: File<AtomicUsize>, + counter: File<Atomic<usize>>, #[pin] inner: File<Mutex<Inner>>, + #[pin] + array_blob: File<Mutex<[u8; 4]>>, + #[pin] + vector_blob: File<Mutex<KVec<u8>>>, } #[derive(Debug)] @@ -95,7 +112,7 @@ kernel::acpi_device_table!( ACPI_TABLE, MODULE_ACPI_TABLE, <RustDebugFs as platform::Driver>::IdInfo, - [(acpi::DeviceId::new(c_str!("LNUXBEEF")), ())] + [(acpi::DeviceId::new(c"LNUXBEEF"), ())] ); impl platform::Driver for RustDebugFs { @@ -106,43 +123,52 @@ impl platform::Driver for RustDebugFs { fn probe( pdev: &platform::Device<Core>, _info: Option<&Self::IdInfo>, - ) -> Result<Pin<KBox<Self>>> { - let result = KBox::try_pin_init(RustDebugFs::new(pdev), GFP_KERNEL)?; - // We can still mutate fields through the files which are atomic or mutexed: - result.counter.store(91, Ordering::Relaxed); - { - let mut guard = result.inner.lock(); - guard.x = guard.y; - guard.y = 42; - } - Ok(result) + ) -> impl PinInit<Self, Error> { + RustDebugFs::new(pdev).pin_chain(|this| { + this.counter.store(91, Relaxed); + { + let mut guard = this.inner.lock(); + guard.x = guard.y; + guard.y = 42; + } + + Ok(()) + }) } } impl RustDebugFs { - fn build_counter(dir: &Dir) -> impl PinInit<File<AtomicUsize>> + '_ { - dir.read_write_file(c_str!("counter"), AtomicUsize::new(0)) + fn build_counter(dir: &Dir) -> impl PinInit<File<Atomic<usize>>> + '_ { + dir.read_write_file(c"counter", Atomic::<usize>::new(0)) } fn build_inner(dir: &Dir) -> impl PinInit<File<Mutex<Inner>>> + '_ { - dir.read_write_file(c_str!("pair"), new_mutex!(Inner { x: 3, y: 10 })) + dir.read_write_file(c"pair", new_mutex!(Inner { x: 3, y: 10 })) } fn new(pdev: &platform::Device<Core>) -> impl PinInit<Self, Error> + '_ { - let debugfs = Dir::new(c_str!("sample_debugfs")); + let debugfs = Dir::new(c"sample_debugfs"); let dev = pdev.as_ref(); try_pin_init! { Self { _compatible <- debugfs.read_only_file( - c_str!("compatible"), + c"compatible", dev.fwnode() .ok_or(ENOENT)? - .property_read::<CString>(c_str!("compatible")) + .property_read::<CString>(c"compatible") .required_by(dev)?, ), counter <- Self::build_counter(&debugfs), inner <- Self::build_inner(&debugfs), + array_blob <- debugfs.read_write_binary_file( + c"array_blob", + new_mutex!([0x62, 0x6c, 0x6f, 0x62]), + ), + vector_blob <- debugfs.read_write_binary_file( + c"vector_blob", + new_mutex!(kernel::kvec!(0x42; SZ_4K)?), + ), _debugfs: debugfs, pdev: pdev.into(), } diff --git a/samples/rust/rust_debugfs_scoped.rs b/samples/rust/rust_debugfs_scoped.rs index b0c4e76b123e..6a575a15a2c2 100644 --- a/samples/rust/rust_debugfs_scoped.rs +++ b/samples/rust/rust_debugfs_scoped.rs @@ -6,11 +6,20 @@ //! `Scope::dir` to create a variety of files without the need to separately //! track them all. -use core::sync::atomic::AtomicUsize; -use kernel::debugfs::{Dir, Scope}; -use kernel::prelude::*; -use kernel::sync::Mutex; -use kernel::{c_str, new_mutex, str::CString}; +use kernel::{ + debugfs::{ + Dir, + Scope, // + }, + new_mutex, + prelude::*, + sizes::*, + str::CString, + sync::{ + atomic::Atomic, + Mutex, // + }, +}; module! { type: RustScopedDebugFs, @@ -38,7 +47,7 @@ fn remove_file_write( mod_data .devices .lock() - .retain(|device| device.name.as_bytes() != to_remove.as_bytes()); + .retain(|device| device.name.to_bytes() != to_remove.to_bytes()); Ok(()) } @@ -62,22 +71,26 @@ fn create_file_write( let file_name = CString::try_from_fmt(fmt!("{name_str}"))?; for sub in items { nums.push( - AtomicUsize::new(sub.parse().map_err(|_| EINVAL)?), + Atomic::<usize>::new(sub.parse().map_err(|_| EINVAL)?), GFP_KERNEL, )?; } + let blob = KBox::pin_init(new_mutex!([0x42; SZ_4K]), GFP_KERNEL)?; let scope = KBox::pin_init( - mod_data - .device_dir - .scope(DeviceData { name, nums }, &file_name, |dev_data, dir| { + mod_data.device_dir.scope( + DeviceData { name, nums, blob }, + &file_name, + |dev_data, dir| { for (idx, val) in dev_data.nums.iter().enumerate() { let Ok(name) = CString::try_from_fmt(fmt!("{idx}")) else { return; }; dir.read_write_file(&name, val); } - }), + dir.read_write_binary_file(c"blob", &dev_data.blob); + }, + ), GFP_KERNEL, )?; (*mod_data.devices.lock()).push(scope, GFP_KERNEL)?; @@ -109,24 +122,21 @@ impl ModuleData { struct DeviceData { name: CString, - nums: KVec<AtomicUsize>, + nums: KVec<Atomic<usize>>, + blob: Pin<KBox<Mutex<[u8; SZ_4K]>>>, } fn init_control(base_dir: &Dir, dyn_dirs: Dir) -> impl PinInit<Scope<ModuleData>> + '_ { - base_dir.scope( - ModuleData::init(dyn_dirs), - c_str!("control"), - |data, dir| { - dir.write_only_callback_file(c_str!("create"), data, &create_file_write); - dir.write_only_callback_file(c_str!("remove"), data, &remove_file_write); - }, - ) + base_dir.scope(ModuleData::init(dyn_dirs), c"control", |data, dir| { + dir.write_only_callback_file(c"create", data, &create_file_write); + dir.write_only_callback_file(c"remove", data, &remove_file_write); + }) } impl kernel::Module for RustScopedDebugFs { fn init(_module: &'static kernel::ThisModule) -> Result<Self> { - let base_dir = Dir::new(c_str!("rust_scoped_debugfs")); - let dyn_dirs = base_dir.subdir(c_str!("dynamic")); + let base_dir = Dir::new(c"rust_scoped_debugfs"); + let dyn_dirs = base_dir.subdir(c"dynamic"); Ok(Self { _data: KBox::pin_init(init_control(&base_dir, dyn_dirs), GFP_KERNEL)?, }) diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs index 4d324f06cc2a..ce39b5545097 100644 --- a/samples/rust/rust_dma.rs +++ b/samples/rust/rust_dma.rs @@ -55,62 +55,64 @@ impl pci::Driver for DmaSampleDriver { type IdInfo = (); const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; - fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> { - dev_info!(pdev.as_ref(), "Probe DMA test driver.\n"); + fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> { + pin_init::pin_init_scope(move || { + dev_info!(pdev, "Probe DMA test driver.\n"); - let mask = DmaMask::new::<64>(); + let mask = DmaMask::new::<64>(); - // SAFETY: There are no concurrent calls to DMA allocation and mapping primitives. - unsafe { pdev.dma_set_mask_and_coherent(mask)? }; + // SAFETY: There are no concurrent calls to DMA allocation and mapping primitives. + unsafe { pdev.dma_set_mask_and_coherent(mask)? }; - let ca: CoherentAllocation<MyStruct> = - CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?; + let ca: CoherentAllocation<MyStruct> = + CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?; - for (i, value) in TEST_VALUES.into_iter().enumerate() { - kernel::dma_write!(ca[i] = MyStruct::new(value.0, value.1))?; - } + for (i, value) in TEST_VALUES.into_iter().enumerate() { + kernel::dma_write!(ca, [i]?, MyStruct::new(value.0, value.1)); + } - let size = 4 * page::PAGE_SIZE; - let pages = VVec::with_capacity(size, GFP_KERNEL)?; + let size = 4 * page::PAGE_SIZE; + let pages = VVec::with_capacity(size, GFP_KERNEL)?; - let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL); + let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL); - let drvdata = KBox::pin_init( - try_pin_init!(Self { + Ok(try_pin_init!(Self { pdev: pdev.into(), ca, sgt <- sgt, - }), - GFP_KERNEL, - )?; + })) + }) + } +} + +impl DmaSampleDriver { + fn check_dma(&self) -> Result { + for (i, value) in TEST_VALUES.into_iter().enumerate() { + let val0 = kernel::dma_read!(self.ca, [i]?.h); + let val1 = kernel::dma_read!(self.ca, [i]?.b); - Ok(drvdata) + assert_eq!(val0, value.0); + assert_eq!(val1, value.1); + } + + Ok(()) } } #[pinned_drop] impl PinnedDrop for DmaSampleDriver { fn drop(self: Pin<&mut Self>) { - let dev = self.pdev.as_ref(); - - dev_info!(dev, "Unload DMA test driver.\n"); - - for (i, value) in TEST_VALUES.into_iter().enumerate() { - let val0 = kernel::dma_read!(self.ca[i].h); - let val1 = kernel::dma_read!(self.ca[i].b); - assert!(val0.is_ok()); - assert!(val1.is_ok()); + dev_info!(self.pdev, "Unload DMA test driver.\n"); - if let Ok(val0) = val0 { - assert_eq!(val0, value.0); - } - if let Ok(val1) = val1 { - assert_eq!(val1, value.1); - } - } + assert!(self.check_dma().is_ok()); for (i, entry) in self.sgt.iter().enumerate() { - dev_info!(dev, "Entry[{}]: DMA address: {:#x}", i, entry.dma_address()); + dev_info!( + self.pdev, + "Entry[{}]: DMA address: {:#x}", + i, + entry.dma_address(), + ); } } } diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 55ece336ee45..5c5a5105a3ff 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -5,13 +5,22 @@ //! To make this driver probe, QEMU must be run with `-device pci-testdev`. use kernel::{ - auxiliary, c_str, device::Core, driver, error::Error, pci, prelude::*, InPlaceModule, + auxiliary, + device::{ + Bound, + Core, // + }, + devres::Devres, + driver, + pci, + prelude::*, + InPlaceModule, // }; -use pin_init::PinInit; +use core::any::TypeId; const MODULE_NAME: &CStr = <LocalModule as kernel::ModuleMetadata>::NAME; -const AUXILIARY_NAME: &CStr = c_str!("auxiliary"); +const AUXILIARY_NAME: &CStr = c"auxiliary"; struct AuxiliaryDriver; @@ -27,23 +36,26 @@ impl auxiliary::Driver for AuxiliaryDriver { const ID_TABLE: auxiliary::IdTable<Self::IdInfo> = &AUX_TABLE; - fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> { + fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> { dev_info!( - adev.as_ref(), + adev, "Probing auxiliary driver for auxiliary device with id={}\n", adev.id() ); ParentDriver::connect(adev)?; - let this = KBox::new(Self, GFP_KERNEL)?; - - Ok(this.into()) + Ok(Self) } } +#[pin_data] struct ParentDriver { - _reg: [auxiliary::Registration; 2], + private: TypeId, + #[pin] + _reg0: Devres<auxiliary::Registration>, + #[pin] + _reg1: Devres<auxiliary::Registration>, } kernel::pci_device_table!( @@ -58,35 +70,35 @@ impl pci::Driver for ParentDriver { const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; - fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> { - let this = KBox::new( - Self { - _reg: [ - auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 0, MODULE_NAME)?, - auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 1, MODULE_NAME)?, - ], - }, - GFP_KERNEL, - )?; - - Ok(this.into()) + fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> { + try_pin_init!(Self { + private: TypeId::of::<Self>(), + _reg0 <- auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 0, MODULE_NAME), + _reg1 <- auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 1, MODULE_NAME), + }) } } impl ParentDriver { - fn connect(adev: &auxiliary::Device) -> Result<()> { - let parent = adev.parent().ok_or(EINVAL)?; - let pdev: &pci::Device = parent.try_into()?; + fn connect(adev: &auxiliary::Device<Bound>) -> Result { + let dev = adev.parent(); + let pdev: &pci::Device<Bound> = dev.try_into()?; + let drvdata = dev.drvdata::<Self>()?; - let vendor = pdev.vendor_id(); dev_info!( - adev.as_ref(), + dev, "Connect auxiliary {} with parent: VendorID={}, DeviceID={:#x}\n", adev.id(), - vendor, + pdev.vendor_id(), pdev.device_id() ); + dev_info!( + dev, + "We have access to the private data of {:?}.\n", + drvdata.private + ); + Ok(()) } } diff --git a/samples/rust/rust_driver_faux.rs b/samples/rust/rust_driver_faux.rs index ecc9fd378cbd..99876c8e3743 100644 --- a/samples/rust/rust_driver_faux.rs +++ b/samples/rust/rust_driver_faux.rs @@ -2,7 +2,11 @@ //! Rust faux device sample. -use kernel::{c_str, faux, prelude::*, Module}; +use kernel::{ + faux, + prelude::*, + Module, // +}; module! { type: SampleModule, @@ -20,9 +24,9 @@ impl Module for SampleModule { fn init(_module: &'static ThisModule) -> Result<Self> { pr_info!("Initialising Rust Faux Device Sample\n"); - let reg = faux::Registration::new(c_str!("rust-faux-sample-device"), None)?; + let reg = faux::Registration::new(c"rust-faux-sample-device", None)?; - dev_info!(reg.as_ref(), "Hello from faux device!\n"); + dev_info!(reg, "Hello from faux device!\n"); Ok(Self { _reg: reg }) } diff --git a/samples/rust/rust_driver_i2c.rs b/samples/rust/rust_driver_i2c.rs new file mode 100644 index 000000000000..6be79f9e9fb5 --- /dev/null +++ b/samples/rust/rust_driver_i2c.rs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust I2C driver sample. + +use kernel::{ + acpi, + device::Core, + i2c, + of, + prelude::*, // +}; + +struct SampleDriver; + +kernel::acpi_device_table! { + ACPI_TABLE, + MODULE_ACPI_TABLE, + <SampleDriver as i2c::Driver>::IdInfo, + [(acpi::DeviceId::new(c"LNUXBEEF"), 0)] +} + +kernel::i2c_device_table! { + I2C_TABLE, + MODULE_I2C_TABLE, + <SampleDriver as i2c::Driver>::IdInfo, + [(i2c::DeviceId::new(c"rust_driver_i2c"), 0)] +} + +kernel::of_device_table! { + OF_TABLE, + MODULE_OF_TABLE, + <SampleDriver as i2c::Driver>::IdInfo, + [(of::DeviceId::new(c"test,rust_driver_i2c"), 0)] +} + +impl i2c::Driver for SampleDriver { + type IdInfo = u32; + + const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE); + const I2C_ID_TABLE: Option<i2c::IdTable<Self::IdInfo>> = Some(&I2C_TABLE); + const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); + + fn probe( + idev: &i2c::I2cClient<Core>, + info: Option<&Self::IdInfo>, + ) -> impl PinInit<Self, Error> { + let dev = idev.as_ref(); + + dev_info!(dev, "Probe Rust I2C driver sample.\n"); + + if let Some(info) = info { + dev_info!(dev, "Probed with info: '{}'.\n", info); + } + + Ok(Self) + } + + fn shutdown(idev: &i2c::I2cClient<Core>, _this: Pin<&Self>) { + dev_info!(idev.as_ref(), "Shutdown Rust I2C driver sample.\n"); + } + + fn unbind(idev: &i2c::I2cClient<Core>, _this: Pin<&Self>) { + dev_info!(idev.as_ref(), "Unbind Rust I2C driver sample.\n"); + } +} + +kernel::module_i2c_driver! { + type: SampleDriver, + name: "rust_driver_i2c", + authors: ["Igor Korotin"], + description: "Rust I2C driver", + license: "GPL v2", +} diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index 55a683c39ed9..d3d4a7931deb 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -4,7 +4,15 @@ //! //! To make this driver probe, QEMU must be run with `-device pci-testdev`. -use kernel::{c_str, device::Core, devres::Devres, pci, prelude::*, sync::aref::ARef}; +use kernel::{ + device::Bound, + device::Core, + devres::Devres, + io::Io, + pci, + prelude::*, + sync::aref::ARef, // +}; struct Regs; @@ -48,7 +56,7 @@ impl SampleDriver { // Select the test. bar.write8(index.0, Regs::TEST); - let offset = u32::from_le(bar.read32(Regs::OFFSET)) as usize; + let offset = bar.read32(Regs::OFFSET) as usize; let data = bar.read8(Regs::DATA); // Write `data` to `offset` to increase `count` by one. @@ -58,42 +66,66 @@ impl SampleDriver { Ok(bar.read32(Regs::COUNT)) } -} -impl pci::Driver for SampleDriver { - type IdInfo = TestIndex; - - const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; + fn config_space(pdev: &pci::Device<Bound>) { + let config = pdev.config_space(); - fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> { - let vendor = pdev.vendor_id(); - dev_dbg!( - pdev.as_ref(), - "Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n", - vendor, - pdev.device_id() + // TODO: use the register!() macro for defining PCI configuration space registers once it + // has been move out of nova-core. + dev_info!( + pdev, + "pci-testdev config space read8 rev ID: {:x}\n", + config.read8(0x8) ); - pdev.enable_device_mem()?; - pdev.set_master(); - - let drvdata = KBox::pin_init( - try_pin_init!(Self { - bar <- pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci")), - pdev: pdev.into(), - index: *info, - }), - GFP_KERNEL, - )?; + dev_info!( + pdev, + "pci-testdev config space read16 vendor ID: {:x}\n", + config.read16(0) + ); - let bar = drvdata.bar.access(pdev.as_ref())?; dev_info!( - pdev.as_ref(), - "pci-testdev data-match count: {}\n", - Self::testdev(info, bar)? + pdev, + "pci-testdev config space read32 BAR 0: {:x}\n", + config.read32(0x10) ); + } +} + +impl pci::Driver for SampleDriver { + type IdInfo = TestIndex; + + const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE; - Ok(drvdata) + fn probe(pdev: &pci::Device<Core>, info: &Self::IdInfo) -> impl PinInit<Self, Error> { + pin_init::pin_init_scope(move || { + let vendor = pdev.vendor_id(); + dev_dbg!( + pdev, + "Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n", + vendor, + pdev.device_id() + ); + + pdev.enable_device_mem()?; + pdev.set_master(); + + Ok(try_pin_init!(Self { + bar <- pdev.iomap_region_sized::<{ Regs::END }>(0, c"rust_driver_pci"), + index: *info, + _: { + let bar = bar.access(pdev.as_ref())?; + + dev_info!( + pdev, + "pci-testdev data-match count: {}\n", + Self::testdev(info, bar)? + ); + Self::config_space(pdev); + }, + pdev: pdev.into(), + })) + }) } fn unbind(pdev: &pci::Device<Core>, this: Pin<&Self>) { @@ -107,7 +139,7 @@ impl pci::Driver for SampleDriver { #[pinned_drop] impl PinnedDrop for SampleDriver { fn drop(self: Pin<&mut Self>) { - dev_dbg!(self.pdev.as_ref(), "Remove Rust PCI driver sample.\n"); + dev_dbg!(self.pdev, "Remove Rust PCI driver sample.\n"); } } diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs index 6473baf4f120..f2229d176fb9 100644 --- a/samples/rust/rust_driver_platform.rs +++ b/samples/rust/rust_driver_platform.rs @@ -63,16 +63,20 @@ //! use kernel::{ - acpi, c_str, + acpi, device::{ self, - property::{FwNodeReferenceArgs, NArgs}, + property::{ + FwNodeReferenceArgs, + NArgs, // + }, Core, }, - of, platform, + of, + platform, prelude::*, str::CString, - sync::aref::ARef, + sync::aref::ARef, // }; struct SampleDriver { @@ -85,14 +89,14 @@ kernel::of_device_table!( OF_TABLE, MODULE_OF_TABLE, <SampleDriver as platform::Driver>::IdInfo, - [(of::DeviceId::new(c_str!("test,rust-device")), Info(42))] + [(of::DeviceId::new(c"test,rust-device"), Info(42))] ); kernel::acpi_device_table!( ACPI_TABLE, MODULE_ACPI_TABLE, <SampleDriver as platform::Driver>::IdInfo, - [(acpi::DeviceId::new(c_str!("LNUXBEEF")), Info(0))] + [(acpi::DeviceId::new(c"LNUXBEEF"), Info(0))] ); impl platform::Driver for SampleDriver { @@ -103,7 +107,7 @@ impl platform::Driver for SampleDriver { fn probe( pdev: &platform::Device<Core>, info: Option<&Self::IdInfo>, - ) -> Result<Pin<KBox<Self>>> { + ) -> impl PinInit<Self, Error> { let dev = pdev.as_ref(); dev_dbg!(dev, "Probe Rust Platform driver sample.\n"); @@ -116,9 +120,7 @@ impl platform::Driver for SampleDriver { Self::properties_parse(dev)?; } - let drvdata = KBox::new(Self { pdev: pdev.into() }, GFP_KERNEL)?; - - Ok(drvdata.into()) + Ok(Self { pdev: pdev.into() }) } } @@ -126,49 +128,47 @@ impl SampleDriver { fn properties_parse(dev: &device::Device) -> Result { let fwnode = dev.fwnode().ok_or(ENOENT)?; - if let Ok(idx) = - fwnode.property_match_string(c_str!("compatible"), c_str!("test,rust-device")) - { + if let Ok(idx) = fwnode.property_match_string(c"compatible", c"test,rust-device") { dev_info!(dev, "matched compatible string idx = {}\n", idx); } - let name = c_str!("compatible"); + let name = c"compatible"; let prop = fwnode.property_read::<CString>(name).required_by(dev)?; dev_info!(dev, "'{name}'='{prop:?}'\n"); - let name = c_str!("test,bool-prop"); - let prop = fwnode.property_read_bool(c_str!("test,bool-prop")); + let name = c"test,bool-prop"; + let prop = fwnode.property_read_bool(c"test,bool-prop"); dev_info!(dev, "'{name}'='{prop}'\n"); - if fwnode.property_present(c_str!("test,u32-prop")) { + if fwnode.property_present(c"test,u32-prop") { dev_info!(dev, "'test,u32-prop' is present\n"); } - let name = c_str!("test,u32-optional-prop"); + let name = c"test,u32-optional-prop"; let prop = fwnode.property_read::<u32>(name).or(0x12); - dev_info!(dev, "'{name}'='{prop:#x}' (default = 0x12)\n",); + dev_info!(dev, "'{name}'='{prop:#x}' (default = 0x12)\n"); // A missing required property will print an error. Discard the error to // prevent properties_parse from failing in that case. - let name = c_str!("test,u32-required-prop"); + let name = c"test,u32-required-prop"; let _ = fwnode.property_read::<u32>(name).required_by(dev); - let name = c_str!("test,u32-prop"); + let name = c"test,u32-prop"; let prop: u32 = fwnode.property_read(name).required_by(dev)?; dev_info!(dev, "'{name}'='{prop:#x}'\n"); - let name = c_str!("test,i16-array"); + let name = c"test,i16-array"; let prop: [i16; 4] = fwnode.property_read(name).required_by(dev)?; dev_info!(dev, "'{name}'='{prop:?}'\n"); let len = fwnode.property_count_elem::<u16>(name)?; - dev_info!(dev, "'{name}' length is {len}\n",); + dev_info!(dev, "'{name}' length is {len}\n"); - let name = c_str!("test,i16-array"); + let name = c"test,i16-array"; let prop: KVec<i16> = fwnode.property_read_array_vec(name, 4)?.required_by(dev)?; dev_info!(dev, "'{name}'='{prop:?}' (KVec)\n"); for child in fwnode.children() { - let name = c_str!("test,ref-arg"); + let name = c"test,ref-arg"; let nargs = NArgs::N(2); let prop: FwNodeReferenceArgs = child.property_get_reference_args(name, nargs, 0)?; dev_info!(dev, "'{name}'='{prop:?}'\n"); @@ -180,7 +180,7 @@ impl SampleDriver { impl Drop for SampleDriver { fn drop(&mut self) { - dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n"); + dev_dbg!(self.pdev, "Remove Rust Platform driver sample.\n"); } } diff --git a/samples/rust/rust_driver_usb.rs b/samples/rust/rust_driver_usb.rs index 5c396f421de7..ab72e99e1274 100644 --- a/samples/rust/rust_driver_usb.rs +++ b/samples/rust/rust_driver_usb.rs @@ -3,7 +3,15 @@ //! Rust USB driver sample. -use kernel::{device, device::Core, prelude::*, sync::aref::ARef, usb}; +use kernel::{ + device::{ + self, + Core, // + }, + prelude::*, + sync::aref::ARef, + usb, // +}; struct SampleDriver { _intf: ARef<usb::Interface>, @@ -24,12 +32,11 @@ impl usb::Driver for SampleDriver { intf: &usb::Interface<Core>, _id: &usb::DeviceId, _info: &Self::IdInfo, - ) -> Result<Pin<KBox<Self>>> { + ) -> impl PinInit<Self, Error> { let dev: &device::Device<Core> = intf.as_ref(); dev_info!(dev, "Rust USB driver sample probed\n"); - let drvdata = KBox::new(Self { _intf: intf.into() }, GFP_KERNEL)?; - Ok(drvdata.into()) + Ok(Self { _intf: intf.into() }) } fn disconnect(intf: &usb::Interface<Core>, _data: Pin<&Self>) { diff --git a/samples/rust/rust_i2c_client.rs b/samples/rust/rust_i2c_client.rs new file mode 100644 index 000000000000..8d2c12e535b0 --- /dev/null +++ b/samples/rust/rust_i2c_client.rs @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust I2C client registration sample. +//! +//! An I2C client in Rust cannot exist on its own. To register a new I2C client, +//! it must be bound to a parent device. In this sample driver, a platform device +//! is used as the parent. +//! + +//! ACPI match table test +//! +//! This demonstrates how to test an ACPI-based Rust I2C client registration driver +//! using QEMU with a custom SSDT. +//! +//! Steps: +//! +//! 1. **Create an SSDT source file** (`ssdt.dsl`) with the following content: +//! +//! ```asl +//! DefinitionBlock ("", "SSDT", 2, "TEST", "VIRTACPI", 0x00000001) +//! { +//! Scope (\_SB) +//! { +//! Device (T432) +//! { +//! Name (_HID, "LNUXBEEF") // ACPI hardware ID to match +//! Name (_UID, 1) +//! Name (_STA, 0x0F) // Device present, enabled +//! Name (_CRS, ResourceTemplate () +//! { +//! Memory32Fixed (ReadWrite, 0xFED00000, 0x1000) +//! }) +//! } +//! } +//! } +//! ``` +//! +//! 2. **Compile the table**: +//! +//! ```sh +//! iasl -tc ssdt.dsl +//! ``` +//! +//! This generates `ssdt.aml` +//! +//! 3. **Run QEMU** with the compiled AML file: +//! +//! ```sh +//! qemu-system-x86_64 -m 512M \ +//! -enable-kvm \ +//! -kernel path/to/bzImage \ +//! -append "root=/dev/sda console=ttyS0" \ +//! -hda rootfs.img \ +//! -serial stdio \ +//! -acpitable file=ssdt.aml +//! ``` +//! +//! Requirements: +//! - The `rust_driver_platform` must be present either: +//! - built directly into the kernel (`bzImage`), or +//! - available as a `.ko` file and loadable from `rootfs.img` +//! +//! 4. **Verify it worked** by checking `dmesg`: +//! +//! ``` +//! rust_driver_platform LNUXBEEF:00: Probed with info: '0'. +//! ``` +//! + +use kernel::{ + acpi, + device, + devres::Devres, + i2c, + of, + platform, + prelude::*, + sync::aref::ARef, // +}; + +#[pin_data] +struct SampleDriver { + parent_dev: ARef<platform::Device>, + #[pin] + _reg: Devres<i2c::Registration>, +} + +kernel::of_device_table!( + OF_TABLE, + MODULE_OF_TABLE, + <SampleDriver as platform::Driver>::IdInfo, + [(of::DeviceId::new(c"test,rust-device"), ())] +); + +kernel::acpi_device_table!( + ACPI_TABLE, + MODULE_ACPI_TABLE, + <SampleDriver as platform::Driver>::IdInfo, + [(acpi::DeviceId::new(c"LNUXBEEF"), ())] +); + +const SAMPLE_I2C_CLIENT_ADDR: u16 = 0x30; +const SAMPLE_I2C_ADAPTER_INDEX: i32 = 0; +const BOARD_INFO: i2c::I2cBoardInfo = + i2c::I2cBoardInfo::new(c"rust_driver_i2c", SAMPLE_I2C_CLIENT_ADDR); + +impl platform::Driver for SampleDriver { + type IdInfo = (); + const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); + const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE); + + fn probe( + pdev: &platform::Device<device::Core>, + _info: Option<&Self::IdInfo>, + ) -> impl PinInit<Self, Error> { + dev_info!( + pdev.as_ref(), + "Probe Rust I2C Client registration sample.\n" + ); + + kernel::try_pin_init!( Self { + parent_dev: pdev.into(), + + _reg <- { + let adapter = i2c::I2cAdapter::get(SAMPLE_I2C_ADAPTER_INDEX)?; + + i2c::Registration::new(&adapter, &BOARD_INFO, pdev.as_ref()) + } + }) + } + + fn unbind(pdev: &platform::Device<device::Core>, _this: Pin<&Self>) { + dev_info!( + pdev.as_ref(), + "Unbind Rust I2C Client registration sample.\n" + ); + } +} + +kernel::module_platform_driver! { + type: SampleDriver, + name: "rust_device_i2c", + authors: ["Danilo Krummrich", "Igor Korotin"], + description: "Rust I2C client registration", + license: "GPL v2", +} diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs index 1fc7a1be6b6d..8eb9583571d7 100644 --- a/samples/rust/rust_minimal.rs +++ b/samples/rust/rust_minimal.rs @@ -10,6 +10,12 @@ module! { authors: ["Rust for Linux Contributors"], description: "Rust minimal sample", license: "GPL", + params: { + test_parameter: i64 { + default: 1, + description: "This parameter has a default of 1", + }, + }, } struct RustMinimal { @@ -20,6 +26,10 @@ impl kernel::Module for RustMinimal { fn init(_module: &'static ThisModule) -> Result<Self> { pr_info!("Rust minimal sample (init)\n"); pr_info!("Am I built-in? {}\n", !cfg!(MODULE)); + pr_info!( + "test_parameter: {}\n", + *module_parameters::test_parameter.value() + ); let mut numbers = KVec::new(); numbers.push(72, GFP_KERNEL)?; diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_device.rs index d69bc33dbd99..87a1fe63533a 100644 --- a/samples/rust/rust_misc_device.rs +++ b/samples/rust/rust_misc_device.rs @@ -95,10 +95,7 @@ //! } //! ``` -use core::pin::Pin; - use kernel::{ - c_str, device::Device, fs::{File, Kiocb}, ioctl::{_IO, _IOC_SIZE, _IOR, _IOW}, @@ -133,7 +130,7 @@ impl kernel::InPlaceModule for RustMiscDeviceModule { pr_info!("Initialising Rust Misc Device Sample\n"); let options = MiscDeviceOptions { - name: c_str!("rust-misc-device"), + name: c"rust-misc-device", }; try_pin_init!(Self { diff --git a/samples/rust/rust_print_main.rs b/samples/rust/rust_print_main.rs index 4095c72afeab..682207c81fc2 100644 --- a/samples/rust/rust_print_main.rs +++ b/samples/rust/rust_print_main.rs @@ -101,7 +101,7 @@ impl Drop for RustPrint { } mod trace { - use kernel::ffi::c_int; + use kernel::prelude::*; kernel::declare_trace! { /// # Safety diff --git a/samples/rust/rust_soc.rs b/samples/rust/rust_soc.rs new file mode 100644 index 000000000000..8079c1c48416 --- /dev/null +++ b/samples/rust/rust_soc.rs @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust SoC Platform driver sample. + +use kernel::{ + acpi, + device::Core, + of, + platform, + prelude::*, + soc, + str::CString, + sync::aref::ARef, // +}; +use pin_init::pin_init_scope; + +#[pin_data] +struct SampleSocDriver { + pdev: ARef<platform::Device>, + #[pin] + _dev_reg: soc::Registration, +} + +kernel::of_device_table!( + OF_TABLE, + MODULE_OF_TABLE, + <SampleSocDriver as platform::Driver>::IdInfo, + [(of::DeviceId::new(c"test,rust-device"), ())] +); + +kernel::acpi_device_table!( + ACPI_TABLE, + MODULE_ACPI_TABLE, + <SampleSocDriver as platform::Driver>::IdInfo, + [(acpi::DeviceId::new(c"LNUXBEEF"), ())] +); + +impl platform::Driver for SampleSocDriver { + type IdInfo = (); + const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE); + const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE); + + fn probe( + pdev: &platform::Device<Core>, + _info: Option<&Self::IdInfo>, + ) -> impl PinInit<Self, Error> { + dev_dbg!(pdev, "Probe Rust SoC driver sample.\n"); + + let pdev = pdev.into(); + pin_init_scope(move || { + let machine = CString::try_from(c"My cool ACME15 dev board")?; + let family = CString::try_from(c"ACME")?; + let revision = CString::try_from(c"1.2")?; + let serial_number = CString::try_from(c"12345")?; + let soc_id = CString::try_from(c"ACME15")?; + + let attr = soc::Attributes { + machine: Some(machine), + family: Some(family), + revision: Some(revision), + serial_number: Some(serial_number), + soc_id: Some(soc_id), + }; + + Ok(try_pin_init!(SampleSocDriver { + pdev: pdev, + _dev_reg <- soc::Registration::new(attr), + }? Error)) + }) + } +} + +kernel::module_platform_driver! { + type: SampleSocDriver, + name: "rust_soc", + authors: ["Matthew Maurer"], + description: "Rust SoC Driver", + license: "GPL", +} diff --git a/samples/vfio-mdev/mbochs.c b/samples/vfio-mdev/mbochs.c index 18623ba666e3..64ea19253ee3 100644 --- a/samples/vfio-mdev/mbochs.c +++ b/samples/vfio-mdev/mbochs.c @@ -143,11 +143,6 @@ static struct mdev_parent mbochs_parent; static atomic_t mbochs_avail_mbytes; static const struct vfio_device_ops mbochs_dev_ops; -struct vfio_region_info_ext { - struct vfio_region_info base; - struct vfio_region_info_cap_type type; -}; - struct mbochs_mode { u32 drm_format; u32 bytepp; @@ -1033,10 +1028,12 @@ static int mbochs_dmabuf_export(struct mbochs_dmabuf *dmabuf) return 0; } -static int mbochs_get_region_info(struct mdev_state *mdev_state, - struct vfio_region_info_ext *ext) +static int mbochs_ioctl_get_region_info(struct vfio_device *vdev, + struct vfio_region_info *region_info, + struct vfio_info_cap *caps) { - struct vfio_region_info *region_info = &ext->base; + struct mdev_state *mdev_state = + container_of(vdev, struct mdev_state, vdev); if (region_info->index >= MBOCHS_NUM_REGIONS) return -EINVAL; @@ -1061,20 +1058,23 @@ static int mbochs_get_region_info(struct mdev_state *mdev_state, region_info->flags = (VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE); break; - case MBOCHS_EDID_REGION_INDEX: - ext->base.argsz = sizeof(*ext); - ext->base.offset = MBOCHS_EDID_OFFSET; - ext->base.size = MBOCHS_EDID_SIZE; - ext->base.flags = (VFIO_REGION_INFO_FLAG_READ | - VFIO_REGION_INFO_FLAG_WRITE | - VFIO_REGION_INFO_FLAG_CAPS); - ext->base.cap_offset = offsetof(typeof(*ext), type); - ext->type.header.id = VFIO_REGION_INFO_CAP_TYPE; - ext->type.header.version = 1; - ext->type.header.next = 0; - ext->type.type = VFIO_REGION_TYPE_GFX; - ext->type.subtype = VFIO_REGION_SUBTYPE_GFX_EDID; - break; + case MBOCHS_EDID_REGION_INDEX: { + struct vfio_region_info_cap_type cap_type = { + .header.id = VFIO_REGION_INFO_CAP_TYPE, + .header.version = 1, + .type = VFIO_REGION_TYPE_GFX, + .subtype = VFIO_REGION_SUBTYPE_GFX_EDID, + }; + + region_info->offset = MBOCHS_EDID_OFFSET; + region_info->size = MBOCHS_EDID_SIZE; + region_info->flags = (VFIO_REGION_INFO_FLAG_READ | + VFIO_REGION_INFO_FLAG_WRITE | + VFIO_REGION_INFO_FLAG_CAPS); + + return vfio_info_add_capability(caps, &cap_type.header, + sizeof(cap_type)); + } default: region_info->size = 0; region_info->offset = 0; @@ -1191,7 +1191,7 @@ static long mbochs_ioctl(struct vfio_device *vdev, unsigned int cmd, struct mdev_state *mdev_state = container_of(vdev, struct mdev_state, vdev); int ret = 0; - unsigned long minsz, outsz; + unsigned long minsz; switch (cmd) { case VFIO_DEVICE_GET_INFO: @@ -1215,30 +1215,6 @@ static long mbochs_ioctl(struct vfio_device *vdev, unsigned int cmd, return 0; } - case VFIO_DEVICE_GET_REGION_INFO: - { - struct vfio_region_info_ext info; - - minsz = offsetofend(typeof(info), base.offset); - - if (copy_from_user(&info, (void __user *)arg, minsz)) - return -EFAULT; - - outsz = info.base.argsz; - if (outsz < minsz) - return -EINVAL; - if (outsz > sizeof(info)) - return -EINVAL; - - ret = mbochs_get_region_info(mdev_state, &info); - if (ret) - return ret; - - if (copy_to_user((void __user *)arg, &info, outsz)) - return -EFAULT; - - return 0; - } case VFIO_DEVICE_GET_IRQ_INFO: { @@ -1376,6 +1352,7 @@ static const struct vfio_device_ops mbochs_dev_ops = { .read = mbochs_read, .write = mbochs_write, .ioctl = mbochs_ioctl, + .get_region_info_caps = mbochs_ioctl_get_region_info, .mmap = mbochs_mmap, .bind_iommufd = vfio_iommufd_emulated_bind, .unbind_iommufd = vfio_iommufd_emulated_unbind, diff --git a/samples/vfio-mdev/mdpy.c b/samples/vfio-mdev/mdpy.c index 8104831ae125..0759bd68edca 100644 --- a/samples/vfio-mdev/mdpy.c +++ b/samples/vfio-mdev/mdpy.c @@ -435,10 +435,13 @@ static int mdpy_mmap(struct vfio_device *vdev, struct vm_area_struct *vma) return remap_vmalloc_range(vma, mdev_state->memblk, 0); } -static int mdpy_get_region_info(struct mdev_state *mdev_state, - struct vfio_region_info *region_info, - u16 *cap_type_id, void **cap_type) +static int mdpy_ioctl_get_region_info(struct vfio_device *vdev, + struct vfio_region_info *region_info, + struct vfio_info_cap *caps) { + struct mdev_state *mdev_state = + container_of(vdev, struct mdev_state, vdev); + if (region_info->index >= VFIO_PCI_NUM_REGIONS && region_info->index != MDPY_DISPLAY_REGION) return -EINVAL; @@ -544,30 +547,6 @@ static long mdpy_ioctl(struct vfio_device *vdev, unsigned int cmd, return 0; } - case VFIO_DEVICE_GET_REGION_INFO: - { - struct vfio_region_info info; - u16 cap_type_id = 0; - void *cap_type = NULL; - - minsz = offsetofend(struct vfio_region_info, offset); - - if (copy_from_user(&info, (void __user *)arg, minsz)) - return -EFAULT; - - if (info.argsz < minsz) - return -EINVAL; - - ret = mdpy_get_region_info(mdev_state, &info, &cap_type_id, - &cap_type); - if (ret) - return ret; - - if (copy_to_user((void __user *)arg, &info, minsz)) - return -EFAULT; - - return 0; - } case VFIO_DEVICE_GET_IRQ_INFO: { @@ -665,6 +644,7 @@ static const struct vfio_device_ops mdpy_dev_ops = { .read = mdpy_read, .write = mdpy_write, .ioctl = mdpy_ioctl, + .get_region_info_caps = mdpy_ioctl_get_region_info, .mmap = mdpy_mmap, .bind_iommufd = vfio_iommufd_emulated_bind, .unbind_iommufd = vfio_iommufd_emulated_unbind, diff --git a/samples/vfio-mdev/mtty.c b/samples/vfio-mdev/mtty.c index 59eefe2fed10..bd92c38379b8 100644 --- a/samples/vfio-mdev/mtty.c +++ b/samples/vfio-mdev/mtty.c @@ -624,7 +624,7 @@ static void handle_bar_read(unsigned int index, struct mdev_state *mdev_state, u8 lsr = 0; mutex_lock(&mdev_state->rxtx_lock); - /* atleast one char in FIFO */ + /* at least one char in FIFO */ if (mdev_state->s[index].rxtx.head != mdev_state->s[index].rxtx.tail) lsr |= UART_LSR_DR; @@ -1717,10 +1717,12 @@ static int mtty_set_irqs(struct mdev_state *mdev_state, uint32_t flags, return ret; } -static int mtty_get_region_info(struct mdev_state *mdev_state, - struct vfio_region_info *region_info, - u16 *cap_type_id, void **cap_type) +static int mtty_ioctl_get_region_info(struct vfio_device *vdev, + struct vfio_region_info *region_info, + struct vfio_info_cap *caps) { + struct mdev_state *mdev_state = + container_of(vdev, struct mdev_state, vdev); unsigned int size = 0; u32 bar_index; @@ -1817,30 +1819,6 @@ static long mtty_ioctl(struct vfio_device *vdev, unsigned int cmd, return 0; } - case VFIO_DEVICE_GET_REGION_INFO: - { - struct vfio_region_info info; - u16 cap_type_id = 0; - void *cap_type = NULL; - - minsz = offsetofend(struct vfio_region_info, offset); - - if (copy_from_user(&info, (void __user *)arg, minsz)) - return -EFAULT; - - if (info.argsz < minsz) - return -EINVAL; - - ret = mtty_get_region_info(mdev_state, &info, &cap_type_id, - &cap_type); - if (ret) - return ret; - - if (copy_to_user((void __user *)arg, &info, minsz)) - return -EFAULT; - - return 0; - } case VFIO_DEVICE_GET_IRQ_INFO: { @@ -1949,6 +1927,7 @@ static const struct vfio_device_ops mtty_dev_ops = { .read = mtty_read, .write = mtty_write, .ioctl = mtty_ioctl, + .get_region_info_caps = mtty_ioctl_get_region_info, .bind_iommufd = vfio_iommufd_emulated_bind, .unbind_iommufd = vfio_iommufd_emulated_unbind, .attach_ioas = vfio_iommufd_emulated_attach_ioas, diff --git a/samples/vfs/Makefile b/samples/vfs/Makefile index 6554b73a75c8..9256ca5d762b 100644 --- a/samples/vfs/Makefile +++ b/samples/vfs/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only userprogs-always-y += test-fsmount test-statx mountinfo test-list-all-mounts +userccflags += -I $(srctree)/tools/testing/selftests/ userccflags += -I usr/include diff --git a/samples/workqueue/stall_detector/Makefile b/samples/workqueue/stall_detector/Makefile new file mode 100644 index 000000000000..8849e85e95bb --- /dev/null +++ b/samples/workqueue/stall_detector/Makefile @@ -0,0 +1 @@ +obj-m += wq_stall.o diff --git a/samples/workqueue/stall_detector/wq_stall.c b/samples/workqueue/stall_detector/wq_stall.c new file mode 100644 index 000000000000..6f4a497b1881 --- /dev/null +++ b/samples/workqueue/stall_detector/wq_stall.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * wq_stall - Test module for the workqueue stall detector. + * + * Deliberately creates a workqueue stall so the watchdog fires and + * prints diagnostic output. Useful for verifying that the stall + * detector correctly identifies stuck workers and produces useful + * backtraces. + * + * The stall is triggered by clearing PF_WQ_WORKER before sleeping, + * which hides the worker from the concurrency manager. A second + * work item queued on the same pool then sits in the worklist with + * no worker available to process it. + * + * After ~30s the workqueue watchdog fires: + * BUG: workqueue lockup - pool cpus=N ... + * + * Build: + * make -C <kernel tree> M=samples/workqueue/stall_detector modules + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + * Copyright (c) 2026 Breno Leitao <leitao@debian.org> + */ + +#include <linux/module.h> +#include <linux/workqueue.h> +#include <linux/wait.h> +#include <linux/atomic.h> +#include <linux/sched.h> + +static DECLARE_WAIT_QUEUE_HEAD(stall_wq_head); +static atomic_t wake_condition = ATOMIC_INIT(0); +static struct work_struct stall_work1; +static struct work_struct stall_work2; + +static void stall_work2_fn(struct work_struct *work) +{ + pr_info("wq_stall: second work item finally ran\n"); +} + +static void stall_work1_fn(struct work_struct *work) +{ + pr_info("wq_stall: first work item running on cpu %d\n", + raw_smp_processor_id()); + + /* + * Queue second item while we're still counted as running + * (pool->nr_running > 0). Since schedule_work() on a per-CPU + * workqueue targets raw_smp_processor_id(), item 2 lands on the + * same pool. __queue_work -> kick_pool -> need_more_worker() + * sees nr_running > 0 and does NOT wake a new worker. + */ + schedule_work(&stall_work2); + + /* + * Hide from the workqueue concurrency manager. Without + * PF_WQ_WORKER, schedule() won't call wq_worker_sleeping(), + * so nr_running is never decremented and no replacement + * worker is created. Item 2 stays stuck in pool->worklist. + */ + current->flags &= ~PF_WQ_WORKER; + + pr_info("wq_stall: entering wait_event_idle (PF_WQ_WORKER cleared)\n"); + pr_info("wq_stall: expect 'BUG: workqueue lockup' in ~30-60s\n"); + wait_event_idle(stall_wq_head, atomic_read(&wake_condition) != 0); + + /* Restore so process_one_work() cleanup works correctly */ + current->flags |= PF_WQ_WORKER; + pr_info("wq_stall: woke up, PF_WQ_WORKER restored\n"); +} + +static int __init wq_stall_init(void) +{ + pr_info("wq_stall: loading\n"); + + INIT_WORK(&stall_work1, stall_work1_fn); + INIT_WORK(&stall_work2, stall_work2_fn); + schedule_work(&stall_work1); + + return 0; +} + +static void __exit wq_stall_exit(void) +{ + pr_info("wq_stall: unloading\n"); + atomic_set(&wake_condition, 1); + wake_up(&stall_wq_head); + flush_work(&stall_work1); + flush_work(&stall_work2); + pr_info("wq_stall: all work flushed, module unloaded\n"); +} + +module_init(wq_stall_init); +module_exit(wq_stall_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Reproduce workqueue stall caused by PF_WQ_WORKER misuse"); +MODULE_AUTHOR("Breno Leitao <leitao@debian.org>"); |
