public inbox for drm-ai-reviews@public-inbox.freedesktop.org
 help / color / mirror / Atom feed
From: Claude Code Review Bot <claude-review@example.com>
To: dri-devel-reviews@example.com
Subject: Claude review: accel/amdxdna: Add carveout memory support for non-IOMMU systems
Date: Tue, 28 Apr 2026 14:22:37 +1000	[thread overview]
Message-ID: <review-patch1-20260427170949.2666601-1-lizhi.hou@amd.com> (raw)
In-Reply-To: <20260427170949.2666601-1-lizhi.hou@amd.com>

Patch Review

#### amdxdna_cbuf.c — Carveout buffer allocator

**`kzalloc_objs` usage for sgl allocation:**
```c
sgl = kzalloc_objs(*sg, n_entries);
```
The variable is named `sgl` but the macro dereferences `*sg` — this works because `sg` has the same type (`struct scatterlist`), but it's confusing. Should be `kzalloc_objs(*sgl, n_entries)` for clarity.

**`amdxdna_cbuf_map` manually builds sg_table:**
```c
sgt = kzalloc_obj(*sgt);
...
sgl = kzalloc_objs(*sg, n_entries);
...
sg_init_table(sgl, n_entries);
sgt->orig_nents = n_entries;
sgt->nents = n_entries;
sgt->sgl = sgl;
```
This manually allocates and wires up the sg_table instead of using `sg_alloc_table`. The manual approach sets `sgt->nents` to `n_entries` which is semantically wrong — `nents` should be the number of DMA-mapped entries (the return from `dma_map_sg`), while `orig_nents` is the pre-mapping count. Since this is filling in DMA addresses directly (via `dma_map_resource`), you should use `sgt->nents` for the DMA-mapped count, which may differ. In practice they're the same here, but it's still conceptually wrong and fragile.

**`amdxdna_cbuf_unmap` uses `drm_prime_get_contiguous_size`:**
```c
dma_unmap_resource(attach->dev, sg_dma_address(sgt->sgl),
		   drm_prime_get_contiguous_size(sgt), direction,
		   DMA_ATTR_SKIP_CPU_SYNC);
```
In `amdxdna_cbuf_map`, the entire buffer is mapped with a single `dma_map_resource` call. But here, `drm_prime_get_contiguous_size` iterates the sg entries to compute a contiguous length. Since the sg entries were filled synthetically and may have been split across max_seg boundaries, this function would scan and compute the right total only if the DMA addresses are contiguous across segments, which they are. This works but is unnecessarily roundabout — just pass `cbuf->node.size` directly (accessible via `attach->dmabuf->priv`).

**`amdxdna_cbuf_unmap` calls `sg_free_table` then `kfree(sgt)`:**
```c
sg_free_table(sgt);
kfree(sgt);
```
But the sgl was allocated with `kzalloc_objs` and manually assigned to `sgt->sgl` without using `sg_alloc_table`. `sg_free_table` will call `kfree(sgt->sgl)` which should be fine since it was `kzalloc`'d, but this asymmetry between manual allocation and `sg_free_table` cleanup is fragile. If `sg_init_table` were ever to do chaining differently, this could break.

**`amdxdna_cbuf_clear` error handling:**
```c
dma_buf_vmap(dbuf, &vmap);
if (!vmap.vaddr)
	return -EFAULT;
```
`dma_buf_vmap` returns an int error code that is ignored here. The code checks `vmap.vaddr` instead. `dma_buf_vmap` can fail and return a negative error while leaving `vmap.vaddr` as-is (NULL from the initializer). Should check the return value:
```c
ret = dma_buf_vmap(dbuf, &vmap);
if (ret)
    return ret;
```

**`amdxdna_get_cbuf` error path after `amdxdna_cbuf_clear` failure:**
```c
ret = amdxdna_cbuf_clear(dbuf);
if (ret) {
	dma_buf_put(dbuf);
	goto out;
}
```
When `amdxdna_cbuf_clear` fails, `dma_buf_put(dbuf)` drops the reference which triggers `amdxdna_cbuf_release`, which calls `drm_mm_remove_node` and `kfree(cbuf)`. Then execution jumps to `out:` which returns `ERR_PTR(ret)`. This is actually correct since the release callback handles cleanup, but the `goto out` skips `remove_node` and `free_cbuf` labels which would double-free. The control flow is correct but non-obvious — a comment would help.

**`pr_err` used instead of `XDNA_ERR`:**
```c
pr_err("Failed to dma_map_resource carveout dma buf, ret %d\n", ret);
```
and
```c
pr_err("Failed to vmap carveout dma buf\n");
```
The rest of the driver consistently uses `XDNA_ERR(xdna, ...)` for error logging. These two `pr_err` calls are inconsistent — they lack the device context that `XDNA_ERR` provides. The cbuf_priv has `cbuf->xdna` available to use.

