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: drm: gem: Introduce shmem::Object::sg_table() Date: Thu, 04 Jun 2026 11:29:39 +1000 Message-ID: In-Reply-To: <20260603195210.693856-7-lyude@redhat.com> References: <20260603195210.693856-1-lyude@redhat.com> <20260603195210.693856-7-lyude@redhat.com> X-Mailer: Claude Code Patch Reviewer Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit MIME-Version: 1.0 Patch Review **Verdict: Has concerns that need resolution.** This is the most complex patch. The overall design (Devres-managed SGTableMap with double-checked locking) is sound, but there are issues. **Critical: `__drm_gem_shmem_free_sgt_locked()` does not exist in the current kernel tree.** ```rust impl Drop for SGTableMap { fn drop(&mut self) { let obj = unsafe { self.obj.as_ref() }; let _lock = DmaResvGuard::new(obj); unsafe { bindings::__drm_gem_shmem_free_sgt_locked(obj.as_raw_shmem()) }; } } ``` I searched the drm-next tree and found no declaration or definition of this function. It must come from a prerequisite patch on drm-rust-next. This function is critical for correctness -- it must NULL out `shmem->sgt` after cleanup, otherwise `drm_gem_shmem_release()` will double-free the sg_table when it runs (it checks `if (shmem->sgt)` and cleans up). The ordering in `free_callback()` is: 1. `sgt_res.reset()` -- drops SGTableMap, calls `__drm_gem_shmem_free_sgt_locked()` 2. `drm_gem_shmem_release()` -- would double-free if `shmem->sgt` wasn't NULLed This is a known concern (the V13 changelog mentions "Fix double-free in free_callback()"), but reviewers should verify `__drm_gem_shmem_free_sgt_locked()` NULLs `shmem->sgt`. **The double-checked locking pattern is correct:** ```rust pub fn sg_table<'a>(&'a self, dev: &'a device::Device) -> Result<&'a scatterlist::SGTable> { // Fast path if let Some(sgt_res) = self.sgt_res.as_ref() { break 'out sgt_res; } // Slow path with lock let _guard = self.sgt_lock.lock(); if let Some(sgt_res) = self.sgt_res.as_ref() { break 'out sgt_res; } self.sgt_res.populate(Devres::new(dev, SGTableMap::new(self))?); unsafe { self.sgt_res.as_ref().unwrap_unchecked() } ``` The fast path uses `SetOnce::as_ref()` with Acquire ordering, the slow path is guarded by the mutex, and after populate the value is immediately available. This is sound. **Device identity check is good defensive programming:** ```rust if dev.as_raw() != self.dev().as_ref().as_raw() { return Err(EINVAL); } ``` This prevents creating an SGTable bound to the wrong device, which the test `fail_sg_table_on_wrong_dev` validates. **SGTableMap self-referential pointer:** `SGTableMap` stores `NonNull>` pointing back to its own containing Object. This is safe because: - `sgt_res` is explicitly reset in `free_callback()` before `KBox::from_raw(this)` drops the Object - The Devres revocation on driver-unbind drops the SGTableMap while the Object (refcounted) is still alive **The `SGTableMap::new()` return type is interesting:** ```rust fn new(obj: &Object) -> impl Init { from_err_ptr(unsafe { bindings::drm_gem_shmem_get_pages_sgt(obj.as_raw_shmem()) })?; Ok(Self { obj: obj.into() }) } ``` This calls `drm_gem_shmem_get_pages_sgt()` which internally acquires/releases the dma_resv lock and initializes `shmem->sgt`. The returned pointer is discarded -- only the side effect (populating the sgt field) matters. This is correct but could use a brief comment explaining why the returned pointer isn't stored. **Minor: Send/Sync impls:** ```rust unsafe impl Send for SGTableMap {} unsafe impl Sync for SGTableMap {} ``` The safety justification is that the NonNull points to a GEM object which is inherently thread-safe (refcounted, lock-protected). This is reasonable. The `DriverObject` bound already requires `Send + Sync` for the driver data. **Test coverage:** The test only covers the failure path (wrong device). The TODO acknowledging the need for dummy dma_ops for success testing is honest and reasonable. --- Generated by Claude Code Patch Reviewer