public inbox for drm-ai-reviews@public-inbox.freedesktop.org
 help / color / mirror / Atom feed
* Claude review: rust: devres: return reference in `devres::register`
  2026-03-06 19:35 ` [PATCH v2 1/4] rust: devres: return reference in `devres::register` Markus Probst
@ 2026-03-08 22:21   ` Claude Code Review Bot
  0 siblings, 0 replies; 19+ messages in thread
From: Claude Code Review Bot @ 2026-03-08 22:21 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

This patch modifies `devres::register` to return `Result<&'a T>` instead of `Result`, allowing callers to get a reference to the devres-managed data.

**Safety concern with the returned reference:**

```rust
+    let data_ptr = &raw const *data;
+
     register_foreign(dev, data)
+        // SAFETY: `dev` is valid for the lifetime of 'a. As long as there is a reference to
+        // `Device<Bound>`, it is guaranteed that the device is not unbound and data has not been
+        // dropped. Thus `data_ptr` is also valid for the lifetime of 'a.
+        .map(|()| unsafe { &*data_ptr })
```

The safety argument is that `data_ptr` remains valid for `'a` because the `Device<Bound>` reference lives that long. This works because `register_foreign` calls `data.into_foreign()` which consumes the `Pin<KBox<T>>` without moving the inner `T` -- the data at `data_ptr` is still at the same address, now owned by the devres system.

However, the safety comment should be more precise: it should mention that `into_foreign()` for `Pin<KBox<T>>` extracts the raw pointer without moving the inner `T`, so `data_ptr` continues pointing to valid data as long as the devres action hasn't fired (which is guaranteed while the device remains bound).

**Existing callers are updated correctly** -- `cpufreq.rs` and `drm/driver.rs` now discard the returned reference with `?; Ok(())`, preserving existing behavior.

Overall this patch is acceptable with a minor safety comment refinement.

---

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Claude review: rust: devres: return reference in `devres::register`
  2026-03-13 18:12 ` [PATCH v3 1/4] rust: devres: return reference in `devres::register` Markus Probst
@ 2026-03-16  2:17   ` Claude Code Review Bot
  0 siblings, 0 replies; 19+ messages in thread
From: Claude Code Review Bot @ 2026-03-16  2:17 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

This patch changes `devres::register` to return `Result<&'a T>` instead of `Result`, tying the returned reference's lifetime to the `Device<Bound>` reference.

**Critical safety concern with the lifetime reasoning:**

```rust
+pub fn register<'a, T, E>(
+    dev: &'a Device<Bound>,
+    data: impl PinInit<T, E>,
+    flags: Flags,
+) -> Result<&'a T>
```

The SAFETY comment states:

```rust
+        // SAFETY: `dev` is valid for the lifetime of 'a. As long as there is a reference to
+        // `Device<Bound>`, it is guaranteed that the device is not unbound and data has not been
+        // dropped. Thus `data_ptr` is also valid for the lifetime of 'a.
```

The issue is that `data` is moved into `register_foreign` via `KBox::pin_init` and then consumed by `into_foreign()` inside `register_foreign`. The raw pointer `data_ptr` is taken *before* the data is registered with devres. If `register_foreign` fails (returns an error), `data` is dropped, but we never reach the `.map()` so that's OK. However, the lifetime argument is subtle: the devres-managed data could in theory be freed by devres callbacks (e.g., if the device is removed concurrently) while a `&'a T` reference is still held. The safety argument relies on `Device<Bound>` guaranteeing the device won't unbind while the reference exists. This needs careful scrutiny from the Rust-for-Linux maintainers (Danilo, Benno, Alice) to verify the invariant actually holds.

**The callers now discard the return value:**

```rust
-        devres::register(dev, Self::new()?, GFP_KERNEL)
+        devres::register(dev, Self::new()?, GFP_KERNEL)?;
+        Ok(())
```

This works but changes the API for all existing callers to require `?;` + `Ok(())` where they previously just returned the `Result`. This is somewhat unfortunate ergonomically. An alternative approach might be a separate `devres::register_ref` function to avoid forcing this pattern on callers that don't need the reference.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Claude review: rust: devres: return reference in `devres::register`
  2026-04-11 15:10 ` [PATCH v4 1/4] rust: devres: return reference in `devres::register` Markus Probst via B4 Relay
@ 2026-04-11 22:55   ` Claude Code Review Bot
  0 siblings, 0 replies; 19+ messages in thread
From: Claude Code Review Bot @ 2026-04-11 22:55 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

This patch changes `devres::register` from returning `Result` to `Result<&'a T>`, tying the returned reference's lifetime to the `&'a Device<Bound>` argument. This is needed by patch 3 to access `PrivateData` after registration.

**Soundness analysis**: The approach is sound. After `KBox::pin_init`, a raw pointer `data_ptr` is captured before `register_foreign` takes ownership of the `KBox`. The `KBox`'s heap allocation (and thus the pointed-to data) remains valid as long as devres hasn't cleaned it up. Since devres cleanup occurs at unbind time (after `remove`), and `Device<Bound>` references cannot exist after unbind, tying the reference to `'a` is conservative and correct.

However, the SAFETY comment could be improved:

```rust
+        // SAFETY: `dev` is valid for the lifetime of 'a. As long as there is a reference to
+        // `Device<Bound>`, it is guaranteed that the device is not unbound and data has not been
+        // dropped. Thus `data_ptr` is also valid for the lifetime of 'a.
```

This should also mention that `data_ptr` was obtained from the `KBox` before `register_foreign` took ownership, and that `register_foreign` preserves the heap allocation (it transfers ownership to devres, it doesn't move the data).

**Existing callers updated correctly**: `cpufreq.rs` and `drm/driver.rs` now discard the returned reference with `?;` + `Ok(())`. This is correct but slightly awkward -- a `register_void()` helper or a method that explicitly discards the reference would be cleaner. Minor style point, not blocking.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Claude review: rust: devres: return reference in `devres::register`
  2026-04-20 20:07 ` [PATCH v5 1/4] rust: devres: return reference in `devres::register` Markus Probst
@ 2026-04-22 23:21   ` Claude Code Review Bot
  0 siblings, 0 replies; 19+ messages in thread
From: Claude Code Review Bot @ 2026-04-22 23:21 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

This changes `devres::register` from returning `Result` to `Result<&'a T>`, tying the returned reference's lifetime to the `&'a Device<Bound>` parameter.

The SAFETY comment is the critical part:
```rust
+        // SAFETY: `dev` is valid for the lifetime of 'a. As long as there is a reference to
+        // `Device<Bound>`, it is guaranteed that the device is not unbound and data has not been
+        // dropped. Thus `data_ptr` is also valid for the lifetime of 'a.
+        .map(|()| unsafe { &*data_ptr })
```

This is correct given the semantics of `Device<Bound>` -- the Bound context guarantees the device hasn't been unbound, and devres resources are freed at unbind time. However, the reasoning depends entirely on the invariant that `Device<Bound>` references cannot outlive the bound state. This is an important invariant for the devres subsystem maintainers to validate.

The mechanical updates to `cpufreq.rs` and `drm/driver.rs` (adding `?; Ok(())`) are straightforward and correct -- existing callers that don't use the returned reference simply discard it.

The doc example should also be updated to show the new return type:
```rust
-///     devres::register(dev, Registration::new(), GFP_KERNEL)
+///     devres::register(dev, Registration::new(), GFP_KERNEL)?;
+///     Ok(())
```
This is done correctly.

Already has an `Acked-by` from Viresh Kumar, which is good since it touches cpufreq.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Claude review: rust: devres: return reference in `devres::register`
  2026-04-27 18:05 ` [PATCH v6 1/4] rust: devres: return reference in `devres::register` Markus Probst via B4 Relay
@ 2026-04-28  4:14   ` Claude Code Review Bot
  0 siblings, 0 replies; 19+ messages in thread
From: Claude Code Review Bot @ 2026-04-28  4:14 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

This patch changes `devres::register` from returning `Result` to `Result<&'a T>`, tying the lifetime of the returned reference to the `&'a Device<Bound>` argument.

**The lifetime reasoning is sound.** As long as you hold `&Device<Bound>`, the device is bound and devres-managed data won't be freed, so the pointer remains valid for `'a`.

The raw pointer capture before the move is the right approach:
```rust
let data_ptr = &raw const *data;

register_foreign(dev, data)
    .map(|()| unsafe { &*data_ptr })
```

**Callers updated correctly.** Both `cpufreq.rs` and `drm/driver.rs` now use `?;` + `Ok(())` since they don't need the returned reference — they just discard it. This is a clean, backward-compatible change.

No issues with this patch.

---

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Claude review: rust: devres: return reference in `devres::register`
  2026-04-29 18:21 ` [PATCH v7 1/4] rust: devres: return reference in `devres::register` Markus Probst via B4 Relay
@ 2026-05-05  1:18   ` Claude Code Review Bot
  0 siblings, 0 replies; 19+ messages in thread
From: Claude Code Review Bot @ 2026-05-05  1:18 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

**Summary:** Changes `devres::register` from returning `Result` to `Result<&'a T>`, tying the returned reference's lifetime to the `&'a Device<Bound>` parameter.

The safety argument is sound: devres-managed data is freed when the device is unbound, and holding a `&'a Device<Bound>` guarantees the device hasn't been unbound for lifetime `'a`. The pointer is captured before `register_foreign` takes ownership of the `KBox`:

```rust
+    let data_ptr = &raw const *data;
+
     register_foreign(dev, data)
+        // SAFETY: `dev` is valid for the lifetime of 'a. As long as there is a reference to
+        // `Device<Bound>`, it is guaranteed that the device is not unbound and data has not been
+        // dropped. Thus `data_ptr` is also valid for the lifetime of 'a.
+        .map(|()| unsafe { &*data_ptr })
```

The callers in `cpufreq.rs` and `drm/driver.rs` are updated to discard the new return value with `?; Ok(())`, which is correct. This patch is clean.

No issues.

---

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v10 0/5] rust: add basic serial device bus abstractions
@ 2026-05-30 19:25 Markus Probst
  2026-05-30 19:25 ` [PATCH v10 1/5] rust: devres: return reference in `devres::register` Markus Probst
                   ` (5 more replies)
  0 siblings, 6 replies; 19+ messages in thread
From: Markus Probst @ 2026-05-30 19:25 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst

This patch series adds the serdev device bus rust abstraction into the
kernel.

This abstraction will be used by a driver,
which targets the MCU devices in Synology devices.

Kari Argillander also messaged me, stating that he wants to write a
watchdog driver with this abstraction (needing initial device data).

This series depends on [1] and [2].

[1]
https://lore.kernel.org/rust-for-linux/20260525202921.124698-1-dakr@kernel.org/
[2] https://lore.kernel.org/rust-for-linux/20260530132736.3298549-1-dakr@kernel.org/

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
Changes in v10:
- revert everything from v9, except BoundInternal
- use mutex to ensure receive_buf won't be called on a dropped drvdata.
- Link to v9: https://patch.msgid.link/20260530-rust_serdev-v9-0-f8b5fccb49c3@posteo.de

Changes in v9:
- make use of BoundInternal
- use PrivateData wrapper and drop rust_private_data field
- use non-devm version of serdev_device_open
- Link to v8: https://patch.msgid.link/20260530-rust_serdev-v8-0-2a95f1da22a7@posteo.de

Changes in v8:
- adapted to driver-lifetime v5 patch series
- add MAINTAINERS file patch
- Link to v7: https://patch.msgid.link/20260429-rust_serdev-v7-0-0d89c791b5c8@posteo.de

Changes in v7:
- adapted to driver-lifetime patch series
- Link to v6: https://patch.msgid.link/20260427-rust_serdev-v6-0-173798d5e1a3@posteo.de

Changes in v6:
- rebased onto v7.1-rc1
- Link to v5: https://patch.msgid.link/20260420-rust_serdev-v5-0-57e8ba0519f3@posteo.de

Changes in v5:
- fix typo in documentation
- Link to v4: https://lore.kernel.org/r/20260411-rust_serdev-v4-0-845e960c6627@posteo.de

Changes in v4:
- fixed not selecting rust serdev abstraction in sample
- Link to v3: https://lore.kernel.org/r/20260313-rust_serdev-v3-0-c9a3af214f7f@posteo.de

Changes in v3:
- fix vertical import style
- add Kconfig entry for the rust abstraction
- fix documentation in include/linux/serdev.h
- rename private_data to rust_private_data
- fix `complete_all` <-> `wait_for_completion` typo
- move drvdata_borrow call after the completion
- Link to v2: https://lore.kernel.org/r/20260306-rust_serdev-v2-0-e9b23b42b255@posteo.de

Changes in v2:
- fix documentation in `serdev::Driver::write` and
  `serdev::Driver::write_all`
- remove use of `dev_info` in probe from the sample
- remove `properties_parse` from the sample
- add optional `baudrate` property to the sample
- remove 1. patch
- remove `TryFrom<&device::Device<Ctx>> for &serdev::Device<Ctx>`
  implementation
- fix import style
- add patch to return reference in `devres::register` to fix safety
  issue
- add patch to add private data to serdev_device, to fix
  `Device.drvdata()` from failing
- simplify abstraction by removing ability to receive the initial
  transmission. It may be added later in a separate patch series if
  needed.
- Link to v1: https://lore.kernel.org/r/20251220-rust_serdev-v1-0-e44645767621@posteo.de

---
Markus Probst (5):
      rust: devres: return reference in `devres::register`
      serdev: add rust private data to serdev_device
      rust: add basic serial device bus abstractions
      samples: rust: add Rust serial device bus sample device driver
      MAINTAINERS: serdev: Add self for serdev

 MAINTAINERS                        |   5 +-
 drivers/tty/serdev/Kconfig         |   7 +
 include/linux/serdev.h             |  15 +-
 rust/bindings/bindings_helper.h    |   1 +
 rust/helpers/helpers.c             |   1 +
 rust/helpers/serdev.c              |  22 ++
 rust/kernel/cpufreq.rs             |   3 +-
 rust/kernel/devres.rs              |  15 +-
 rust/kernel/drm/driver.rs          |   3 +-
 rust/kernel/lib.rs                 |   2 +
 rust/kernel/serdev.rs              | 553 +++++++++++++++++++++++++++++++++++++
 samples/rust/Kconfig               |  11 +
 samples/rust/Makefile              |   1 +
 samples/rust/rust_driver_serdev.rs |  91 ++++++
 14 files changed, 719 insertions(+), 11 deletions(-)
---
base-commit: 9e171fc1d7d7ab847a750c03571c87ac3c17bd84
change-id: 20251217-rust_serdev-ee5481e9085c
prerequisite-message-id: 20260505152400.3905096-1-dakr@kernel.org
prerequisite-patch-id: d2aebf69b153c039bbed1d0ed26906708fd22534
prerequisite-patch-id: 84b28da2f5de20fc1785095c647b2ffc35d969a5
prerequisite-patch-id: 67318671a5eed5fb4ad23a450f1cf0e442bf8ca2
prerequisite-message-id: 20260525202921.124698-1-dakr@kernel.org
prerequisite-patch-id: b84db329d4372a175cb8d49e4e88c3eecf7eb228
prerequisite-patch-id: 2c30303f409cc8288cc87e241920219f5ddd8390
prerequisite-patch-id: 4e4f0ad370d763ad00b0f75b91fa216f2cc95953
prerequisite-patch-id: 5bcd6b37f3498feebda275dfef78136eba34004e
prerequisite-patch-id: 872b0982f3e5e7d1698d9df3b325e4cd27b27789
prerequisite-patch-id: 3a3c7749e017d9335f58497404d1350e96caf471
prerequisite-patch-id: 3526c9154f581497a11465b936d83ef61a875454
prerequisite-patch-id: 65d8c757b52475c2acc7d22ddc92cd3f0152b55d
prerequisite-patch-id: 4bd31f1414d5248dc080884caadf5f21684a8427
prerequisite-patch-id: 7beadbb0da3e589ed86d12f512d1c83427dd82b4
prerequisite-patch-id: 12cd0f67ffd27347f90c065db491945908206b7f
prerequisite-patch-id: 4642e31f66331f6c3b579377111ea733dbb3a11c
prerequisite-patch-id: 52d67b40b4396c741e2222d6a5bc7927abcb77aa
prerequisite-patch-id: 74ca82ff26cf9c7a993757c87db8be62006e820f
prerequisite-patch-id: 466fb9fa7febbffd8ef51b311c7d9893c11fc0f0
prerequisite-patch-id: e515cd98b06e26721cbbe6a4fbacd251d0073b63
prerequisite-patch-id: 8dc8e75d9f6499a554ef7e474bbacdbf3660a9f2
prerequisite-patch-id: 5fdb9f71dca2f44dd293760a60db125b770f1f55
prerequisite-patch-id: c766a24c2d5064f5ec09daada0b8e8fba862d3aa
prerequisite-patch-id: b768f6456d35fa7a80c015e34bbdba6082dbd593
prerequisite-patch-id: 6a8b17234f12f7084e6e2ce843a7031b0a891ce4
prerequisite-patch-id: 98b2deb9e60c1f28f90c5ee34fd608aaa9fd9420
prerequisite-patch-id: 774b29be66e641ee50cedb4704cf49d8b9fabf50
prerequisite-patch-id: cf95dc936cfc4b3a7a363435a51a48d9009645b3
prerequisite-message-id: 20260530132736.3298549-1-dakr@kernel.org
prerequisite-patch-id: 310c6bee038ca3909a8e5e58ec12b74f7189b869
prerequisite-patch-id: 92812c3d42b29504838e6dc66c307ff5c035bb5e


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v10 1/5] rust: devres: return reference in `devres::register`
  2026-05-30 19:25 [PATCH v10 0/5] rust: add basic serial device bus abstractions Markus Probst