#### amdxdna_debugfs.c — Debugfs carveout interface

**Security: no validation of physical address range:**
```c
ret = kstrtou64(sep, 0, &addr);
...
ret = amdxdna_carveout_init(xdna, addr, size);
```
The debugfs write handler accepts an arbitrary physical address and size from userspace (root). There's no validation that the address actually corresponds to reserved/carveout memory. A root user could point the driver at arbitrary physical memory (MMIO regions, other devices' memory, etc.). While debugfs is root-only and this is a debug feature, it would be prudent to at minimum validate against `memblock_is_reserved()` or similar.

**`file_to_xdna` macro is fragile:**
```c
#define file_to_xdna(file) (((struct seq_file *)(file)->private_data)->private)
```
This assumes the file was opened via `single_open` and that `private_data` is a `struct seq_file *`. While true for the current usage, this is a sharp macro. Consider using `seq_file_private()` or similar kernel helpers.

#### amdxdna_gem.c — GEM object changes

**Removed import BO skip from `amdxdna_gem_skip_bo_usage`:**
```c
-	/* Do not count imported BOs since the buffer is not allocated by us. */
-	if (is_import_bo(abo))
-		return true;
```
Carveout BOs are self-imported, so removing this check is necessary to count them. But this also means genuinely external imported BOs (from other drivers) will now be counted in `total_bo_usage`. The fdinfo comment update acknowledges this, but it's a user-visible semantic change.

**`amdxdna_gem_obj_open` restructured logic:**
```c
+	if (abo->open_ref > 1)
+		return 0;
+
+	/* Attached to the client when first opened by it. */
+	abo->client = filp->driver_priv;
+
+	/* No need to set up dma addr mapping in PASID mode. */
+	if (!amdxdna_pasid_on(abo->client)) {
+		ret = amdxdna_dma_map_bo(xdna, abo);
```
This now calls `amdxdna_pasid_on(abo->client)` to decide whether to map. But with the change to `amdxdna_drm_open`, SVA bind failure is now a warning rather than an error. If SVA bind fails (no IOMMU at all), `client->pasid` remains `IOMMU_PASID_INVALID`, so `amdxdna_pasid_on()` returns false, and `amdxdna_dma_map_bo` is called. Inside `amdxdna_dma_map_bo`, if it's a carveout-imported BO, `drm_gem_shmem_get_pages_sgt` will be called on it — but carveout BOs are imported via `drm_gem_shmem_prime_import_sg_table` and already have an sgt from the dma_buf attachment. **This will likely fail or behave incorrectly** because `drm_gem_shmem_get_pages_sgt` expects to manage shmem pages, not pre-existing imported sgt.

**`amdxdna_gem_obj_vmap` and `amdxdna_gem_obj_vunmap`:**
```c
+	if (is_import_bo(abo))
+		ret = dma_buf_vmap(abo->dma_buf, map);
+	else
+		ret = drm_gem_shmem_object_vmap(obj, map);
```
These new wrappers dispatch between imported and shmem BOs. This is needed because carveout BOs are imported and need to go through the dma_buf vmap path (which calls `amdxdna_cbuf_vmap` → `memremap`). This is correct.

**Removed `fallthrough` between `AMDXDNA_BO_CMD` and `AMDXDNA_BO_SHARE`:**
```c
 	case AMDXDNA_BO_CMD:
-		fallthrough;
 	case AMDXDNA_BO_SHARE:
```
These two cases still fall through to the same code — removing the explicit `fallthrough` annotation is correct since there's no code between them (it's an empty case label, not a fallthrough). Good cleanup.

#### amdxdna_iommu.c — DMA mapping refactoring

**BO type check changed from `AMDXDNA_BO_SHMEM` to `AMDXDNA_BO_SHARE`:**
```c
-	if (abo->type != AMDXDNA_BO_DEV_HEAP && abo->type != AMDXDNA_BO_SHMEM)
+	if (abo->type != AMDXDNA_BO_DEV_HEAP && abo->type != AMDXDNA_BO_SHARE)
```
I see `AMDXDNA_BO_SHMEM` in the original but the existing tree has this exact same check. This suggests the patch is against a slightly different base. Need to verify that `AMDXDNA_BO_SHARE` is the correct type here — since carveout BOs get `type = AMDXDNA_BO_SHARE` from `amdxdna_gem_prime_import`, this would include them. But the core concern remains: `drm_gem_shmem_get_pages_sgt` is called for all these BOs including imported carveout ones.

