On 9/11/26 20:38, Mike Lothian wrote:
> amdgpu_dma_buf_map() adds VRAM to the allowed domains for a peer2peer
> attachment. GTT is only a fallback placement when VRAM is preferred, so
> ttm_bo_validate() migrates the buffer from GTT into VRAM. While the
> exporting device is runtime suspended its SDMA rings are down and the
> move fails:
>
> amdgpu: Move buffer fallback to memcpy unavailable
>
> An importer on a second GPU reaches this holding no runtime PM
> reference on the exporter, e.g. a compositor on the APU submitting a
> frame that references a buffer exported by an idle dGPU:
>
> amdgpu_cs_ioctl -> amdgpu_cs_parser_bos -> amdgpu_cs_bo_validate
> -> ttm_bo_validate -> amdgpu_bo_move -> dma_buf_map_attachment
> -> amdgpu_dma_buf_map -> ttm_bo_validate -> amdgpu_bo_move
>
> Pinning a dma-buf into VRAM has the same requirement, which
> commit 030631e97b20 ("drm/amdgpu: revert "take runtime pm reference
> when we attach a buffer" v2") called out as the one case that would
> need the reference back.
>
> Take it in attach and drop it in detach. pm_runtime_get_if_active()
> never resumes the device, so it cannot deadlock against the reservation
> taken during resume, which is why the old pm_runtime_get_sync() had to
> go. If the device is not active, clear peer2peer instead: the buffer
> then stays in GTT, which remains accessible while the GPU is powered
> down.
>
> Fixes: 030631e97b20 ("drm/amdgpu: revert "take runtime pm reference when we
> attach a buffer" v2")
> Cc: [email protected]
> Suggested-by: Christian König <[email protected]>
> Signed-off-by: Mike Lothian <[email protected]>
> Assisted-by: Claude:Opus-5 [Claude Code]
Reviewed-by: Christian König <[email protected]>
> ---
>
> v3: take the reference in attach and drop it in detach, clearing
> peer2peer when the device is not active, as suggested by Christian.
> v2 only covered amdgpu_dma_buf_map() and left VRAM pinning exposed.
> v2: use pm_runtime_get_if_active() instead of testing
> adev->mman.buffer_funcs_enabled, which was racy against a
> concurrent suspend. Reported by Sashiko AI review.
>
> Reproduced on a HawkPoint APU [1002:1900] driving the display with a
> Navi 48 [Radeon AI PRO R9700] [1002:7551] on oculink for render
> offload. Without the patch kwin_wayland hits the call chain above
> within a minute of the dGPU autosuspending and the desktop stops
> repainting until it resumes.
>
> Tested with v3: Chromium rendering on the dGPU and composited by kwin
> 6.7.5 for five minutes, then closed. The dGPU stayed active while the
> window was on screen and suspended six seconds after Chromium exited,
> with no fallback errors or runtime PM usage count underflows. After
> an hour of yuzu render offload the dGPU also suspended once yuzu
> exited.
>
> drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c | 39 ++++++++++++++++++++-
> 1 file changed, 38 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c
> b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c
> index b33c300e26e2..fae695c3e531 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c
> @@ -43,6 +43,7 @@
> #include <linux/dma-buf.h>
> #include <linux/dma-fence-array.h>
> #include <linux/pci-p2pdma.h>
> +#include <linux/pm_runtime.h>
>
> static const struct dma_buf_attach_ops amdgpu_dma_buf_attach_ops;
>
> @@ -100,15 +101,50 @@ static int amdgpu_dma_buf_attach(struct dma_buf *dmabuf,
> pci_p2pdma_distance(adev->pdev, attach->dev, false) < 0)
> attach->peer2peer = false;
>
> + /*
> + * P2P access needs the exporter awake for the lifetime of the
> + * attachment. pm_runtime_get_if_active() never resumes the device,
> + * so it cannot deadlock against the reservation taken during resume.
> + * A negative return means runtime PM is disabled and the device
> + * cannot suspend, in which case the put in detach is a no-op.
> + */
> + if (attach->peer2peer &&
> + !pm_runtime_get_if_active(adev_to_drm(adev)->dev))
> + attach->peer2peer = false;
> +
> r = dma_resv_lock(bo->tbo.base.resv, NULL);
> if (r)
> - return r;
> + goto err_pm_put;
>
> amdgpu_vm_bo_update_shared(bo);
>
> dma_resv_unlock(bo->tbo.base.resv);
>
> return 0;
> +
> +err_pm_put:
> + if (attach->peer2peer)
> + pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
> + return r;
> +}
> +
> +/**
> + * amdgpu_dma_buf_detach - &dma_buf_ops.detach implementation
> + *
> + * @dmabuf: DMA-buf where we remove the attachment from
> + * @attach: the attachment to remove
> + *
> + * Drop the runtime PM reference taken in amdgpu_dma_buf_attach().
> + */
> +static void amdgpu_dma_buf_detach(struct dma_buf *dmabuf,
> + struct dma_buf_attachment *attach)
> +{
> + struct drm_gem_object *obj = dmabuf->priv;
> + struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj);
> + struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
> +
> + if (attach->peer2peer)
> + pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
> }
>
> /**
> @@ -350,6 +386,7 @@ static void amdgpu_dma_buf_vunmap(struct dma_buf
> *dma_buf, struct iosys_map *map
>
> const struct dma_buf_ops amdgpu_dmabuf_ops = {
> .attach = amdgpu_dma_buf_attach,
> + .detach = amdgpu_dma_buf_detach,
> .pin = amdgpu_dma_buf_pin,
> .unpin = amdgpu_dma_buf_unpin,
> .map_dma_buf = amdgpu_dma_buf_map,