@ 2026-05-30 19:25 ` Markus Probst
  2026-06-04  5:18   ` Claude review: " Claude Code Review Bot
  2026-05-30 19:25 ` [PATCH v10 2/5] serdev: add rust private data to serdev_device Markus Probst
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: Markus Probst @ 2026-05-30 19:25 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst

Return the reference to the initialized data in the `devres::register`
function.

This is needed in a following commit (rust: add basic serial device bus
abstractions).

Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 rust/kernel/cpufreq.rs    |  3 ++-
 rust/kernel/devres.rs     | 15 +++++++++++++--
 rust/kernel/drm/driver.rs |  3 ++-
 3 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs
index d94c6cdbc45a..bbbd4f54218a 100644
--- a/rust/kernel/cpufreq.rs
+++ b/rust/kernel/cpufreq.rs
@@ -1053,7 +1053,8 @@ pub fn new_foreign_owned(dev: &Device<Bound>) -> Result
     where
         T: 'static,
     {
-        devres::register(dev, Self::new()?, GFP_KERNEL)
+        devres::register(dev, Self::new()?, GFP_KERNEL)?;
+        Ok(())
     }
 }
 
diff --git a/rust/kernel/devres.rs b/rust/kernel/devres.rs
index 82cbd8b969fb..e3e954478caa 100644
--- a/rust/kernel/devres.rs
+++ b/rust/kernel/devres.rs
@@ -419,15 +419,26 @@ fn register_foreign<P>(dev: &Device<Bound>, data: P) -> Result
 /// }
 ///
 /// fn from_bound_context(dev: &Device<Bound>) -> Result {
-///     devres::register(dev, Registration::new(), GFP_KERNEL)
+///     devres::register(dev, Registration::new(), GFP_KERNEL)?;
+///     Ok(())
 /// }
 /// ```
-pub fn register<T, E>(dev: &Device<Bound>, data: impl PinInit<T, E>, flags: Flags) -> Result
+pub fn register<'a, T, E>(
+    dev: &'a Device<Bound>,
+    data: impl PinInit<T, E>,
+    flags: Flags,
+) -> Result<&'a T>
 where
     T: Send + 'static,
     Error: From<E>,
 {
     let data = KBox::pin_init(data, flags)?;
 
+    let data_ptr = &raw const *data;
+
     register_foreign(dev, data)
+        // SAFETY: `dev` is valid for the lifetime of 'a. As long as there is a reference to
+        // `Device<Bound>`, it is guaranteed that the device is not unbound and data has not been
+        // dropped. Thus `data_ptr` is also valid for the lifetime of 'a.
+        .map(|()| unsafe { &*data_ptr })
 }
diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
index 5233bdebc9fc..1edfd7bacddb 100644
--- a/rust/kernel/drm/driver.rs
+++ b/rust/kernel/drm/driver.rs
@@ -147,7 +147,8 @@ pub fn new_foreign_owned(
 
         let reg = Registration::<T>::new(drm, flags)?;
 
-        devres::register(dev, reg, GFP_KERNEL)
+        devres::register(dev, reg, GFP_KERNEL)?;
+        Ok(())
     }
 
     /// Returns a reference to the `Device` instance for this registration.

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v10 2/5] serdev: add rust private data to serdev_device
  2026-05-30 19:25 [PATCH v10 0/5] rust: add basic serial device bus abstractions Markus Probst
  2026-05-30 19:25 ` [PATCH v10 1/5] rust: devres: return reference in `devres::register` Markus Probst
@ 2026-05-30 19:25 ` Markus Probst
  2026-06-04  5:18   ` Claude review: " Claude Code Review Bot
  2026-05-30 19:25 ` [PATCH v10 3/5] rust: add basic serial device bus abstractions Markus Probst
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: Markus Probst @ 2026-05-30 19:25 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst

Add rust private data to `struct serdev_device`, as it is required by the
rust abstraction added in the following commit
(rust: add basic serial device bus abstractions).

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 include/linux/serdev.h | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/include/linux/serdev.h b/include/linux/serdev.h
index b6c3d957ec15..048ef5857786 100644
--- a/include/linux/serdev.h
+++ b/include/linux/serdev.h
@@ -33,12 +33,14 @@ struct serdev_device_ops {
 
 /**
  * struct serdev_device - Basic representation of an serdev device
- * @dev:	Driver model representation of the device.
- * @nr:		Device number on serdev bus.
- * @ctrl:	serdev controller managing this device.
- * @ops:	Device operations.
- * @write_comp:	Completion used by serdev_device_write() internally
- * @write_lock:	Lock to serialize access when writing data
+ * @dev:		Driver model representation of the device.
+ * @nr:			Device number on serdev bus.
+ * @ctrl:		serdev controller managing this device.
+ * @ops:		Device operations.
+ * @write_comp:		Completion used by serdev_device_write() internally
+ * @write_lock:		Lock to serialize access when writing data
+ * @rust_private_data:	Private data for the rust abstraction. This should
+ *			not be used by the C drivers.
  */
 struct serdev_device {
 	struct device dev;
@@ -47,6 +49,7 @@ struct serdev_device {
 	const struct serdev_device_ops *ops;
 	struct completion write_comp;
 	struct mutex write_lock;
+	void *rust_private_data;
 };
 
 #define to_serdev_device(d) container_of_const(d, struct serdev_device, dev)

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v10 3/5] rust: add basic serial device bus abstractions
  2026-05-30 19:25 [PATCH v10 0/5] rust: add basic serial device bus abstractions Markus Probst
  2026-05-30 19:25 ` [PATCH v10 1/5] rust: devres: return reference in `devres::register` Markus Probst
  2026-05-30 19:25 ` [PATCH v10 2/5] serdev: add rust private data to serdev_device Markus Probst
@ 2026-05-30 19:25 ` Markus Probst
  2026-06-04  5:18   ` Claude review: " Claude Code Review Bot
  2026-05-30 19:25 ` [PATCH v10 4/5] samples: rust: add Rust serial device bus sample device driver Markus Probst
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: Markus Probst @ 2026-05-30 19:25 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst

Implement the basic serial device bus abstractions required to write a
serial device bus device driver with or without the need for initial device
data. This includes the following data structures:

The `serdev::Driver` trait represents the interface to the driver.

The `serdev::Device` abstraction represents a `struct serdev_device`.

In order to provide the Serdev specific parts to a generic
`driver::Registration` the `driver::RegistrationOps` trait is
implemented by `serdev::Adapter`.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 MAINTAINERS                     |   2 +
 drivers/tty/serdev/Kconfig      |   7 +
 rust/bindings/bindings_helper.h |   1 +
 rust/helpers/helpers.c          |   1 +
 rust/helpers/serdev.c           |  22 ++
 rust/kernel/lib.rs              |   2 +
 rust/kernel/serdev.rs           | 553 ++++++++++++++++++++++++++++++++++++++++
 7 files changed, 588 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 4e118f704699..d2f608ff8ca0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24276,6 +24276,8 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/serial/serial.yaml
 F:	drivers/tty/serdev/
 F:	include/linux/serdev.h
+F:	rust/helpers/serdev.c
+F:	rust/kernel/serdev.rs
 
 SERIAL IR RECEIVER
 M:	Sean Young <sean@mess.org>
diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig
index 46ae732bfc68..e6dfe949ad01 100644
--- a/drivers/tty/serdev/Kconfig
+++ b/drivers/tty/serdev/Kconfig
@@ -9,6 +9,13 @@ menuconfig SERIAL_DEV_BUS
 
 	  Note that you typically also want to enable TTY port controller support.
 
+config RUST_SERIAL_DEV_BUS_ABSTRACTIONS
+	bool "Rust Serial device bus abstractions"
+	depends on RUST
+	select SERIAL_DEV_BUS
+	help
+	  This enables the Rust abstraction for the serial device bus API.
+
 if SERIAL_DEV_BUS
 
 config SERIAL_DEV_CTRL_TTYPORT
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 446dbeaf0866..4e42635b8607 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -84,6 +84,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/sched.h>
 #include <linux/security.h>
+#include <linux/serdev.h>
 #include <linux/slab.h>
 #include <linux/sys_soc.h>
 #include <linux/task_work.h>
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 38b34518eff1..2fb8506a748a 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -86,6 +86,7 @@
 #include "regulator.c"
 #include "scatterlist.c"
 #include "security.c"
+#include "serdev.c"
 #include "signal.c"
 #include "slab.c"
 #include "spinlock.c"
diff --git a/rust/helpers/serdev.c b/rust/helpers/serdev.c
new file mode 100644
index 000000000000..c52b78ca3fc7
--- /dev/null
+++ b/rust/helpers/serdev.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/serdev.h>
+
+__rust_helper
+void rust_helper_serdev_device_driver_unregister(struct serdev_device_driver *sdrv)
+{
+	serdev_device_driver_unregister(sdrv);
+}
+
+__rust_helper
+void rust_helper_serdev_device_put(struct serdev_device *serdev)
+{
+	serdev_device_put(serdev);
+}
+
+__rust_helper
+void rust_helper_serdev_device_set_client_ops(struct serdev_device *serdev,
+					      const struct serdev_device_ops *ops)
+{
+	serdev_device_set_client_ops(serdev, ops);
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index b72b2fbe046d..83bc2c312241 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -118,6 +118,8 @@
 pub mod scatterlist;
 pub mod security;
 pub mod seq_file;
+#[cfg(CONFIG_RUST_SERIAL_DEV_BUS_ABSTRACTIONS)]
+pub mod serdev;
 pub mod sizes;
 #[cfg(CONFIG_SOC_BUS)]
 pub mod soc;
diff --git a/rust/kernel/serdev.rs b/rust/kernel/serdev.rs
new file mode 100644
index 000000000000..cc9fc88d6923
--- /dev/null
+++ b/rust/kernel/serdev.rs
@@ -0,0 +1,553 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Abstractions for the serial device bus.
+//!
+//! C header: [`include/linux/serdev.h`](srctree/include/linux/serdev.h)
+
+use crate::{
+    acpi,
+    device,
+    devres,
+    driver,
+    error::{
+        from_result,
+        to_result,
+        VTABLE_DEFAULT_ERROR, //
+    },
+    new_mutex,
+    of,
+    prelude::*,
+    sync::{
+        aref::AlwaysRefCounted,
+        Mutex, //
+    },
+    time::{
+        msecs_to_jiffies,
+        Jiffies,
+        Msecs, //
+    },
+    types::Opaque, //
+};
+
+use core::{
+    marker::PhantomData,
+    mem::offset_of,
+    num::NonZero,
+    ptr::NonNull, //
+};
+
+/// Parity bit to use with a serial device.
+#[repr(u32)]
+pub enum Parity {
+    /// No parity bit.
+    None = bindings::serdev_parity_SERDEV_PARITY_NONE,
+    /// Even partiy.
+    Even = bindings::serdev_parity_SERDEV_PARITY_EVEN,
+    /// Odd parity.
+    Odd = bindings::serdev_parity_SERDEV_PARITY_ODD,
+}
+
+/// Timeout in Jiffies.
+pub enum Timeout {
+    /// Wait for a specific amount of [`Jiffies`].
+    Jiffies(NonZero<Jiffies>),
+    /// Wait for a specific amount of [`Msecs`].
+    Milliseconds(NonZero<Msecs>),
+    /// Wait as long as possible.
+    ///
+    /// This is equivalent to [`kernel::task::MAX_SCHEDULE_TIMEOUT`].
+    Max,
+}
+
+impl Timeout {
+    fn into_jiffies(self) -> isize {
+        match self {
+            Self::Jiffies(value) => value.get().try_into().unwrap_or_default(),
+            Self::Milliseconds(value) => {
+                msecs_to_jiffies(value.get()).try_into().unwrap_or_default()
+            }
+            Self::Max => 0,
+        }
+    }
+}
+
+/// An adapter for the registration of serial device bus device drivers.
+pub struct Adapter<T: Driver>(T);
+
+// SAFETY:
+// - `bindings::serdev_device_driver` is a C type declared as `repr(C)`.
+// - `T::Data` is the type of the driver's device private data.
+// - `struct serdev_device_driver` embeds a `struct device_driver`.
+// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
+unsafe impl<T: Driver> driver::DriverLayout for Adapter<T> {
+    type DriverType = bindings::serdev_device_driver;
+    type DriverData<'bound> = T::Data<'bound>;
+    const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
+}
+
+// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
+// a preceding call to `register` has been successful.
+unsafe impl<T: Driver> driver::RegistrationOps for Adapter<T> {
+    unsafe fn register(
+        sdrv: &Opaque<Self::DriverType>,
+        name: &'static CStr,
+        module: &'static ThisModule,
+    ) -> Result {
+        let of_table = match T::OF_ID_TABLE {
+            Some(table) => table.as_ptr(),
+            None => core::ptr::null(),
+        };
+
+        let acpi_table = match T::ACPI_ID_TABLE {
+            Some(table) => table.as_ptr(),
+            None => core::ptr::null(),
+        };
+
+        // SAFETY: It's safe to set the fields of `struct serdev_device_driver` on initialization.
+        unsafe {
+            (*sdrv.get()).driver.name = name.as_char_ptr();
+            (*sdrv.get()).probe = Some(Self::probe_callback);
+            (*sdrv.get()).remove = Some(Self::remove_callback);
+            (*sdrv.get()).driver.of_match_table = of_table;
+            (*sdrv.get()).driver.acpi_match_table = acpi_table;
+        }
+
+        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
+        to_result(unsafe { bindings::__serdev_device_driver_register(sdrv.get(), module.0) })
+    }
+
+    unsafe fn unregister(sdrv: &Opaque<Self::DriverType>) {
+        // SAFETY: `sdrv` is guaranteed to be a valid `DriverType`.
+        unsafe { bindings::serdev_device_driver_unregister(sdrv.get()) };
+    }
+}
+
+#[pin_data]
+struct PrivateData {
+    #[pin]
+    active: Mutex<bool>,
+}
+
+impl<T: Driver> Adapter<T> {
+    const OPS: &'static bindings::serdev_device_ops = &bindings::serdev_device_ops {
+        receive_buf: if T::HAS_RECEIVE {
+            Some(Self::receive_buf_callback)
+        } else {
+            None
+        },
+        write_wakeup: Some(bindings::serdev_device_write_wakeup),
+    };
+
+    extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> kernel::ffi::c_int {
+        // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer to
+        // a `struct serdev_device`.
+        //
+        // INVARIANT: `sdev` is valid for the duration of `probe_callback()`.
+        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal<'_>>>() };
+        let info = <Self as driver::Adapter>::id_info(sdev.as_ref());
+
+        from_result(|| {
+            let private_data = devres::register(
+                sdev.as_ref(),
+                try_pin_init!(PrivateData {
+                    active <- new_mutex!(false),
+                }),
+                GFP_KERNEL,
+            )?;
+            let mut active = private_data.active.lock();
+
+            // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
+            unsafe {
+                (*sdev.as_raw()).rust_private_data =
+                    (&raw const *private_data).cast::<c_void>().cast_mut()
+            };
+
+            // SAFETY: `sdev.as_raw()` is guaranteed to be a valid pointer to `serdev_device`.
+            unsafe { bindings::serdev_device_set_client_ops(sdev.as_raw(), Self::OPS) };
+
+            // SAFETY: The serial device bus only ever calls the probe callback with a valid pointer
+            // to a `serdev_device`.
+            to_result(unsafe {
+                bindings::devm_serdev_device_open(sdev.as_ref().as_raw(), sdev.as_raw())
+            })?;
+
+            let data = T::probe(sdev, info);
+            let result = sdev.as_ref().set_drvdata(data);
+
+            *active = result.is_ok();
+
+            result.map(|()| 0)
+        })
+    }
+
+    extern "C" fn remove_callback(sdev: *mut bindings::serdev_device) {
+        // SAFETY: The serial device bus only ever calls the remove callback with a valid pointer
+        // to a `struct serdev_device`.
+        //
+        // INVARIANT: `sdev` is valid for the duration of `remove_callback()`.
+        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal<'_>>>() };
+
+        // SAFETY: `remove_callback` is only ever called after a successful call to
+        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
+        // and stored a `Pin<KBox<T::Data<'_>>>`.
+        let data = unsafe { sdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
+
+        // SAFETY:
+        // - The serial device bus only ever calls the remove callback with a valid pointer to
+        //   a `struct serdev_device`.
+        // - `remove_callback` is only ever called after a successful call to
+        //   `probe_callback`, hence it's guaranteed that `sdev.private_data` is a pointer
+        //   to a valid `PrivateData`.
+        let private_data = unsafe { &*(*sdev.as_raw()).rust_private_data.cast::<PrivateData>() };
+
+        *private_data.active.lock() = false;
+
+        T::unbind(sdev, data);
+    }
+
+    extern "C" fn receive_buf_callback(
+        sdev: *mut bindings::serdev_device,
+        buf: *const u8,
+        length: usize,
+    ) -> usize {
+        // SAFETY: The serial device bus only ever calls the receive buf callback with a valid
+        // pointer to a `struct serdev_device`.
+        //
+        // INVARIANT: `sdev` is valid for the duration of `receive_buf_callback()`.
+        let sdev = unsafe { &*sdev.cast::<Device<device::BoundInternal>>() };
+
+        // SAFETY:
+        // - The serial device bus only ever calls the receive buf callback with a valid pointer to
+        //   a `struct serdev_device`.
+        // - `receive_buf_callback` is only ever called after a successful call to
+        //   `probe_callback`, hence it's guaranteed that `sdev.private_data` is a pointer
+        //   to a valid `PrivateData`.
+        let private_data = unsafe { &*(*sdev.as_raw()).rust_private_data.cast::<PrivateData>() };
+
+        let active = private_data.active.lock();
+
+        if !*active {
+            return length;
+        }
+
+        // SAFETY: `receive_buf_callback` is only ever called after a successful call to
+        // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called
+        // and stored a `Pin<KBox<T::Data<'_>>>`.
+        let data = unsafe { sdev.as_ref().drvdata_borrow::<T::Data<'_>>() };
+
+        // SAFETY: `buf` is guaranteed to be non-null and has the size of `length`.
+        let buf = unsafe { core::slice::from_raw_parts(buf, length) };
+
+        T::receive(sdev, data, buf)
+    }
+}
+
+impl<T: Driver> driver::Adapter for Adapter<T> {
+    type IdInfo = T::IdInfo;
+
+    fn of_id_table() -> Option<of::IdTable<Self::IdInfo>> {
+        T::OF_ID_TABLE
+    }
+
+    fn acpi_id_table() -> Option<acpi::IdTable<Self::IdInfo>> {
+        T::ACPI_ID_TABLE
+    }
+}
+
+/// Declares a kernel module that exposes a single serial device bus device driver.
+///
+/// # Examples
+///
+/// ```ignore
+/// kernel::module_serdev_device_driver! {
+///     type: MyDriver,
+///     name: "Module name",
+///     authors: ["Author name"],
+///     description: "Description",
+///     license: "GPL v2",
+/// }
+/// ```
+#[macro_export]
+macro_rules! module_serdev_device_driver {
+    ($($f:tt)*) => {
+        $crate::module_driver!(<T>, $crate::serdev::Adapter<T>, { $($f)* });
+    };
+}
+
+/// The serial device bus device driver trait.
+///
+/// Drivers must implement this trait in order to get a serial device bus device driver registered.
+///
+/// # Examples
+///
+///```
+/// # use kernel::{
+///     acpi,
+///     bindings,
+///     device::{
+///         Bound,
+///         Core, //
+///     },
+///     of,
+///     serdev, //
+/// };
+///
+/// struct MyDriver;
+///
+/// kernel::of_device_table!(
+///     OF_TABLE,
+///     MODULE_OF_TABLE,
+///     <MyDriver as serdev::Driver>::IdInfo,
+///     [
+///         (of::DeviceId::new(c"test,device"), ())
+///     ]
+/// );
+///
+/// kernel::acpi_device_table!(
+///     ACPI_TABLE,
+///     MODULE_ACPI_TABLE,
+///     <MyDriver as serdev::Driver>::IdInfo,
+///     [
+///         (acpi::DeviceId::new(c"LNUXBEEF"), ())
+///     ]
+/// );
+///
+/// #[vtable]
+/// impl serdev::Driver for MyDriver {
+///     type IdInfo = ();
+///     type Data<'bound> = Self;
+///     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<'bound>(
+///         sdev: &'bound serdev::Device<Core<'_>>,
+///         _id_info: Option<&'bound Self::IdInfo>,
+///     ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound {
+///         sdev.set_baudrate(115200);
+///         sdev.write_all(b"Hello\n", serdev::Timeout::Max)?;
+///         Ok(MyDriver)
+///     }
+/// }
+///```
+#[vtable]
+pub trait Driver {
+    /// The type holding driver private data about each device id supported by the driver.
+    // TODO: Use associated_type_defaults once stabilized:
+    //
+    // ```
+    // type IdInfo: 'static = ();
+    // ```
+    type IdInfo: 'static;
+
+    /// The type of the driver's bus device private data.
+    type Data<'bound>: Send + 'bound;
+
+    /// The table of OF device ids supported by the driver.
+    const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = None;
+
+    /// The table of ACPI device ids supported by the driver.
+    const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
+
+    /// Serial device bus device driver probe.
+    ///
+    /// Called when a new serial device bus device is added or discovered.
+    /// Implementers should attempt to initialize the device here.
+    fn probe<'bound>(
+        sdev: &'bound Device<device::Core<'_>>,
+        id_info: Option<&'bound Self::IdInfo>,
+    ) -> impl PinInit<Self::Data<'bound>, Error> + 'bound;
+
+    /// Serial device bus device driver unbind.
+    ///
+    /// Called when a [`Device`] is unbound from its bound [`Driver`]. Implementing this callback
+    /// is optional.
+    ///
+    /// This callback serves as a place for drivers to perform teardown operations that require a
+    /// `&Device<Core>` or `&Device<Bound>` reference. For instance.
+    ///
+    /// Otherwise, release operations for driver resources should be performed in `Drop`.
+    fn unbind<'bound>(sdev: &'bound Device<device::Core<'_>>, this: Pin<&Self::Data<'bound>>) {
+        let _ = (sdev, this);
+    }
+
+    /// Serial device bus device data receive callback.
+    ///
+    /// Called when data got received from device.
+    ///
+    /// Returns the number of bytes accepted.
+    fn receive<'bound>(
+        sdev: &'bound Device<device::Bound>,
+        this: Pin<&Self::Data<'bound>>,
+        data: &[u8],
+    ) -> usize {
+        let _ = (sdev, this, data);
+        build_error!(VTABLE_DEFAULT_ERROR)
+    }
+}
+
+/// The serial device bus device representation.
+///
+/// This structure represents the Rust abstraction for a C `struct serdev_device`. The
+/// implementation abstracts the usage of an already existing C `struct serdev_device` within Rust
+/// code that we get passed from the C side.
+///
+/// # Invariants
+///
+/// A [`Device`] instance represents a valid `struct serdev_device` created by the C portion of
+/// the kernel.
+#[repr(transparent)]
+pub struct Device<Ctx: device::DeviceContext = device::Normal>(
+    Opaque<bindings::serdev_device>,
+    PhantomData<Ctx>,
+);
+
+impl<Ctx: device::DeviceContext> Device<Ctx> {
+    fn as_raw(&self) -> *mut bindings::serdev_device {
+        self.0.get()
+    }
+}
+
+impl Device<device::Bound> {
+    /// Set the baudrate in bits per second.
+    ///
+    /// Common baudrates are 115200, 9600, 19200, 57600, 4800.
+    ///
+    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
+    pub fn set_baudrate(&self, speed: u32) -> Result<(), u32> {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        let ret = unsafe { bindings::serdev_device_set_baudrate(self.as_raw(), speed) };
+        if ret == speed {
+            Ok(())
+        } else {
+            Err(ret)
+        }
+    }
+
+    /// Set if flow control should be enabled.
+    ///
+    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
+    pub fn set_flow_control(&self, enable: bool) {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        unsafe { bindings::serdev_device_set_flow_control(self.as_raw(), enable) };
+    }
+
+    /// Set parity to use.
+    ///
+    /// Use [`Device::write_flush`] before calling this if you have written data prior to this call.
+    pub fn set_parity(&self, parity: Parity) -> Result {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        to_result(unsafe { bindings::serdev_device_set_parity(self.as_raw(), parity as u32) })
+    }
+
+    /// Write data to the serial device until the controller has accepted all the data or has
+    /// been interrupted by a timeout or signal.
+    ///
+    /// Note that any accepted data has only been buffered by the controller. Use
+    /// [`Device::wait_until_sent`] to make sure the controller write buffer has actually been
+    /// emptied.
+    ///
+    /// Returns the number of bytes written (less than `data.len()` if interrupted).
+    /// [`kernel::error::code::ETIMEDOUT`] or [`kernel::error::code::ERESTARTSYS`] if interrupted
+    /// before any bytes were written.
+    pub fn write_all(&self, data: &[u8], timeout: Timeout) -> Result<usize> {
+        // SAFETY:
+        // - `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        // - `data.as_ptr()` is guaranteed to be a valid array pointer with the size of
+        //   `data.len()`.
+        let ret = unsafe {
+            bindings::serdev_device_write(
+                self.as_raw(),
+                data.as_ptr(),
+                data.len(),
+                timeout.into_jiffies(),
+            )
+        };
+        // CAST: negative return values are guaranteed to be between `-MAX_ERRNO` and `-1`,
+        // which always fit into a `i32`.
+        to_result(ret as i32).map(|()| ret.unsigned_abs())
+    }
+
+    /// Write data to the serial device.
+    ///
+    /// If you want to write until the controller has accepted all the data, use
+    /// [`Device::write_all`].
+    ///
+    /// Note that any accepted data has only been buffered by the controller. Use
+    /// [ Device::wait_until_sent`] to make sure the controller write buffer has actually been
+    /// emptied.
+    ///
+    /// Returns the number of bytes written (less than `data.len()` if not enough room in the
+    /// write buffer).
+    pub fn write(&self, data: &[u8]) -> Result<u32> {
+        // SAFETY:
+        // - `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        // - `data.as_ptr()` is guaranteed to be a valid array pointer with the size of
+        //   `data.len()`.
+        let ret =
+            unsafe { bindings::serdev_device_write_buf(self.as_raw(), data.as_ptr(), data.len()) };
+
+        to_result(ret as i32).map(|()| ret.unsigned_abs())
+    }
+
+    /// Send data to the serial device immediately.
+    ///
+    /// Note that this doesn't guarantee that the data has been transmitted.
+    /// Use [`Device::wait_until_sent`] for this purpose.
+    pub fn write_flush(&self) {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        unsafe { bindings::serdev_device_write_flush(self.as_raw()) };
+    }
+
+    /// Wait for the data to be sent.
+    ///
+    /// After this function, the write buffer of the controller should be empty.
+    pub fn wait_until_sent(&self, timeout: Timeout) {
+        // SAFETY: `self.as_raw()` is guaranteed to be a pointer to a valid `serdev_device`.
+        unsafe { bindings::serdev_device_wait_until_sent(self.as_raw(), timeout.into_jiffies()) };
+    }
+}
+
+// SAFETY: `serdev::Device` is a transparent wrapper of `struct serdev_device`.
+// The offset is guaranteed to point to a valid device field inside `serdev::Device`.
+unsafe impl<Ctx: device::DeviceContext> device::AsBusDevice<Ctx> for Device<Ctx> {
+    const OFFSET: usize = offset_of!(bindings::serdev_device, dev);
+}
+
+// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
+// argument.
+kernel::impl_device_context_deref!(unsafe { Device });
+kernel::impl_device_context_into_aref!(Device);
+
+// SAFETY: Instances of `Device` are always reference-counted.
+unsafe impl AlwaysRefCounted for Device {
+    fn inc_ref(&self) {
+        self.as_ref().inc_ref();
+    }
+
+    unsafe fn dec_ref(obj: NonNull<Self>) {
+        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+        unsafe { bindings::serdev_device_put(obj.cast().as_ptr()) }
+    }
+}
+
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
+    fn as_ref(&self) -> &device::Device<Ctx> {
+        // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
+        // `struct serdev_device`.
+        let dev = unsafe { &raw mut (*self.as_raw()).dev };
+
+        // SAFETY: `dev` points to a valid `struct device`.
+        unsafe { device::Device::from_raw(dev) }
+    }
+}
+
+// SAFETY: A `Device` is always reference-counted and can be released from any thread.
+unsafe impl Send for Device {}
+
+// SAFETY: `Device` can be shared among threads because all methods of `Device`
+// (i.e. `Device<Normal>) are thread safe.
+unsafe impl Sync for Device {}
+
+// SAFETY: Same as `Device<Normal>` -- the underlying `struct serdev_device` is the same;
+// `Bound` is a zero-sized type-state marker that does not affect thread safety.
+unsafe impl Sync for Device<device::Bound> {}

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v10 4/5] samples: rust: add Rust serial device bus sample device driver
  2026-05-30 19:25 [PATCH v10 0/5] rust: add basic serial device bus abstractions Markus Probst
                   ` (2 preceding siblings ...)
  2026-05-30 19:25 ` [PATCH v10 3/5] rust: add basic serial device bus abstractions Markus Probst
@ 2026-05-30 19:25 ` Markus Probst
  2026-06-04  5:18   ` Claude review: " Claude Code Review Bot
  2026-05-30 19:25 ` [PATCH v10 5/5] MAINTAINERS: serdev: Add self for serdev Markus Probst
  2026-06-04  5:18 ` Claude review: rust: add basic serial device bus abstractions Claude Code Review Bot
  5 siblings, 1 reply; 19+ messages in thread
From: Markus Probst @ 2026-05-30 19:25 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst

Add a sample Rust serial device bus device driver illustrating the usage
of the serial device bus abstractions.

This drivers probes through either a match of device / driver name or a
match within the OF ID table.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 MAINTAINERS                        |  1 +
 samples/rust/Kconfig               | 11 +++++
 samples/rust/Makefile              |  1 +
 samples/rust/rust_driver_serdev.rs | 91 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 104 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index d2f608ff8ca0..fa3f20f0cc4f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24278,6 +24278,7 @@ F:	drivers/tty/serdev/
 F:	include/linux/serdev.h
 F:	rust/helpers/serdev.c
 F:	rust/kernel/serdev.rs
+F:	samples/rust/rust_driver_serdev.rs
 
 SERIAL IR RECEIVER
 M:	Sean Young <sean@mess.org>
diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index c49ab9106345..31d62533ef25 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -161,6 +161,17 @@ config SAMPLE_RUST_DRIVER_AUXILIARY
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_DRIVER_SERDEV
+	tristate "Serial Device Bus Device Driver"
+	select RUST_SERIAL_DEV_BUS_ABSTRACTIONS
+	help
+	  This option builds the Rust serial device bus driver sample.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_driver_serdev.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_SOC
 	tristate "SoC Driver"
 	select SOC_BUS
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index 6c0aaa58cccc..b986b681cde5 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -14,6 +14,7 @@ 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_DRIVER_SERDEV)		+= rust_driver_serdev.o
 obj-$(CONFIG_SAMPLE_RUST_CONFIGFS)		+= rust_configfs.o
 obj-$(CONFIG_SAMPLE_RUST_SOC)			+= rust_soc.o
 
diff --git a/samples/rust/rust_driver_serdev.rs b/samples/rust/rust_driver_serdev.rs
new file mode 100644
index 000000000000..824affbf6593
--- /dev/null
+++ b/samples/rust/rust_driver_serdev.rs
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust Serial device bus device driver sample.
+
+use kernel::{
+    acpi,
+    device::{
+        Bound,
+        Core, //
+    },
+    of,
+    prelude::*,
+    serdev,
+    sync::aref::ARef, //
+};
+
+struct SampleDriver {
+    sdev: ARef<serdev::Device>,
+}
+
+kernel::of_device_table!(
+    OF_TABLE,
+    MODULE_OF_TABLE,
+    <SampleDriver as serdev::Driver>::IdInfo,
+    [(of::DeviceId::new(c"test,rust_driver_serdev"), ())]
+);
+
+kernel::acpi_device_table!(
+    ACPI_TABLE,
+    MODULE_ACPI_TABLE,
+    <SampleDriver as serdev::Driver>::IdInfo,
+    [(acpi::DeviceId::new(c"LNUXBEEF"), ())]
+);
+
+#[vtable]
+impl serdev::Driver for SampleDriver {
+    type IdInfo = ();
+    type Data<'bound> = Self;
+    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<'bound>(
+        sdev: &'bound serdev::Device<Core<'_>>,
+        _info: Option<&'bound Self::IdInfo>,
+    ) -> impl PinInit<Self, Error> + 'bound {
+        let dev = sdev.as_ref();
+
+        dev_dbg!(dev, "Probe Rust Serial device bus device driver sample.\n");
+
+        if sdev
+            .set_baudrate(
+                dev.fwnode()
+                    .and_then(|fwnode| fwnode.property_read(c"baudrate").optional())
+                    .unwrap_or(115200),
+            )
+            .is_err()
+        {
+            return Err(EINVAL);
+        }
+        sdev.set_flow_control(false);
+        sdev.set_parity(serdev::Parity::None)?;
+
+        Ok(Self { sdev: sdev.into() })
+    }
+
+    fn receive<'bound>(
+        sdev: &'bound serdev::Device<Bound>,
+        _this: Pin<&Self>,
+        data: &[u8],
+    ) -> usize {
+        let _ = sdev.write_all(data, serdev::Timeout::Max);
+        data.len()
+    }
+}
+
+impl Drop for SampleDriver {
+    fn drop(&mut self) {
+        dev_dbg!(
+            self.sdev.as_ref(),
+            "Remove Rust Serial device bus device driver sample.\n"
+        );
+    }
+}
+
+kernel::module_serdev_device_driver! {
+    type: SampleDriver,
+    name: "rust_driver_serdev",
+    authors: ["Markus Probst"],
+    description: "Rust Serial device bus device driver",
+    license: "GPL v2",
+}

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH v10 5/5] MAINTAINERS: serdev: Add self for serdev
  2026-05-30 19:25 [PATCH v10 0/5] rust: add basic serial device bus abstractions Markus Probst
                   ` (3 preceding siblings ...)
  2026-05-30 19:25 ` [PATCH v10 4/5] samples: rust: add Rust serial device bus sample device driver Markus Probst
@ 2026-05-30 19:25 ` Markus Probst
  2026-06-04  5:18   ` Claude review: " Claude Code Review Bot
  2026-06-04  5:18 ` Claude review: rust: add basic serial device bus abstractions Claude Code Review Bot
  5 siblings, 1 reply; 19+ messages in thread
From: Markus Probst @ 2026-05-30 19:25 UTC (permalink / raw)
  To: Rob Herring, Greg Kroah-Hartman, Jiri Slaby, Miguel Ojeda,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Alice Ryhl, Trevor Gross, Danilo Krummrich, Kari Argillander,
	Rafael J. Wysocki, Viresh Kumar, Boqun Feng, David Airlie,
	Simona Vetter, Boqun Feng
  Cc: linux-serial, linux-kernel, rust-for-linux, linux-pm, driver-core,
	dri-devel, Markus Probst

Rob mentioned he needs to find someone else to maintain serdev.

Link: https://lore.kernel.org/rust-for-linux/20260430195858.GA1650658-robh@kernel.org/
Link: https://lore.kernel.org/rust-for-linux/da85ceb81f51079d4a8248a1ffde6a27d2ef24ad.camel@posteo.de/
Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 MAINTAINERS | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index fa3f20f0cc4f..58db6aedf45a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24270,7 +24270,7 @@ F:	drivers/iio/chemical/sps30_i2c.c
 F:	drivers/iio/chemical/sps30_serial.c
 
 SERIAL DEVICE BUS
-M:	Rob Herring <robh@kernel.org>
+M:	Markus Probst <markus.probst@posteo.de>
 L:	linux-serial@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/serial/serial.yaml

-- 
2.53.0


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Claude review: rust: add basic serial device bus abstractions
  2026-05-30 19:25 [PATCH v10 0/5] rust: add basic serial device bus abstractions Markus Probst
                   ` (4 preceding siblings ...)
  2026-05-30 19:25 ` [PATCH v10 5/5] MAINTAINERS: serdev: Add self for serdev Markus Probst
@ 2026-06-04  5:18 ` Claude Code Review Bot
  5 siblings, 0 replies; 19+ messages in thread
From: Claude Code Review Bot @ 2026-06-04  5:18 UTC (permalink / raw)
  To: dri-devel-reviews

Overall Series Review

Subject: rust: add basic serial device bus abstractions
Author: Markus Probst <markus.probst@posteo.de>
Patches: 6
Reviewed: 2026-06-04T15:18:43.045766

---

This is a v10 patch series adding Rust abstractions for the Linux serial device bus (serdev). The series is well-structured across 5 patches: a preparatory devres change, a C-side struct modification, the main Rust abstraction, a sample driver, and a MAINTAINERS update.

The overall design follows established Rust-for-Linux patterns: a `Driver` trait with vtable callbacks, a transparent `Device` wrapper with type-state (`Core`, `Bound`, `Normal`), and an `Adapter` bridging `RegistrationOps`. The `PrivateData` mutex approach for guarding `receive_buf` against use-after-free on the driver data during teardown is a reasonable solution, though it introduces per-callback locking overhead.

The code is generally clean and well-documented. I have a handful of concerns below, mostly around the `devres::register` safety argument, a typo, and a few minor API design questions.

---

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Claude review: rust: devres: return reference in `devres::register`
  2026-05-30 19:25 ` [PATCH v10 1/5] rust: devres: return reference in `devres::register` Markus Probst
@ 2026-06-04  5:18   ` Claude Code Review Bot
  0 siblings, 0 replies; 19+ messages in thread
From: Claude Code Review Bot @ 2026-06-04  5:18 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

This patch changes `devres::register` to return `Result<&'a T>` instead of `Result`, tying the returned reference's lifetime to the `&'a Device<Bound>`.

The core change:

```rust
+pub fn register<'a, T, E>(
+    dev: &'a Device<Bound>,
+    data: impl PinInit<T, E>,
+    flags: Flags,
+) -> Result<&'a T>
...
+    let data_ptr = &raw const *data;
+
     register_foreign(dev, data)
+        // SAFETY: `dev` is valid for the lifetime of 'a. As long as there is a reference to
+        // `Device<Bound>`, it is guaranteed that the device is not unbound and data has not been
+        // dropped. Thus `data_ptr` is also valid for the lifetime of 'a.
+        .map(|()| unsafe { &*data_ptr })
```

**Concern: Safety of the returned reference.** The safety comment says "As long as there is a reference to `Device<Bound>`, it is guaranteed that the device is not unbound and data has not been dropped." This is the critical invariant. After `register_foreign` moves ownership of `data` (the `KBox`) to the devres system, dereferencing `data_ptr` is only valid if the devres entry outlives `'a`. The argument is that `'a` is tied to `&Device<Bound>`, and `Bound` state guarantees the device won't be unbound (and hence devres won't fire) during that lifetime. This seems sound given the Rust driver model's `Bound` guarantees, but this is a subtle invariant that deserves scrutiny from the Rust-for-Linux maintainers, particularly regarding whether any path could trigger devres cleanup while a `&Device<Bound>` still exists.

The callers (`cpufreq.rs`, `drm/driver.rs`) are updated to discard the new return value with `?; Ok(())`, which is straightforward and correct.

**Nit:** The raw pointer `data_ptr` is taken before `register_foreign` moves `data`. Since `register_foreign` takes `data: P` by value (a `Pin<KBox<T>>`), the pointer remains valid only because register_foreign stores it in devres. The ordering is correct, but worth calling out in the safety comment that `data_ptr` points into the `KBox`-allocated memory which `register_foreign` takes ownership of.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Claude review: serdev: add rust private data to serdev_device
  2026-05-30 19:25 ` [PATCH v10 2/5] serdev: add rust private data to serdev_device Markus Probst
@ 2026-06-04  5:18   ` Claude Code Review Bot
  0 siblings, 0 replies; 19+ messages in thread
From: Claude Code Review Bot @ 2026-06-04  5:18 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

```c
+	void *rust_private_data;
```

This adds a `void *rust_private_data` field to `struct serdev_device` in the C header. The field is documented as "Private data for the rust abstraction. This should not be used by the C drivers."

**Question:** Adding a field to a C struct that is only used by Rust is somewhat unusual. Normally bus subsystems provide a mechanism like `dev_set_drvdata()` for private data. The commit message says this is "required by the rust abstraction" but it would benefit from a brief explanation of *why* the existing `drvdata` mechanism isn't sufficient (presumably because `drvdata` is used for `T::Data` and the abstraction needs a second private data slot for the `PrivateData` mutex). The serdev maintainer (Rob Herring, being replaced in patch 5) should weigh in.

**Minor:** The doc comment reformatting (re-aligning the `@` entries with tabs) is a cosmetic change bundled with the functional change. It's harmless but slightly noisy.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Claude review: rust: add basic serial device bus abstractions
  2026-05-30 19:25 ` [PATCH v10 3/5] rust: add basic serial device bus abstractions Markus Probst
