summaryrefslogtreecommitdiff
path: root/samples
diff options
context:
space:
mode:
Diffstat (limited to 'samples')
-rw-r--r--samples/Kconfig9
-rw-r--r--samples/cgroup/memcg_event_listener.c2
-rw-r--r--samples/damon/mtier.c84
-rw-r--r--samples/damon/prcl.c59
-rw-r--r--samples/damon/wsse.c59
-rw-r--r--samples/fanotify/fs-monitor.c7
-rw-r--r--samples/ftrace/ftrace-direct-modify.c2
-rw-r--r--samples/hung_task/hung_task_tests.c81
-rw-r--r--samples/kobject/kobject-example.c4
-rw-r--r--samples/kobject/kset-example.c4
-rw-r--r--samples/landlock/sandboxer.c5
-rw-r--r--samples/mei/mei-amt-version.c2
-rw-r--r--samples/rust/Kconfig33
-rw-r--r--samples/rust/Makefile3
-rw-r--r--samples/rust/rust_configfs.rs6
-rw-r--r--samples/rust/rust_debugfs.rs151
-rw-r--r--samples/rust/rust_debugfs_scoped.rs134
-rw-r--r--samples/rust/rust_dma.rs77
-rw-r--r--samples/rust/rust_driver_auxiliary.rs15
-rw-r--r--samples/rust/rust_driver_pci.rs39
-rw-r--r--samples/rust/rust_driver_platform.rs147
-rw-r--r--samples/rust/rust_driver_usb.rs47
-rw-r--r--samples/rust/rust_misc_device.rs41
-rw-r--r--samples/rust/rust_print_main.rs2
-rw-r--r--samples/v4l/v4l2-pci-skeleton.c10
25 files changed, 895 insertions, 128 deletions
diff --git a/samples/Kconfig b/samples/Kconfig
index ffef99950206..6e072a5f1ed8 100644
--- a/samples/Kconfig
+++ b/samples/Kconfig
@@ -54,7 +54,7 @@ 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 instancess"
+ 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
@@ -316,10 +316,9 @@ config SAMPLE_HUNG_TASK
depends on DETECT_HUNG_TASK && DEBUG_FS
help
Build a module that provides debugfs files (e.g., mutex, semaphore,
- etc.) under <debugfs>/hung_task. If user reads one of these files,
- it will sleep long time (256 seconds) with holding a lock. Thus,
- if 2 or more processes read the same file concurrently, it will
- be detected by the hung_task watchdog.
+ rw_semaphore_read, rw_semaphore_write) under <debugfs>/hung_task.
+ Reading these files with multiple processes triggers hung task
+ detection by holding locks for a long time (256 seconds).
source "samples/rust/Kconfig"
diff --git a/samples/cgroup/memcg_event_listener.c b/samples/cgroup/memcg_event_listener.c
index a1667fe2489a..41425edbd88a 100644
--- a/samples/cgroup/memcg_event_listener.c
+++ b/samples/cgroup/memcg_event_listener.c
@@ -18,8 +18,6 @@
#include <sys/inotify.h>
#include <unistd.h>
-#define MEMCG_EVENTS "memory.events"
-
/* Size of buffer to use when reading inotify events */
#define INOTIFY_BUFFER_SIZE 8192
diff --git a/samples/damon/mtier.c b/samples/damon/mtier.c
index c94254b77fc9..775838a23d93 100644
--- a/samples/damon/mtier.c
+++ b/samples/damon/mtier.c
@@ -12,6 +12,11 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#ifdef MODULE_PARAM_PREFIX
+#undef MODULE_PARAM_PREFIX
+#endif
+#define MODULE_PARAM_PREFIX "damon_sample_mtier."
+
static unsigned long node0_start_addr __read_mostly;
module_param(node0_start_addr, ulong, 0600);
@@ -24,20 +29,47 @@ module_param(node1_start_addr, ulong, 0600);
static unsigned long node1_end_addr __read_mostly;
module_param(node1_end_addr, ulong, 0600);
+static unsigned long node0_mem_used_bp __read_mostly = 9970;
+module_param(node0_mem_used_bp, ulong, 0600);
+
+static unsigned long node0_mem_free_bp __read_mostly = 50;
+module_param(node0_mem_free_bp, ulong, 0600);
+
static int damon_sample_mtier_enable_store(
const char *val, const struct kernel_param *kp);
-static const struct kernel_param_ops enable_param_ops = {
+static const struct kernel_param_ops enabled_param_ops = {
.set = damon_sample_mtier_enable_store,
.get = param_get_bool,
};
-static bool enable __read_mostly;
-module_param_cb(enable, &enable_param_ops, &enable, 0600);
-MODULE_PARM_DESC(enable, "Enable of disable DAMON_SAMPLE_MTIER");
+static bool enabled __read_mostly;
+module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
+MODULE_PARM_DESC(enabled, "Enable or disable DAMON_SAMPLE_MTIER");
+
+static bool detect_node_addresses __read_mostly;
+module_param(detect_node_addresses, bool, 0600);
static struct damon_ctx *ctxs[2];
+struct region_range {
+ phys_addr_t start;
+ phys_addr_t end;
+};
+
+static int nid_to_phys(int target_node, struct region_range *range)
+{
+ if (!node_online(target_node)) {
+ pr_err("NUMA node %d is not online\n", target_node);
+ return -EINVAL;
+ }
+
+ range->start = PFN_PHYS(node_start_pfn(target_node));
+ range->end = PFN_PHYS(node_end_pfn(target_node));
+
+ return 0;
+}
+
static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote)
{
struct damon_ctx *ctx;
@@ -47,6 +79,8 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote)
struct damos *scheme;
struct damos_quota_goal *quota_goal;
struct damos_filter *filter;
+ struct region_range addr;
+ int ret;
ctx = damon_new_ctx();
if (!ctx)
@@ -76,9 +110,17 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote)
if (!target)
goto free_out;
damon_add_target(ctx, target);
- region = damon_new_region(
- promote ? node1_start_addr : node0_start_addr,
- promote ? node1_end_addr : node0_end_addr);
+
+ if (detect_node_addresses) {
+ ret = promote ? nid_to_phys(1, &addr) : nid_to_phys(0, &addr);
+ if (ret)
+ goto free_out;
+ } else {
+ addr.start = promote ? node1_start_addr : node0_start_addr;
+ addr.end = promote ? node1_end_addr : node0_end_addr;
+ }
+
+ region = damon_new_region(addr.start, addr.end);
if (!region)
goto free_out;
damon_add_region(region, target);
@@ -112,7 +154,7 @@ static struct damon_ctx *damon_sample_mtier_build_ctx(bool promote)
quota_goal = damos_new_quota_goal(
promote ? DAMOS_QUOTA_NODE_MEM_USED_BP :
DAMOS_QUOTA_NODE_MEM_FREE_BP,
- promote ? 9970 : 50);
+ promote ? node0_mem_used_bp : node0_mem_free_bp);
if (!quota_goal)
goto free_out;
quota_goal->nid = 0;
@@ -154,20 +196,23 @@ static void damon_sample_mtier_stop(void)
static int damon_sample_mtier_enable_store(
const char *val, const struct kernel_param *kp)
{
- bool enabled = enable;
+ bool is_enabled = enabled;
int err;
- err = kstrtobool(val, &enable);
+ err = kstrtobool(val, &enabled);
if (err)
return err;
- if (enable == enabled)
+ if (enabled == is_enabled)
return 0;
- if (enable) {
+ if (!damon_initialized())
+ return 0;
+
+ if (enabled) {
err = damon_sample_mtier_start();
if (err)
- enable = false;
+ enabled = false;
return err;
}
damon_sample_mtier_stop();
@@ -176,6 +221,19 @@ static int damon_sample_mtier_enable_store(
static int __init damon_sample_mtier_init(void)
{
+ int err = 0;
+
+ if (!damon_initialized()) {
+ if (enabled)
+ enabled = false;
+ return -ENOMEM;
+ }
+
+ if (enabled) {
+ err = damon_sample_mtier_start();
+ if (err)
+ enabled = false;
+ }
return 0;
}
diff --git a/samples/damon/prcl.c b/samples/damon/prcl.c
index 5597e6a08ab2..b7c50f2656ce 100644
--- a/samples/damon/prcl.c
+++ b/samples/damon/prcl.c
@@ -11,26 +11,32 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#ifdef MODULE_PARAM_PREFIX
+#undef MODULE_PARAM_PREFIX
+#endif
+#define MODULE_PARAM_PREFIX "damon_sample_prcl."
+
static int target_pid __read_mostly;
module_param(target_pid, int, 0600);
static int damon_sample_prcl_enable_store(
const char *val, const struct kernel_param *kp);
-static const struct kernel_param_ops enable_param_ops = {
+static const struct kernel_param_ops enabled_param_ops = {
.set = damon_sample_prcl_enable_store,
.get = param_get_bool,
};
-static bool enable __read_mostly;
-module_param_cb(enable, &enable_param_ops, &enable, 0600);
-MODULE_PARM_DESC(enable, "Enable of disable DAMON_SAMPLE_WSSE");
+static bool enabled __read_mostly;
+module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
+MODULE_PARM_DESC(enabled, "Enable or disable DAMON_SAMPLE_PRCL");
static struct damon_ctx *ctx;
static struct pid *target_pidp;
-static int damon_sample_prcl_after_aggregate(struct damon_ctx *c)
+static int damon_sample_prcl_repeat_call_fn(void *data)
{
+ struct damon_ctx *c = data;
struct damon_target *t;
damon_for_each_target(t, c) {
@@ -46,10 +52,16 @@ static int damon_sample_prcl_after_aggregate(struct damon_ctx *c)
return 0;
}
+static struct damon_call_control repeat_call_control = {
+ .fn = damon_sample_prcl_repeat_call_fn,
+ .repeat = true,
+};
+
static int damon_sample_prcl_start(void)
{
struct damon_target *target;
struct damos *scheme;
+ int err;
pr_info("start\n");
@@ -74,8 +86,6 @@ static int damon_sample_prcl_start(void)
}
target->pid = target_pidp;
- ctx->callback.after_aggregation = damon_sample_prcl_after_aggregate;
-
scheme = damon_new_scheme(
&(struct damos_access_pattern) {
.min_sz_region = PAGE_SIZE,
@@ -95,7 +105,12 @@ static int damon_sample_prcl_start(void)
}
damon_set_schemes(ctx, &scheme, 1);
- return damon_start(&ctx, 1, true);
+ err = damon_start(&ctx, 1, true);
+ if (err)
+ return err;
+
+ repeat_call_control.data = ctx;
+ return damon_call(ctx, &repeat_call_control);
}
static void damon_sample_prcl_stop(void)
@@ -105,27 +120,28 @@ static void damon_sample_prcl_stop(void)
damon_stop(&ctx, 1);
damon_destroy_ctx(ctx);
}
- if (target_pidp)
- put_pid(target_pidp);
}
static int damon_sample_prcl_enable_store(
const char *val, const struct kernel_param *kp)
{
- bool enabled = enable;
+ bool is_enabled = enabled;
int err;
- err = kstrtobool(val, &enable);
+ err = kstrtobool(val, &enabled);
if (err)
return err;
- if (enable == enabled)
+ if (enabled == is_enabled)
return 0;
- if (enable) {
+ if (!damon_initialized())
+ return 0;
+
+ if (enabled) {
err = damon_sample_prcl_start();
if (err)
- enable = false;
+ enabled = false;
return err;
}
damon_sample_prcl_stop();
@@ -134,6 +150,19 @@ static int damon_sample_prcl_enable_store(
static int __init damon_sample_prcl_init(void)
{
+ int err = 0;
+
+ if (!damon_initialized()) {
+ if (enabled)
+ enabled = false;
+ return -ENOMEM;
+ }
+
+ if (enabled) {
+ err = damon_sample_prcl_start();
+ if (err)
+ enabled = false;
+ }
return 0;
}
diff --git a/samples/damon/wsse.c b/samples/damon/wsse.c
index e20238a249e7..799ad4443943 100644
--- a/samples/damon/wsse.c
+++ b/samples/damon/wsse.c
@@ -12,26 +12,32 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#ifdef MODULE_PARAM_PREFIX
+#undef MODULE_PARAM_PREFIX
+#endif
+#define MODULE_PARAM_PREFIX "damon_sample_wsse."
+
static int target_pid __read_mostly;
module_param(target_pid, int, 0600);
static int damon_sample_wsse_enable_store(
const char *val, const struct kernel_param *kp);
-static const struct kernel_param_ops enable_param_ops = {
+static const struct kernel_param_ops enabled_param_ops = {
.set = damon_sample_wsse_enable_store,
.get = param_get_bool,
};
-static bool enable __read_mostly;
-module_param_cb(enable, &enable_param_ops, &enable, 0600);
-MODULE_PARM_DESC(enable, "Enable or disable DAMON_SAMPLE_WSSE");
+static bool enabled __read_mostly;
+module_param_cb(enabled, &enabled_param_ops, &enabled, 0600);
+MODULE_PARM_DESC(enabled, "Enable or disable DAMON_SAMPLE_WSSE");
static struct damon_ctx *ctx;
static struct pid *target_pidp;
-static int damon_sample_wsse_after_aggregate(struct damon_ctx *c)
+static int damon_sample_wsse_repeat_call_fn(void *data)
{
+ struct damon_ctx *c = data;
struct damon_target *t;
damon_for_each_target(t, c) {
@@ -47,9 +53,15 @@ static int damon_sample_wsse_after_aggregate(struct damon_ctx *c)
return 0;
}
+static struct damon_call_control repeat_call_control = {
+ .fn = damon_sample_wsse_repeat_call_fn,
+ .repeat = true,
+};
+
static int damon_sample_wsse_start(void)
{
struct damon_target *target;
+ int err;
pr_info("start\n");
@@ -74,8 +86,11 @@ static int damon_sample_wsse_start(void)
}
target->pid = target_pidp;
- ctx->callback.after_aggregation = damon_sample_wsse_after_aggregate;
- return damon_start(&ctx, 1, true);
+ err = damon_start(&ctx, 1, true);
+ if (err)
+ return err;
+ repeat_call_control.data = ctx;
+ return damon_call(ctx, &repeat_call_control);
}
static void damon_sample_wsse_stop(void)
@@ -85,27 +100,28 @@ static void damon_sample_wsse_stop(void)
damon_stop(&ctx, 1);
damon_destroy_ctx(ctx);
}
- if (target_pidp)
- put_pid(target_pidp);
}
static int damon_sample_wsse_enable_store(
const char *val, const struct kernel_param *kp)
{
- bool enabled = enable;
+ bool is_enabled = enabled;
int err;
- err = kstrtobool(val, &enable);
+ err = kstrtobool(val, &enabled);
if (err)
return err;
- if (enable == enabled)
+ if (enabled == is_enabled)
+ return 0;
+
+ if (!damon_initialized())
return 0;
- if (enable) {
+ if (enabled) {
err = damon_sample_wsse_start();
if (err)
- enable = false;
+ enabled = false;
return err;
}
damon_sample_wsse_stop();
@@ -114,7 +130,20 @@ static int damon_sample_wsse_enable_store(
static int __init damon_sample_wsse_init(void)
{
- return 0;
+ int err = 0;
+
+ if (!damon_initialized()) {
+ err = -ENOMEM;
+ if (enabled)
+ enabled = false;
+ }
+
+ if (enabled) {
+ err = damon_sample_wsse_start();
+ if (err)
+ enabled = false;
+ }
+ return err;
}
module_init(damon_sample_wsse_init);
diff --git a/samples/fanotify/fs-monitor.c b/samples/fanotify/fs-monitor.c
index 608db24c471e..28c0a652ffeb 100644
--- a/samples/fanotify/fs-monitor.c
+++ b/samples/fanotify/fs-monitor.c
@@ -12,6 +12,9 @@
#include <sys/fanotify.h>
#include <sys/types.h>
#include <unistd.h>
+#ifndef __GLIBC__
+#include <asm-generic/int-ll64.h>
+#endif
#ifndef FAN_FS_ERROR
#define FAN_FS_ERROR 0x00008000
@@ -95,7 +98,11 @@ static void handle_notifications(char *buffer, int len)
fid = (struct fanotify_event_info_fid *) info;
printf("\tfsid: %x%x\n",
+#if defined(__GLIBC__)
fid->fsid.val[0], fid->fsid.val[1]);
+#else
+ fid->fsid.__val[0], fid->fsid.__val[1]);
+#endif
print_fh((struct file_handle *) &fid->handle);
break;
diff --git a/samples/ftrace/ftrace-direct-modify.c b/samples/ftrace/ftrace-direct-modify.c
index cfea7a38befb..da3a9f2091f5 100644
--- a/samples/ftrace/ftrace-direct-modify.c
+++ b/samples/ftrace/ftrace-direct-modify.c
@@ -75,8 +75,8 @@ asm (
CALL_DEPTH_ACCOUNT
" call my_direct_func1\n"
" leave\n"
-" .size my_tramp1, .-my_tramp1\n"
ASM_RET
+" .size my_tramp1, .-my_tramp1\n"
" .type my_tramp2, @function\n"
" .globl my_tramp2\n"
diff --git a/samples/hung_task/hung_task_tests.c b/samples/hung_task/hung_task_tests.c
index a5c09bd3a47d..0360ec916890 100644
--- a/samples/hung_task/hung_task_tests.c
+++ b/samples/hung_task/hung_task_tests.c
@@ -4,11 +4,12 @@
* semaphore, etc.
*
* Usage: Load this module and read `<debugfs>/hung_task/mutex`,
- * `<debugfs>/hung_task/semaphore`, etc., with 2 or more processes.
+ * `<debugfs>/hung_task/semaphore`, `<debugfs>/hung_task/rw_semaphore_read`,
+ * `<debugfs>/hung_task/rw_semaphore_write`, etc., with 2 or more processes.
*
* This is for testing kernel hung_task error messages with various locking
- * mechanisms (e.g., mutex, semaphore, etc.). Note that this may freeze
- * your system or cause a panic. Use only for testing purposes.
+ * mechanisms (e.g., mutex, semaphore, rw_semaphore_read, rw_semaphore_write, etc.).
+ * Note that this may freeze your system or cause a panic. Use only for testing purposes.
*/
#include <linux/debugfs.h>
@@ -17,21 +18,29 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/semaphore.h>
+#include <linux/rwsem.h>
-#define HUNG_TASK_DIR "hung_task"
-#define HUNG_TASK_MUTEX_FILE "mutex"
-#define HUNG_TASK_SEM_FILE "semaphore"
-#define SLEEP_SECOND 256
+#define HUNG_TASK_DIR "hung_task"
+#define HUNG_TASK_MUTEX_FILE "mutex"
+#define HUNG_TASK_SEM_FILE "semaphore"
+#define HUNG_TASK_RWSEM_READ_FILE "rw_semaphore_read"
+#define HUNG_TASK_RWSEM_WRITE_FILE "rw_semaphore_write"
+#define SLEEP_SECOND 256
static const char dummy_string[] = "This is a dummy string.";
static DEFINE_MUTEX(dummy_mutex);
static DEFINE_SEMAPHORE(dummy_sem, 1);
+static DECLARE_RWSEM(dummy_rwsem);
static struct dentry *hung_task_dir;
/* Mutex-based read function */
static ssize_t read_dummy_mutex(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
+ /* Check if data is already read */
+ if (*ppos >= sizeof(dummy_string))
+ return 0;
+
/* Second task waits on mutex, entering uninterruptible sleep */
guard(mutex)(&dummy_mutex);
@@ -46,6 +55,10 @@ static ssize_t read_dummy_mutex(struct file *file, char __user *user_buf,
static ssize_t read_dummy_semaphore(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
+ /* Check if data is already read */
+ if (*ppos >= sizeof(dummy_string))
+ return 0;
+
/* Second task waits on semaphore, entering uninterruptible sleep */
down(&dummy_sem);
@@ -58,6 +71,46 @@ static ssize_t read_dummy_semaphore(struct file *file, char __user *user_buf,
sizeof(dummy_string));
}
+/* Read-write semaphore read function */
+static ssize_t read_dummy_rwsem_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ /* Check if data is already read */
+ if (*ppos >= sizeof(dummy_string))
+ return 0;
+
+ /* Acquires read lock, allowing concurrent readers but blocks if write lock is held */
+ down_read(&dummy_rwsem);
+
+ /* Sleeps here, potentially triggering hung task detection if lock is held too long */
+ msleep_interruptible(SLEEP_SECOND * 1000);
+
+ up_read(&dummy_rwsem);
+
+ return simple_read_from_buffer(user_buf, count, ppos, dummy_string,
+ sizeof(dummy_string));
+}
+
+/* Read-write semaphore write function */
+static ssize_t read_dummy_rwsem_write(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ /* Check if data is already read */
+ if (*ppos >= sizeof(dummy_string))
+ return 0;
+
+ /* Acquires exclusive write lock, blocking all other readers and writers */
+ down_write(&dummy_rwsem);
+
+ /* Sleeps here, potentially triggering hung task detection if lock is held too long */
+ msleep_interruptible(SLEEP_SECOND * 1000);
+
+ up_write(&dummy_rwsem);
+
+ return simple_read_from_buffer(user_buf, count, ppos, dummy_string,
+ sizeof(dummy_string));
+}
+
/* File operations for mutex */
static const struct file_operations hung_task_mutex_fops = {
.read = read_dummy_mutex,
@@ -68,6 +121,16 @@ static const struct file_operations hung_task_sem_fops = {
.read = read_dummy_semaphore,
};
+/* File operations for rw_semaphore read */
+static const struct file_operations hung_task_rwsem_read_fops = {
+ .read = read_dummy_rwsem_read,
+};
+
+/* File operations for rw_semaphore write */
+static const struct file_operations hung_task_rwsem_write_fops = {
+ .read = read_dummy_rwsem_write,
+};
+
static int __init hung_task_tests_init(void)
{
hung_task_dir = debugfs_create_dir(HUNG_TASK_DIR, NULL);
@@ -79,6 +142,10 @@ static int __init hung_task_tests_init(void)
&hung_task_mutex_fops);
debugfs_create_file(HUNG_TASK_SEM_FILE, 0400, hung_task_dir, NULL,
&hung_task_sem_fops);
+ debugfs_create_file(HUNG_TASK_RWSEM_READ_FILE, 0400, hung_task_dir, NULL,
+ &hung_task_rwsem_read_fops);
+ debugfs_create_file(HUNG_TASK_RWSEM_WRITE_FILE, 0400, hung_task_dir, NULL,
+ &hung_task_rwsem_write_fops);
return 0;
}
diff --git a/samples/kobject/kobject-example.c b/samples/kobject/kobject-example.c
index c9c3db19799a..36d87ca0bee2 100644
--- a/samples/kobject/kobject-example.c
+++ b/samples/kobject/kobject-example.c
@@ -13,7 +13,7 @@
/*
* This module shows how to create a simple subdirectory in sysfs called
- * /sys/kernel/kobject-example In that directory, 3 files are created:
+ * /sys/kernel/kobject_example In that directory, 3 files are created:
* "foo", "baz", and "bar". If an integer is written to these files, it can be
* later read out of it.
*/
@@ -102,7 +102,7 @@ static struct attribute *attrs[] = {
* created for the attributes with the directory being the name of the
* attribute group.
*/
-static struct attribute_group attr_group = {
+static const struct attribute_group attr_group = {
.attrs = attrs,
};
diff --git a/samples/kobject/kset-example.c b/samples/kobject/kset-example.c
index 552d7e363539..579ce150217c 100644
--- a/samples/kobject/kset-example.c
+++ b/samples/kobject/kset-example.c
@@ -14,8 +14,8 @@
/*
* This module shows how to create a kset in sysfs called
- * /sys/kernel/kset-example
- * Then tree kobjects are created and assigned to this kset, "foo", "baz",
+ * /sys/kernel/kset_example
+ * Then three kobjects are created and assigned to this kset, "foo", "baz",
* and "bar". In those kobjects, attributes of the same name are also
* created and if an integer is written to these files, it can be later
* read out of it.
diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
index 4e2854c6f9a3..e7af02f98208 100644
--- a/samples/landlock/sandboxer.c
+++ b/samples/landlock/sandboxer.c
@@ -13,7 +13,6 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/landlock.h>
-#include <linux/prctl.h>
#include <linux/socket.h>
#include <stddef.h>
#include <stdio.h>
@@ -25,6 +24,10 @@
#include <unistd.h>
#include <stdbool.h>
+#if defined(__GLIBC__)
+#include <linux/prctl.h>
+#endif
+
#ifndef landlock_create_ruleset
static inline int
landlock_create_ruleset(const struct landlock_ruleset_attr *const attr,
diff --git a/samples/mei/mei-amt-version.c b/samples/mei/mei-amt-version.c
index 867debd3b912..1d7254bcb44c 100644
--- a/samples/mei/mei-amt-version.c
+++ b/samples/mei/mei-amt-version.c
@@ -69,11 +69,11 @@
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
+#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <stdbool.h>
-#include <bits/wordsize.h>
#include <linux/mei.h>
/*****************************************************************************
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 7f7371a004ee..c376eb899b7a 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -62,6 +62,28 @@ config SAMPLE_RUST_DMA
If unsure, say N.
+config SAMPLE_RUST_DEBUGFS
+ tristate "DebugFS Test Module"
+ depends on DEBUG_FS
+ help
+ This option builds the Rust DebugFS Test module sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_debugfs.
+
+ If unsure, say N.
+
+config SAMPLE_RUST_DEBUGFS_SCOPED
+ tristate "Scoped DebugFS Test Module"
+ depends on DEBUG_FS
+ help
+ This option builds the Rust Scoped DebugFS Test module sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_debugfs_scoped.
+
+ If unsure, say N.
+
config SAMPLE_RUST_DRIVER_PCI
tristate "PCI Driver"
depends on PCI
@@ -83,6 +105,17 @@ config SAMPLE_RUST_DRIVER_PLATFORM
If unsure, say N.
+config SAMPLE_RUST_DRIVER_USB
+ tristate "USB Driver"
+ depends on USB = y && BROKEN
+ help
+ This option builds the Rust USB driver sample.
+
+ To compile this as a module, choose M here:
+ the module will be called rust_driver_usb.
+
+ If unsure, say N.
+
config SAMPLE_RUST_DRIVER_FAUX
tristate "Faux Driver"
help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index bd2faad63b4f..cf8422f8f219 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -4,9 +4,12 @@ ccflags-y += -I$(src) # needed for trace events
obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o
obj-$(CONFIG_SAMPLE_RUST_MISC_DEVICE) += rust_misc_device.o
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_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
diff --git a/samples/rust/rust_configfs.rs b/samples/rust/rust_configfs.rs
index 60ddbe62cda3..0ccc7553ef39 100644
--- a/samples/rust/rust_configfs.rs
+++ b/samples/rust/rust_configfs.rs
@@ -5,7 +5,7 @@
use kernel::alloc::flags;
use kernel::c_str;
use kernel::configfs;
-use kernel::configfs_attrs;
+use kernel::configfs::configfs_attrs;
use kernel::new_mutex;
use kernel::page::PAGE_SIZE;
use kernel::prelude::*;
@@ -14,7 +14,7 @@ use kernel::sync::Mutex;
module! {
type: RustConfigfs,
name: "rust_configfs",
- author: "Rust for Linux Contributors",
+ authors: ["Rust for Linux Contributors"],
description: "Rust configfs sample",
license: "GPL",
}
@@ -94,7 +94,7 @@ impl configfs::AttributeOperations<0> for Configuration {
fn show(container: &Configuration, page: &mut [u8; PAGE_SIZE]) -> Result<usize> {
pr_info!("Show message\n");
- let data = container.message;
+ let data = container.message.to_bytes();
page[0..data.len()].copy_from_slice(data);
Ok(data.len())
}
diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs
new file mode 100644
index 000000000000..82b61a15a34b
--- /dev/null
+++ b/samples/rust/rust_debugfs.rs
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+//! Sample DebugFS exporting platform driver
+//!
+//! To successfully probe this driver with ACPI, use an ssdt that looks like
+//!
+//! ```dsl
+//! 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 (_DSD, Package () { // Sample attribute
+//! ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+//! Package() {
+//! Package(2) {"compatible", "sample-debugfs"}
+//! }
+//! })
+//! Name (_CRS, ResourceTemplate ()
+//! {
+//! Memory32Fixed (ReadWrite, 0xFED00000, 0x1000)
+//! })
+//! }
+//! }
+//! }
+//! ```
+
+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};
+
+kernel::module_platform_driver! {
+ type: RustDebugFs,
+ name: "rust_debugfs",
+ authors: ["Matthew Maurer"],
+ description: "Rust DebugFS usage sample",
+ license: "GPL",
+}
+
+#[pin_data]
+struct RustDebugFs {
+ pdev: ARef<platform::Device>,
+ // As we only hold these for drop effect (to remove the directory/files) we have a leading
+ // underscore to indicate to the compiler that we don't expect to use this field directly.
+ _debugfs: Dir,
+ #[pin]
+ _compatible: File<CString>,
+ #[pin]
+ counter: File<AtomicUsize>,
+ #[pin]
+ inner: File<Mutex<Inner>>,
+}
+
+#[derive(Debug)]
+struct Inner {
+ x: u32,
+ y: u32,
+}
+
+impl FromStr for Inner {
+ type Err = Error;
+ fn from_str(s: &str) -> Result<Self> {
+ let mut parts = s.split_whitespace();
+ let x = parts
+ .next()
+ .ok_or(EINVAL)?
+ .parse::<u32>()
+ .map_err(|_| EINVAL)?;
+ let y = parts
+ .next()
+ .ok_or(EINVAL)?
+ .parse::<u32>()
+ .map_err(|_| EINVAL)?;
+ if parts.next().is_some() {
+ return Err(EINVAL);
+ }
+ Ok(Inner { x, y })
+ }
+}
+
+kernel::acpi_device_table!(
+ ACPI_TABLE,
+ MODULE_ACPI_TABLE,
+ <RustDebugFs as platform::Driver>::IdInfo,
+ [(acpi::DeviceId::new(c_str!("LNUXBEEF")), ())]
+);
+
+impl platform::Driver for RustDebugFs {
+ type IdInfo = ();
+ const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
+ const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
+
+ 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 RustDebugFs {
+ fn build_counter(dir: &Dir) -> impl PinInit<File<AtomicUsize>> + '_ {
+ dir.read_write_file(c_str!("counter"), AtomicUsize::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 }))
+ }
+
+ fn new(pdev: &platform::Device<Core>) -> impl PinInit<Self, Error> + '_ {
+ let debugfs = Dir::new(c_str!("sample_debugfs"));
+ let dev = pdev.as_ref();
+
+ try_pin_init! {
+ Self {
+ _compatible <- debugfs.read_only_file(
+ c_str!("compatible"),
+ dev.fwnode()
+ .ok_or(ENOENT)?
+ .property_read::<CString>(c_str!("compatible"))
+ .required_by(dev)?,
+ ),
+ counter <- Self::build_counter(&debugfs),
+ inner <- Self::build_inner(&debugfs),
+ _debugfs: debugfs,
+ pdev: pdev.into(),
+ }
+ }
+ }
+}
diff --git a/samples/rust/rust_debugfs_scoped.rs b/samples/rust/rust_debugfs_scoped.rs
new file mode 100644
index 000000000000..b0c4e76b123e
--- /dev/null
+++ b/samples/rust/rust_debugfs_scoped.rs
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Google LLC.
+
+//! Sample DebugFS exporting platform driver that demonstrates the use of
+//! `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};
+
+module! {
+ type: RustScopedDebugFs,
+ name: "rust_debugfs_scoped",
+ authors: ["Matthew Maurer"],
+ description: "Rust Scoped DebugFS usage sample",
+ license: "GPL",
+}
+
+fn remove_file_write(
+ mod_data: &ModuleData,
+ reader: &mut kernel::uaccess::UserSliceReader,
+) -> Result {
+ let mut buf = [0u8; 128];
+ if reader.len() >= buf.len() {
+ return Err(EINVAL);
+ }
+ let n = reader.len();
+ reader.read_slice(&mut buf[..n])?;
+
+ let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();
+ let nul_idx = s.len();
+ buf[nul_idx] = 0;
+ let to_remove = CStr::from_bytes_with_nul(&buf[..nul_idx + 1]).map_err(|_| EINVAL)?;
+ mod_data
+ .devices
+ .lock()
+ .retain(|device| device.name.as_bytes() != to_remove.as_bytes());
+ Ok(())
+}
+
+fn create_file_write(
+ mod_data: &ModuleData,
+ reader: &mut kernel::uaccess::UserSliceReader,
+) -> Result {
+ let mut buf = [0u8; 128];
+ if reader.len() > buf.len() {
+ return Err(EINVAL);
+ }
+ let n = reader.len();
+ reader.read_slice(&mut buf[..n])?;
+
+ let mut nums = KVec::new();
+
+ let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?.trim();
+ let mut items = s.split_whitespace();
+ let name_str = items.next().ok_or(EINVAL)?;
+ let name = CString::try_from_fmt(fmt!("{name_str}"))?;
+ let file_name = CString::try_from_fmt(fmt!("{name_str}"))?;
+ for sub in items {
+ nums.push(
+ AtomicUsize::new(sub.parse().map_err(|_| EINVAL)?),
+ GFP_KERNEL,
+ )?;
+ }
+
+ let scope = KBox::pin_init(
+ mod_data
+ .device_dir
+ .scope(DeviceData { name, nums }, &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);
+ }
+ }),
+ GFP_KERNEL,
+ )?;
+ (*mod_data.devices.lock()).push(scope, GFP_KERNEL)?;
+
+ Ok(())
+}
+
+struct RustScopedDebugFs {
+ _data: Pin<KBox<Scope<ModuleData>>>,
+}
+
+#[pin_data]
+struct ModuleData {
+ device_dir: Dir,
+ #[pin]
+ devices: Mutex<KVec<Pin<KBox<Scope<DeviceData>>>>>,
+}
+
+impl ModuleData {
+ fn init(device_dir: Dir) -> impl PinInit<Self> {
+ pin_init! {
+ Self {
+ device_dir: device_dir,
+ devices <- new_mutex!(KVec::new())
+ }
+ }
+ }
+}
+
+struct DeviceData {
+ name: CString,
+ nums: KVec<AtomicUsize>,
+}
+
+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);
+ },
+ )
+}
+
+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"));
+ 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 874c2c964afa..4d324f06cc2a 100644
--- a/samples/rust/rust_dma.rs
+++ b/samples/rust/rust_dma.rs
@@ -4,11 +4,21 @@
//!
//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
-use kernel::{bindings, device::Core, dma::CoherentAllocation, pci, prelude::*, types::ARef};
-
+use kernel::{
+ device::Core,
+ dma::{CoherentAllocation, DataDirection, Device, DmaMask},
+ page, pci,
+ prelude::*,
+ scatterlist::{Owned, SGTable},
+ sync::aref::ARef,
+};
+
+#[pin_data(PinnedDrop)]
struct DmaSampleDriver {
pdev: ARef<pci::Device>,
ca: CoherentAllocation<MyStruct>,
+ #[pin]
+ sgt: SGTable<Owned<VVec<u8>>>,
}
const TEST_VALUES: [(u32, u32); 5] = [
@@ -38,10 +48,7 @@ kernel::pci_device_table!(
PCI_TABLE,
MODULE_PCI_TABLE,
<DmaSampleDriver as pci::Driver>::IdInfo,
- [(
- pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
- ()
- )]
+ [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())]
);
impl pci::Driver for DmaSampleDriver {
@@ -51,40 +58,60 @@ impl pci::Driver for DmaSampleDriver {
fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> Result<Pin<KBox<Self>>> {
dev_info!(pdev.as_ref(), "Probe DMA test driver.\n");
+ 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)? };
+
let ca: CoherentAllocation<MyStruct> =
CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?;
- || -> Result {
- 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)?;
- Ok(())
- }()?;
+ let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL);
- let drvdata = KBox::new(
- Self {
+ let drvdata = KBox::pin_init(
+ try_pin_init!(Self {
pdev: pdev.into(),
ca,
- },
+ sgt <- sgt,
+ }),
GFP_KERNEL,
)?;
- Ok(drvdata.into())
+ Ok(drvdata)
}
}
-impl Drop for DmaSampleDriver {
- fn drop(&mut self) {
- dev_info!(self.pdev.as_ref(), "Unload DMA test driver.\n");
+#[pinned_drop]
+impl PinnedDrop for DmaSampleDriver {
+ fn drop(self: Pin<&mut Self>) {
+ let dev = self.pdev.as_ref();
- let _ = || -> Result {
- for (i, value) in TEST_VALUES.into_iter().enumerate() {
- assert_eq!(kernel::dma_read!(self.ca[i].h), value.0);
- assert_eq!(kernel::dma_read!(self.ca[i].b), value.1);
+ 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());
+
+ if let Ok(val0) = val0 {
+ assert_eq!(val0, value.0);
}
- Ok(())
- }();
+ if let Ok(val1) = val1 {
+ assert_eq!(val1, value.1);
+ }
+ }
+
+ for (i, entry) in self.sgt.iter().enumerate() {
+ dev_info!(dev, "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 3e15e6d002bb..55ece336ee45 100644
--- a/samples/rust/rust_driver_auxiliary.rs
+++ b/samples/rust/rust_driver_auxiliary.rs
@@ -5,8 +5,7 @@
//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
use kernel::{
- auxiliary, bindings, c_str, device::Core, driver, error::Error, pci, prelude::*, str::CStr,
- InPlaceModule,
+ auxiliary, c_str, device::Core, driver, error::Error, pci, prelude::*, InPlaceModule,
};
use pin_init::PinInit;
@@ -51,10 +50,7 @@ kernel::pci_device_table!(
PCI_TABLE,
MODULE_PCI_TABLE,
<ParentDriver as pci::Driver>::IdInfo,
- [(
- pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
- ()
- )]
+ [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())]
);
impl pci::Driver for ParentDriver {
@@ -82,11 +78,12 @@ impl ParentDriver {
let parent = adev.parent().ok_or(EINVAL)?;
let pdev: &pci::Device = parent.try_into()?;
+ let vendor = pdev.vendor_id();
dev_info!(
adev.as_ref(),
- "Connect auxiliary {} with parent: VendorID={:#x}, DeviceID={:#x}\n",
+ "Connect auxiliary {} with parent: VendorID={}, DeviceID={:#x}\n",
adev.id(),
- pdev.vendor_id(),
+ vendor,
pdev.device_id()
);
@@ -114,7 +111,7 @@ impl InPlaceModule for SampleModule {
module! {
type: SampleModule,
name: "rust_driver_auxiliary",
- author: "Danilo Krummrich",
+ authors: ["Danilo Krummrich"],
description: "Rust auxiliary driver",
license: "GPL v2",
}
diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs
index 15147e4401b2..55a683c39ed9 100644
--- a/samples/rust/rust_driver_pci.rs
+++ b/samples/rust/rust_driver_pci.rs
@@ -4,7 +4,7 @@
//!
//! To make this driver probe, QEMU must be run with `-device pci-testdev`.
-use kernel::{bindings, c_str, device::Core, devres::Devres, pci, prelude::*, types::ARef};
+use kernel::{c_str, device::Core, devres::Devres, pci, prelude::*, sync::aref::ARef};
struct Regs;
@@ -18,16 +18,19 @@ impl Regs {
type Bar0 = pci::Bar<{ Regs::END }>;
-#[derive(Debug)]
+#[derive(Copy, Clone, Debug)]
struct TestIndex(u8);
impl TestIndex {
const NO_EVENTFD: Self = Self(0);
}
+#[pin_data(PinnedDrop)]
struct SampleDriver {
pdev: ARef<pci::Device>,
+ #[pin]
bar: Devres<Bar0>,
+ index: TestIndex,
}
kernel::pci_device_table!(
@@ -35,7 +38,7 @@ kernel::pci_device_table!(
MODULE_PCI_TABLE,
<SampleDriver as pci::Driver>::IdInfo,
[(
- pci::DeviceId::from_id(bindings::PCI_VENDOR_ID_REDHAT, 0x5),
+ pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5),
TestIndex::NO_EVENTFD
)]
);
@@ -63,23 +66,23 @@ impl pci::Driver for SampleDriver {
const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
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}, 0x{:x}).\n",
- pdev.vendor_id(),
+ "Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n",
+ vendor,
pdev.device_id()
);
pdev.enable_device_mem()?;
pdev.set_master();
- let bar = pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci"))?;
-
- let drvdata = KBox::new(
- Self {
+ 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(),
- bar,
- },
+ index: *info,
+ }),
GFP_KERNEL,
)?;
@@ -90,12 +93,20 @@ impl pci::Driver for SampleDriver {
Self::testdev(info, bar)?
);
- Ok(drvdata.into())
+ Ok(drvdata)
+ }
+
+ fn unbind(pdev: &pci::Device<Core>, this: Pin<&Self>) {
+ if let Ok(bar) = this.bar.access(pdev.as_ref()) {
+ // Reset pci-testdev by writing a new test index.
+ bar.write8(this.index.0, Regs::TEST);
+ }
}
}
-impl Drop for SampleDriver {
- fn drop(&mut self) {
+#[pinned_drop]
+impl PinnedDrop for SampleDriver {
+ fn drop(self: Pin<&mut Self>) {
dev_dbg!(self.pdev.as_ref(), "Remove Rust PCI driver sample.\n");
}
}
diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs
index 8b42b3cfb363..6473baf4f120 100644
--- a/samples/rust/rust_driver_platform.rs
+++ b/samples/rust/rust_driver_platform.rs
@@ -2,7 +2,78 @@
//! Rust Platform driver sample.
-use kernel::{c_str, device::Core, of, platform, prelude::*, types::ARef};
+//! ACPI match table test
+//!
+//! This demonstrates how to test an ACPI-based Rust platform 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, c_str,
+ device::{
+ self,
+ property::{FwNodeReferenceArgs, NArgs},
+ Core,
+ },
+ of, platform,
+ prelude::*,
+ str::CString,
+ sync::aref::ARef,
+};
struct SampleDriver {
pdev: ARef<platform::Device>,
@@ -17,18 +88,32 @@ kernel::of_device_table!(
[(of::DeviceId::new(c_str!("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))]
+);
+
impl platform::Driver for SampleDriver {
type IdInfo = Info;
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>,
) -> Result<Pin<KBox<Self>>> {
- dev_dbg!(pdev.as_ref(), "Probe Rust Platform driver sample.\n");
+ let dev = pdev.as_ref();
+
+ dev_dbg!(dev, "Probe Rust Platform driver sample.\n");
if let Some(info) = info {
- dev_info!(pdev.as_ref(), "Probed with info: '{}'.\n", info.0);
+ dev_info!(dev, "Probed with info: '{}'.\n", info.0);
+ }
+
+ if dev.fwnode().is_some_and(|node| node.is_of_node()) {
+ Self::properties_parse(dev)?;
}
let drvdata = KBox::new(Self { pdev: pdev.into() }, GFP_KERNEL)?;
@@ -37,6 +122,62 @@ impl platform::Driver for SampleDriver {
}
}
+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"))
+ {
+ dev_info!(dev, "matched compatible string idx = {}\n", idx);
+ }
+
+ let name = c_str!("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"));
+ dev_info!(dev, "'{name}'='{prop}'\n");
+
+ if fwnode.property_present(c_str!("test,u32-prop")) {
+ dev_info!(dev, "'test,u32-prop' is present\n");
+ }
+
+ let name = c_str!("test,u32-optional-prop");
+ let prop = fwnode.property_read::<u32>(name).or(0x12);
+ 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 _ = fwnode.property_read::<u32>(name).required_by(dev);
+
+ let name = c_str!("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 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",);
+
+ let name = c_str!("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 nargs = NArgs::N(2);
+ let prop: FwNodeReferenceArgs = child.property_get_reference_args(name, nargs, 0)?;
+ dev_info!(dev, "'{name}'='{prop:?}'\n");
+ }
+
+ Ok(())
+ }
+}
+
impl Drop for SampleDriver {
fn drop(&mut self) {
dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n");
diff --git a/samples/rust/rust_driver_usb.rs b/samples/rust/rust_driver_usb.rs
new file mode 100644
index 000000000000..5c396f421de7
--- /dev/null
+++ b/samples/rust/rust_driver_usb.rs
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (C) 2025 Collabora Ltd.
+
+//! Rust USB driver sample.
+
+use kernel::{device, device::Core, prelude::*, sync::aref::ARef, usb};
+
+struct SampleDriver {
+ _intf: ARef<usb::Interface>,
+}
+
+kernel::usb_device_table!(
+ USB_TABLE,
+ MODULE_USB_TABLE,
+ <SampleDriver as usb::Driver>::IdInfo,
+ [(usb::DeviceId::from_id(0x1234, 0x5678), ()),]
+);
+
+impl usb::Driver for SampleDriver {
+ type IdInfo = ();
+ const ID_TABLE: usb::IdTable<Self::IdInfo> = &USB_TABLE;
+
+ fn probe(
+ intf: &usb::Interface<Core>,
+ _id: &usb::DeviceId,
+ _info: &Self::IdInfo,
+ ) -> Result<Pin<KBox<Self>>> {
+ 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())
+ }
+
+ fn disconnect(intf: &usb::Interface<Core>, _data: Pin<&Self>) {
+ let dev: &device::Device<Core> = intf.as_ref();
+ dev_info!(dev, "Rust USB driver sample disconnected\n");
+ }
+}
+
+kernel::module_usb_driver! {
+ type: SampleDriver,
+ name: "rust_driver_usb",
+ authors: ["Daniel Almeida"],
+ description: "Rust USB driver sample",
+ license: "GPL v2",
+}
diff --git a/samples/rust/rust_misc_device.rs b/samples/rust/rust_misc_device.rs
index c881fd6dbd08..d69bc33dbd99 100644
--- a/samples/rust/rust_misc_device.rs
+++ b/samples/rust/rust_misc_device.rs
@@ -100,13 +100,13 @@ use core::pin::Pin;
use kernel::{
c_str,
device::Device,
- fs::File,
+ fs::{File, Kiocb},
ioctl::{_IO, _IOC_SIZE, _IOR, _IOW},
+ iov::{IovIterDest, IovIterSource},
miscdevice::{MiscDevice, MiscDeviceOptions, MiscDeviceRegistration},
new_mutex,
prelude::*,
- sync::Mutex,
- types::ARef,
+ sync::{aref::ARef, Mutex},
uaccess::{UserSlice, UserSliceReader, UserSliceWriter},
};
@@ -144,6 +144,7 @@ impl kernel::InPlaceModule for RustMiscDeviceModule {
struct Inner {
value: i32,
+ buffer: KVVec<u8>,
}
#[pin_data(PinnedDrop)]
@@ -165,7 +166,10 @@ impl MiscDevice for RustMiscDevice {
KBox::try_pin_init(
try_pin_init! {
RustMiscDevice {
- inner <- new_mutex!( Inner{ value: 0_i32 } ),
+ inner <- new_mutex!(Inner {
+ value: 0_i32,
+ buffer: KVVec::new(),
+ }),
dev: dev,
}
},
@@ -173,9 +177,38 @@ impl MiscDevice for RustMiscDevice {
)
}
+ fn read_iter(mut kiocb: Kiocb<'_, Self::Ptr>, iov: &mut IovIterDest<'_>) -> Result<usize> {
+ let me = kiocb.file();
+ dev_info!(me.dev, "Reading from Rust Misc Device Sample\n");
+
+ let inner = me.inner.lock();
+ // Read the buffer contents, taking the file position into account.
+ let read = iov.simple_read_from_buffer(kiocb.ki_pos_mut(), &inner.buffer)?;
+
+ Ok(read)
+ }
+
+ fn write_iter(mut kiocb: Kiocb<'_, Self::Ptr>, iov: &mut IovIterSource<'_>) -> Result<usize> {
+ let me = kiocb.file();
+ dev_info!(me.dev, "Writing to Rust Misc Device Sample\n");
+
+ let mut inner = me.inner.lock();
+
+ // Replace buffer contents.
+ inner.buffer.clear();
+ let len = iov.copy_from_iter_vec(&mut inner.buffer, GFP_KERNEL)?;
+
+ // Set position to zero so that future `read` calls will see the new contents.
+ *kiocb.ki_pos_mut() = 0;
+
+ Ok(len)
+ }
+
fn ioctl(me: Pin<&RustMiscDevice>, _file: &File, cmd: u32, arg: usize) -> Result<isize> {
dev_info!(me.dev, "IOCTLing Rust Misc Device Sample\n");
+ // Treat the ioctl argument as a user pointer.
+ let arg = UserPtr::from_addr(arg);
let size = _IOC_SIZE(cmd);
match cmd {
diff --git a/samples/rust/rust_print_main.rs b/samples/rust/rust_print_main.rs
index 8ea95e8c2f36..4095c72afeab 100644
--- a/samples/rust/rust_print_main.rs
+++ b/samples/rust/rust_print_main.rs
@@ -40,7 +40,7 @@ fn arc_print() -> Result {
// behaviour, contract or protocol on both `i32` and `&str` into a single `Arc` of
// type `Arc<dyn Display>`.
- use core::fmt::Display;
+ use kernel::fmt::Display;
fn arc_dyn_print(arc: &Arc<dyn Display>) {
pr_info!("Arc<dyn Display> says {arc}");
}
diff --git a/samples/v4l/v4l2-pci-skeleton.c b/samples/v4l/v4l2-pci-skeleton.c
index d709d82800cd..69925d30329e 100644
--- a/samples/v4l/v4l2-pci-skeleton.c
+++ b/samples/v4l/v4l2-pci-skeleton.c
@@ -470,7 +470,7 @@ static int skeleton_querystd(struct file *file, void *priv, v4l2_std_id *std)
return 0;
}
-static int skeleton_s_dv_timings(struct file *file, void *_fh,
+static int skeleton_s_dv_timings(struct file *file, void *priv,
struct v4l2_dv_timings *timings)
{
struct skeleton *skel = video_drvdata(file);
@@ -509,7 +509,7 @@ static int skeleton_s_dv_timings(struct file *file, void *_fh,
return 0;
}
-static int skeleton_g_dv_timings(struct file *file, void *_fh,
+static int skeleton_g_dv_timings(struct file *file, void *priv,
struct v4l2_dv_timings *timings)
{
struct skeleton *skel = video_drvdata(file);
@@ -522,7 +522,7 @@ static int skeleton_g_dv_timings(struct file *file, void *_fh,
return 0;
}
-static int skeleton_enum_dv_timings(struct file *file, void *_fh,
+static int skeleton_enum_dv_timings(struct file *file, void *priv,
struct v4l2_enum_dv_timings *timings)
{
struct skeleton *skel = video_drvdata(file);
@@ -544,7 +544,7 @@ static int skeleton_enum_dv_timings(struct file *file, void *_fh,
* can lock but that the DMA engine it is connected to cannot handle
* pixelclocks above a certain frequency), then -ERANGE is returned.
*/
-static int skeleton_query_dv_timings(struct file *file, void *_fh,
+static int skeleton_query_dv_timings(struct file *file, void *priv,
struct v4l2_dv_timings *timings)
{
struct skeleton *skel = video_drvdata(file);
@@ -573,7 +573,7 @@ static int skeleton_query_dv_timings(struct file *file, void *_fh,
return 0;
}
-static int skeleton_dv_timings_cap(struct file *file, void *fh,
+static int skeleton_dv_timings_cap(struct file *file, void *priv,
struct v4l2_dv_timings_cap *cap)
{
struct skeleton *skel = video_drvdata(file);