**`ssize_t size` variable removed, but `iommu_map_sgtable` returns `ssize_t`:**
The original code had `size_t size` (in the existing tree actually `ssize_t`) and the patch changes the variable name but the check `if (size < 0)` is added for the iommu path. However, looking at the patch diff more carefully:
```c
+	unsigned long contig_sz;
 	struct sg_table *sgt;
 	dma_addr_t dma_addr;
 	struct iova *iova;
 	ssize_t size;
```
The `ssize_t size` variable is kept for the IOMMU path where `iommu_map_sgtable` returns `ssize_t`. The new `unsigned long contig_sz` is added for the non-IOMMU path. This is fine.

**Non-IOVA path calls `drm_prime_get_contiguous_size`:**
```c
+		contig_sz = drm_prime_get_contiguous_size(sgt);
+		if (contig_sz < abo->mem.size) {
```
For carveout BOs, the sgt comes from the dma_buf attachment mapping (`amdxdna_cbuf_map`), which maps the physical address range to DMA addresses. The contiguous size check should pass since carveout memory is physically contiguous. However, this whole path through `drm_gem_shmem_get_pages_sgt` is questionable for imported BOs as noted above.

#### amdxdna_pci_drv.c — Probe and open changes

**SVA bind failure downgraded to warning:**
```c
+		/* No need to fail open since user may use pa + carveout later. */
+		if (amdxdna_sva_init(client))
+			XDNA_WARN(xdna, "PASID not available for pid %d", client->pid);
```
This is the most significant behavioral change. Previously, if SVA bind failed in a non-IOVA configuration, the open would fail entirely. Now it succeeds with a warning. This means clients can open the device even when neither IOMMU nor PASID is available, relying on carveout memory being configured later via debugfs. **The problem is**: if carveout is never configured, the client is in a broken state where buffer allocation will fail in confusing ways. It would be better to check at buffer allocation time whether we're in a valid configuration.

**`mmdrop` before `amdxdna_sva_fini` in cleanup:**
```c
+	mmdrop(client->mm);
+	amdxdna_sva_fini(client);
```
The original code called `iommu_sva_unbind_device` before `mmdrop`. The patch reverses this order. `iommu_sva_unbind_device` may access the mm_struct internally, so calling `mmdrop` first could lead to use-after-free if the mm refcount drops to zero. **This ordering should be reversed** — unbind SVA first, then drop the mm reference.

**`amdxdna_xdna_drm_release` cleanup action:**
```c
+static void amdxdna_xdna_drm_release(struct drm_device *drm, void *res)
+{
+	struct amdxdna_dev *xdna = res;
+	amdxdna_carveout_fini(xdna);
+}
```
Using `drmm_add_action` to clean up carveout on DRM device release is appropriate. However, `drmm_add_action` is called early in probe, before carveout is initialized. `amdxdna_carveout_fini` handles the NULL case (`if (!amdxdna_use_carveout(xdna)) return;`), so this is safe.

**`amdxdna_debugfs_init` called after `drm_dev_register`:**
```c
+	amdxdna_debugfs_init(xdna);
 	return 0;
```
Called after `drm_dev_register` which is correct — the debugfs root is created during registration. But there's no cleanup on the error paths above, and `amdxdna_debugfs_init` doesn't return errors (void return). This is fine since debugfs files are cleaned up automatically by DRM framework.

#### Summary of Required Fixes

1. **Critical**: Reverse `mmdrop`/`amdxdna_sva_fini` ordering in `amdxdna_client_cleanup` to avoid potential use-after-free.
2. **Important**: Verify that `drm_gem_shmem_get_pages_sgt` works correctly on carveout BOs that are self-imported via `drm_gem_shmem_prime_import_sg_table`. If not, `amdxdna_dma_map_bo` needs a different path for imported BOs.
3. **Important**: Check return value of `dma_buf_vmap` in `amdxdna_cbuf_clear` instead of just checking `vmap.vaddr`.
4. **Minor**: Use `XDNA_ERR` instead of `pr_err` in cbuf map/vmap callbacks.
5. **Minor**: Fix `kzalloc_objs(*sg, ...)` to `kzalloc_objs(*sgl, ...)`.
6. **Consider**: Add physical address range validation in the debugfs write handler.

---
Generated by Claude Code Patch Reviewer

      parent reply	other threads:[~2026-04-28  4:22 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-27 17:09 [PATCH V2] accel/amdxdna: Add carveout memory support for non-IOMMU systems Lizhi Hou
2026-04-28  4:22 ` Claude review: " Claude Code Review Bot
2026-04-28  4:22 ` Claude Code Review Bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=review-patch1-20260427170949.2666601-1-lizhi.hou@amd.com \
    --to=claude-review@example.com \
    --cc=dri-devel-reviews@example.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox