From mboxrd@z Thu Jan 1 00:00:00 1970 From: Claude Code Review Bot To: dri-devel-reviews@example.com Subject: Claude review: rust: add basic serial device bus abstractions Date: Mon, 16 Mar 2026 12:17:35 +1000 Message-ID: In-Reply-To: <20260313-rust_serdev-v3-3-c9a3af214f7f@posteo.de> References: <20260313-rust_serdev-v3-0-c9a3af214f7f@posteo.de> <20260313-rust_serdev-v3-3-c9a3af214f7f@posteo.de> X-Mailer: Claude Code Patch Reviewer Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Patch Review This is the main patch. Several observations: **1. Timeout::Max maps to 0, which is wrong:** ```rust +impl Timeout { + fn into_jiffies(self) -> isize { + match self { + ... + Self::Max =3D> 0, + } + } +} ``` `MAX_SCHEDULE_TIMEOUT` is `LONG_MAX` (i.e., `c_long::MAX`), not 0. A timeou= t of 0 means "don't wait at all" in `serdev_device_write()`. The `Timeout::= Max` variant's doc says "Wait as long as possible" and references `MAX_SCHE= DULE_TIMEOUT`, but then passes 0, which is the exact opposite. This should = be: ```rust Self::Max =3D> isize::MAX, // or kernel::task::MAX_SCHEDULE_TIMEOUT ``` **2. Race condition in probe/receive synchronization:** The `PrivateData` struct uses a `Completion` to synchronize `receive_buf_ca= llback` with probe completion: ```rust + private_data.probe_complete.wait_for_completion(); + + // SAFETY: No one has exclusive access to `private_data.error`. + if unsafe { *private_data.error.get() } { + return length; + } ``` The `error` field uses `UnsafeCell` without any synchronization primi= tive. The SAFETY comment "No one has exclusive access to `private_data.erro= r`" doesn't actually justify the safety =E2=80=94 it's the *opposite* of wh= at you'd want. The concern is whether the write to `error` in probe is guar= anteed to be visible in `receive_buf_callback` on another thread. The `Comp= letion` itself should provide a memory barrier (via `complete_all`/`wait_fo= r_completion`), so the ordering is likely correct in practice, but this sho= uld be documented explicitly. Using an `AtomicBool` would be cleaner and av= oid the need for `unsafe` altogether. **3. `set_baudrate` returns `Result<(), u32>` instead of idiomatic kernel `= Result`:** ```rust + pub fn set_baudrate(&self, speed: u32) -> Result<(), u32> { ``` Returning the actual baudrate set on failure is useful information, but the= `Result<(), u32>` type is non-standard for kernel Rust code. The C functio= n `serdev_device_set_baudrate` returns the actual speed set (which may diff= er from requested). The sample driver checks `is_err()` and returns `EINVAL= `, losing the actual value. Consider either returning `Result` (the ac= tual speed set) or `Result` with a standard error code. **4. `write` returns `Result` but `write_all` returns `Result` = =E2=80=94 inconsistent types:** ```rust + pub fn write_all(&self, data: &[u8], timeout: Timeout) -> Result { ... + pub fn write(&self, data: &[u8]) -> Result { ``` Both should consistently use `usize` for byte counts. **5. The `write` function wraps `serdev_device_write_buf` which returns `ss= ize_t`:** ```rust + let ret =3D + unsafe { bindings::serdev_device_write_buf(self.as_raw(), data= .as_ptr(), data.len()) }; + to_result(ret as i32).map(|()| ret.unsigned_abs()) ``` Casting `ssize_t` (which is `isize`/`i64` on 64-bit) to `i32` could truncat= e positive values. Since this returns the number of bytes written, if `data= .len()` > `i32::MAX`, the cast would produce an incorrect error. The same i= ssue exists in `write_all`. Use `ret as i32` carefully =E2=80=94 `to_result= ` expects a C `int`, but the value range should be OK in practice since wri= te buffers won't exceed `i32::MAX`. **6. `set_baudrate`, `set_flow_control`, `set_parity`, `write`, `write_all`= , `write_flush`, `wait_until_sent` are all on `Device`, but probe re= ceives `Device`:** Looking at the sample driver, `sdev.set_baudrate(...)` is called in `probe`= which receives `&Device`. This compiles because of `impl_device_cont= ext_deref` which lets `Device` deref to `Device`. This is the = standard pattern, so it's fine. **7. Typo in doc comments:** ```rust + /// Even partiy. ``` Should be "parity". ```rust + /// [ Device::wait_until_sent`] ``` Missing `[` =E2=80=94 should be `[`Device::wait_until_sent`]`. This appears= twice. **8. The `Kconfig` entry `RUST_SERIAL_DEV_BUS_ABSTRACTIONS` should probably= be inside the `if SERIAL_DEV_BUS` block:** ```diff +config RUST_SERIAL_DEV_BUS_ABSTRACTIONS + bool "Rust Serial device bus abstractions" + depends on RUST + select SERIAL_DEV_BUS ``` It uses `select SERIAL_DEV_BUS` rather than `depends on`, which means enabl= ing the Rust abstractions will force-enable `SERIAL_DEV_BUS`. This is place= d *before* `if SERIAL_DEV_BUS`, which is a bit odd. Consider using `depends= on SERIAL_DEV_BUS` instead. **9. `remove_callback` doesn't call `drvdata_obtain` to free the driver dat= a:** ```rust + extern "C" fn remove_callback(sdev: *mut bindings::serdev_device) { + let sdev =3D unsafe { &*sdev.cast::>(= ) }; + let data =3D unsafe { sdev.as_ref().drvdata_borrow::() }; + T::unbind(sdev, data); + } ``` It borrows but never takes ownership of the drvdata. The data appears to be= freed by devres when the device is released, since `set_drvdata` stores it= via `dev_set_drvdata`. But wait =E2=80=94 `set_drvdata` stores data via `i= nto_foreign()` and there's no corresponding `from_foreign()` call. The data= should be cleaned up by the devres system if `set_drvdata` registers a dev= res action, but looking at the `device.rs` code, `set_drvdata` does *not* r= egister a devres =E2=80=94 it just calls `dev_set_drvdata`. This looks like= a **memory leak** =E2=80=94 the `Pin>` stored via `set_drvdata` is= never freed. Other bus drivers (platform, etc.) likely call `drvdata_obtai= n` in their remove callbacks. This needs to be verified. --- Generated by Claude Code Patch Reviewer