@ 2026-06-04  5:18   ` Claude Code Review Bot
  0 siblings, 0 replies; 19+ messages in thread
From: Claude Code Review Bot @ 2026-06-04  5:18 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

This is the main patch at 553 lines. Several observations:

**1. Typo in `Parity` enum:**

```rust
+    /// Even partiy.
+    Even = bindings::serdev_parity_SERDEV_PARITY_EVEN,
```

"partiy" should be "parity".

**2. `Timeout::into_jiffies` and the `Max` variant:**

```rust
+impl Timeout {
+    fn into_jiffies(self) -> isize {
+        match self {
+            Self::Jiffies(value) => value.get().try_into().unwrap_or_default(),
+            Self::Milliseconds(value) => {
+                msecs_to_jiffies(value.get()).try_into().unwrap_or_default()
+            }
+            Self::Max => 0,
+        }
+    }
+}
```

`Timeout::Max` returns 0, which works because the C `serdev_device_write()` treats `timeout == 0` as `MAX_SCHEDULE_TIMEOUT`. However, `serdev_device_wait_until_sent()` passes this timeout directly to `tty_wait_until_sent()`, which calls `schedule_timeout()`. For `schedule_timeout()`, a timeout of 0 means "don't wait at all" — the opposite of "wait forever." So `Timeout::Max` passed to `wait_until_sent()` would result in no waiting, which is a bug.

This should be documented or handled differently. Consider mapping `Max` to `isize::MAX` (which is `LONG_MAX` = `MAX_SCHEDULE_TIMEOUT` on the C side) and only keeping the special 0 mapping for `write_all` if that's what's intended, or making `Timeout` method-specific.

**3. `unwrap_or_default()` for overflow:**

```rust
+            Self::Jiffies(value) => value.get().try_into().unwrap_or_default(),
```

If the conversion from `Jiffies` (likely `u64`) to `isize` overflows, it silently becomes 0. Combined with `serdev_device_write`'s treatment of 0 as `MAX_SCHEDULE_TIMEOUT`, an overflow would silently become an infinite wait. This is surprising — a saturating conversion to `isize::MAX` would be safer.

**4. Probe callback and `PrivateData` registration:**

```rust
+    extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> kernel::ffi::c_int {
+        let sdev = unsafe { &*sdev.cast::<Device<device::CoreInternal<'_>>>() };
+        ...
+            let private_data = devres::register(
+                sdev.as_ref(),
+                try_pin_init!(PrivateData {
+                    active <- new_mutex!(false),
+                }),
+                GFP_KERNEL,
+            )?;
+            let mut active = private_data.active.lock();
+            ...
+            unsafe {
+                (*sdev.as_raw()).rust_private_data =
+                    (&raw const *private_data).cast::<c_void>().cast_mut()
+            };
```

The `PrivateData` is registered via `devres::register`, so it will be cleaned up when the device is unregistered. The pointer is stored in `rust_private_data` after registration. The mutex is locked before `set_drvdata` to synchronize with `receive_buf_callback`, which is good.

However, the sequencing is important: `serdev_device_set_client_ops` is called *after* storing `rust_private_data`, so there's no race window where `receive_buf` could fire with `rust_private_data` unset. Good.

**5. `remove_callback` synchronization with `receive_buf`:**

```rust
+        let private_data = unsafe { &*(*sdev.as_raw()).rust_private_data.cast::<PrivateData>() };
+        *private_data.active.lock() = false;
+        T::unbind(sdev, data);
```

The `active` flag is set to `false` under the mutex before calling `unbind`. In `receive_buf_callback`, the mutex is held while checking `active` and calling `T::receive`. This ensures `receive` won't be called after `remove_callback` starts. However, there's a subtlety: `drvdata_borrow` is called *before* the `active` check in `remove_callback`, but *after* it in `receive_buf_callback`. In `remove_callback`, `drvdata_borrow` is called before setting `active = false` — this is fine since the driver data hasn't been dropped yet. The ordering looks correct.

**6. `write_all` return value handling:**

```rust
+    pub fn write_all(&self, data: &[u8], timeout: Timeout) -> Result<usize> {
+        let ret = unsafe {
+            bindings::serdev_device_write(...)
+        };
+        to_result(ret as i32).map(|()| ret.unsigned_abs())
+    }
```

`serdev_device_write` returns `ssize_t`. Casting to `i32` could truncate on platforms where `ssize_t` is 64-bit. If a write succeeds with more than `i32::MAX` bytes (theoretically), the cast could produce a negative value and falsely trigger an error. In practice serdev writes won't be that large, but the cast is technically lossy. The `ret.unsigned_abs()` on the return is also on the original `ssize_t`, which correctly preserves the full value. The issue is only in the error check path.

**7. `write` return type is `Result<u32>`:**

```rust
+    pub fn write(&self, data: &[u8]) -> Result<u32> {
```

The C function `serdev_device_write_buf` returns `int`. Returning `u32` for the count is fine since successful values are non-negative, but the same `ret as i32` cast concern applies since `ret` comes from an `int` which is already `i32`-sized in practice on Linux, so this one is actually fine.

**8. Missing `[ ` bracket in doc comment:**

```rust
+    /// Note that any accepted data has only been buffered by the controller. Use
+    /// [ Device::wait_until_sent`] to make sure the controller write buffer has actually been
```

There's a broken rustdoc link: `[ Device::wait_until_sent`]` should be `[`Device::wait_until_sent`]` (missing backtick after `[`).

**9. `set_baudrate` returns `Result<(), u32>`:**

```rust
+    pub fn set_baudrate(&self, speed: u32) -> Result<(), u32> {
```

This is unusual — most Rust-for-Linux abstractions use `Result` (i.e., `Result<T, Error>`). Returning `Err(actual_baudrate)` when the requested baudrate isn't exactly matched is potentially confusing, as the C `serdev_device_set_baudrate` returns the actual baudrate set (which might be close but not exact). The sample driver treats any mismatch as `EINVAL`. Consider whether a baudrate mismatch (e.g., requesting 115200 and getting 115201) should really be an error or just a warning. This is arguably a design choice that should be documented.

**10. `set_baudrate` and other config methods on `Device<Bound>` only:**

All the configuration methods are on `impl Device<device::Bound>`, but in the sample driver, `probe` receives `&'bound serdev::Device<Core<'_>>`. The sample calls `sdev.set_baudrate(...)` directly, which means `Device<Core>` must deref to `Device<Bound>` (via `impl_device_context_deref!`). This works, but it's worth noting that `Core` impls `Bound` methods through deref.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Claude review: samples: rust: add Rust serial device bus sample device driver
  2026-05-30 19:25 ` [PATCH v10 4/5] samples: rust: add Rust serial device bus sample device driver Markus Probst
@ 2026-06-04  5:18   ` Claude Code Review Bot
  0 siblings, 0 replies; 19+ messages in thread
From: Claude Code Review Bot @ 2026-06-04  5:18 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

The sample is clean and demonstrates the abstraction well.

```rust
+    fn receive<'bound>(
+        sdev: &'bound serdev::Device<Bound>,
+        _this: Pin<&Self>,
+        data: &[u8],
+    ) -> usize {
+        let _ = sdev.write_all(data, serdev::Timeout::Max);
+        data.len()
+    }
```

The echo-back in `receive` silently discards write errors with `let _ =`. For a sample this is acceptable, but it might be worth a brief comment or `dev_warn!` for educational purposes.

The `set_baudrate` error handling:

```rust
+        if sdev
+            .set_baudrate(...)
+            .is_err()
+        {
+            return Err(EINVAL);
+        }
```

This discards the actual baudrate returned in the `Err` variant. A more idiomatic approach would be to use `map_err` or log the actual baudrate.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Claude review: MAINTAINERS: serdev: Add self for serdev
  2026-05-30 19:25 ` [PATCH v10 5/5] MAINTAINERS: serdev: Add self for serdev Markus Probst
@ 2026-06-04  5:18   ` Claude Code Review Bot
  0 siblings, 0 replies; 19+ messages in thread
From: Claude Code Review Bot @ 2026-06-04  5:18 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

```diff
-M:	Rob Herring <robh@kernel.org>
+M:	Markus Probst <markus.probst@posteo.de>
```

This replaces Rob as maintainer entirely rather than adding Markus as co-maintainer. The commit message references Rob's message about needing someone else to maintain serdev. This seems like a significant ownership change that Rob should explicitly Ack.

---

**Summary of key issues:**

1. **Bug (Medium):** `Timeout::Max` maps to 0 which is incorrect for `wait_until_sent` — it would mean "don't wait" instead of "wait forever".
2. **Typo:** "partiy" in `Parity::Even` doc comment.
3. **Broken doc link:** `[ Device::wait_until_sent`]` in `write` method docs.
4. **Design question:** `unwrap_or_default()` silently converting overflow to 0 (infinite timeout) in `Timeout::into_jiffies`.
5. **Minor:** `ssize_t` to `i32` truncation in `write_all` error check path.
6. **Process:** MAINTAINERS change replacing Rob entirely should have his explicit Ack.

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Claude review: rust: devres: return reference in `devres::register`
  2026-05-30  1:13 ` [PATCH v8 1/5] rust: devres: return reference in `devres::register` Markus Probst via B4 Relay
@ 2026-06-04  5:57   ` Claude Code Review Bot
  0 siblings, 0 replies; 19+ messages in thread
From: Claude Code Review Bot @ 2026-06-04  5:57 UTC (permalink / raw)
  To: dri-devel-reviews

Patch Review

This changes `devres::register` from returning `Result` (unit) to `Result<&'a T>`, allowing callers to obtain a reference to the devres-managed data. The lifetime `'a` is tied to the `&'a Device<Bound>` borrow, which ensures the reference can't outlive the bound state (during which devres won't fire).

The safety argument is sound: `data_ptr` is obtained from the `Pin<KBox<T>>` heap allocation before the `KBox` is moved into `register_foreign`. The move only transfers ownership of the heap pointer — the allocation itself doesn't move, so `data_ptr` remains valid. After registration, devres owns the allocation and keeps it alive until unbind.

```rust
+    let data_ptr = &raw const *data;
+
     register_foreign(dev, data)
+        // SAFETY: `dev` is valid for the lifetime of 'a. As long as there is a reference to
+        // `Device<Bound>`, it is guaranteed that the device is not unbound and data has not been
+        // dropped. Thus `data_ptr` is also valid for the lifetime of 'a.
+        .map(|()| unsafe { &*data_ptr })
```

The existing callers (`cpufreq.rs`, `drm/driver.rs`) are updated to discard the new return value with `?; Ok(())`, preserving their existing behavior. Clean mechanical adaptation.

Reviewed-by quality: **Good.** Already has Viresh Kumar's Ack.

---

---
Generated by Claude Code Patch Reviewer

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2026-06-04  5:57 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-30 19:25 [PATCH v10 0/5] rust: add basic serial device bus abstractions Markus Probst
2026-05-30 19:25 ` [PATCH v10 1/5] rust: devres: return reference in `devres::register` Markus Probst
2026-06-04  5:18   ` Claude review: " Claude Code Review Bot
2026-05-30 19:25 ` [PATCH v10 2/5] serdev: add rust private data to serdev_device Markus Probst
2026-06-04  5:18   ` Claude review: " Claude Code Review Bot
2026-05-30 19:25 ` [PATCH v10 3/5] rust: add basic serial device bus abstractions Markus Probst
2026-06-04  5:18   ` Claude review: " Claude Code Review Bot
2026-05-30 19:25 ` [PATCH v10 4/5] samples: rust: add Rust serial device bus sample device driver Markus Probst
2026-06-04  5:18   ` Claude review: " Claude Code Review Bot
2026-05-30 19:25 ` [PATCH v10 5/5] MAINTAINERS: serdev: Add self for serdev Markus Probst
2026-06-04  5:18   ` Claude review: " Claude Code Review Bot
2026-06-04  5:18 ` Claude review: rust: add basic serial device bus abstractions Claude Code Review Bot
  -- strict thread matches above, loose matches on Subject: below --
2026-05-30  1:13 [PATCH v8 0/5] " Markus Probst via B4 Relay
2026-05-30  1:13 ` [PATCH v8 1/5] rust: devres: return reference in `devres::register` Markus Probst via B4 Relay
2026-06-04  5:57   ` Claude review: " Claude Code Review Bot
2026-04-29 18:21 [PATCH v7 0/4] rust: add basic serial device bus abstractions Markus Probst via B4 Relay
2026-04-29 18:21 ` [PATCH v7 1/4] rust: devres: return reference in `devres::register` Markus Probst via B4 Relay
2026-05-05  1:18   ` Claude review: " Claude Code Review Bot
2026-04-27 18:05 [PATCH v6 0/4] rust: add basic serial device bus abstractions Markus Probst via B4 Relay
2026-04-27 18:05 ` [PATCH v6 1/4] rust: devres: return reference in `devres::register` Markus Probst via B4 Relay
2026-04-28  4:14   ` Claude review: " Claude Code Review Bot
2026-04-20 20:07 [PATCH v5 0/4] rust: add basic serial device bus abstractions Markus Probst
2026-04-20 20:07 ` [PATCH v5 1/4] rust: devres: return reference in `devres::register` Markus Probst
2026-04-22 23:21   ` Claude review: " Claude Code Review Bot
2026-04-11 15:10 [PATCH v4 0/4] rust: add basic serial device bus abstractions Markus Probst via B4 Relay
2026-04-11 15:10 ` [PATCH v4 1/4] rust: devres: return reference in `devres::register` Markus Probst via B4 Relay
2026-04-11 22:55   ` Claude review: " Claude Code Review Bot
2026-03-13 18:12 [PATCH v3 0/4] rust: add basic serial device bus abstractions Markus Probst
2026-03-13 18:12 ` [PATCH v3 1/4] rust: devres: return reference in `devres::register` Markus Probst
2026-03-16  2:17   ` Claude review: " Claude Code Review Bot
2026-03-06 19:35 [PATCH v2 0/4] rust: add basic serial device bus abstractions Markus Probst
2026-03-06 19:35 ` [PATCH v2 1/4] rust: devres: return reference in `devres::register` Markus Probst
2026-03-08 22:21   ` Claude review: " Claude Code Review Bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox