When a GPU shader hits an exception, the hardware jumps to the
first-level trap handler. The first-level handler (managed by the kernel
via CWSR) checks the TMA buffer for a second-level handler address and
forwards the trap to userspace if one is installed.

KFD already supports this for compute workloads. Render-node user queues
had no equivalent mechanism. Add it via the VM ioctl.

Add two new VM ioctl operations:
  AMDGPU_VM_OP_SET_L2_TRAP   (op = 3)
  AMDGPU_VM_OP_CLEAR_L2_TRAP (op = 4)

Extend drm_amdgpu_vm_in with a 32-byte union for op-specific data.  The
l2trap member carries the GPU virtual addresses and sizes of the TBA
(handler code) and TMA (handler scratch memory). Existing ops only use
the first 8 bytes (op + flags); the union is zero-extended for those ops
by the DRM framework, so existing userspace is unaffected.

Both TBA and TMA buffers MUST be allocated with the
AMDGPU_GEM_CREATE_VM_ALWAYS_VALID flag. The GPU jumps to TBA
automatically on a hardware exception without any software job
submission, so the kernel cannot track TBA/TMA in a job's used-buffer
list. Without VM_ALWAYS_VALID the BO can be evicted and the page table
entry becomes stale, causing GPU page faults or corruption.

Explicit padding is used at every natural alignment boundary so the
layout is identical for native and 32-bit compat userspace.

The TRAP ioctl and its associated structures are removed entirely; the
ioctl number was never part of a released kernel.

v2:
  - Document VM_ALWAYS_VALID requirement in kdoc. The GPU jumps to TBA
    on hardware exception without any software submission so the kernel
    cannot track BOs in a job's used-buffer list. Without this flag,
    eviction leaves a stale page table entry (Natalie Vock).

Cc: Christian König <[email protected]>
Cc: Alex Deucher <[email protected]>
Cc: Felix Kuehling <[email protected]>
Cc: James Zhu <[email protected]>
Cc: Lijo Lazar <[email protected]>
Cc: Lancelot Six <[email protected]>
Cc: Natalie Vock <[email protected]>
Cc: Pierre-Eric Pelloux-Prayer <[email protected]>
Signed-off-by: Srinivasan Shanmugam <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c  |   1 -
 drivers/gpu/drm/amd/amdgpu/amdgpu_trap.c | 105 +----------------------
 drivers/gpu/drm/amd/amdgpu/amdgpu_trap.h |   2 -
 include/uapi/drm/amdgpu_drm.h            |  98 ++++++++++++++++-----
 4 files changed, 77 insertions(+), 129 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index a93ee106ab92..6473faf18d71 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -3079,7 +3079,6 @@ const struct drm_ioctl_desc amdgpu_ioctls_kms[] = {
        DRM_IOCTL_DEF_DRV(AMDGPU_SCHED, amdgpu_sched_ioctl, DRM_MASTER),
        DRM_IOCTL_DEF_DRV(AMDGPU_BO_LIST, amdgpu_bo_list_ioctl, 
DRM_AUTH|DRM_RENDER_ALLOW),
        DRM_IOCTL_DEF_DRV(AMDGPU_FENCE_TO_HANDLE, 
amdgpu_cs_fence_to_handle_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
-       DRM_IOCTL_DEF_DRV(AMDGPU_TRAP, amdgpu_trap_ioctl, DRM_AUTH | 
DRM_RENDER_ALLOW),
        /* KMS */
        DRM_IOCTL_DEF_DRV(AMDGPU_GEM_MMAP, amdgpu_gem_mmap_ioctl, 
DRM_AUTH|DRM_RENDER_ALLOW),
        DRM_IOCTL_DEF_DRV(AMDGPU_GEM_WAIT_IDLE, amdgpu_gem_wait_idle_ioctl, 
DRM_AUTH|DRM_RENDER_ALLOW),
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_trap.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_trap.c
index 31f653ec3fb1..804b4b4f65bb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_trap.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_trap.c
@@ -505,109 +505,6 @@ void amdgpu_trap_free(struct amdgpu_device *adev, struct 
amdgpu_vm *vm,
        *trap_obj = NULL;
 }
 
-static int amdgpu_trap_validate_user_addr(struct amdgpu_device *adev,
-                                         struct amdgpu_vm *vm,
-                                         struct amdgpu_trap_usr_addr *usr_addr)
-{
-       struct amdgpu_bo_va_mapping *va_map;
-       uint64_t addr;
-       uint32_t size;
-       int r;
-
-       addr = (usr_addr->addr & AMDGPU_GMC_HOLE_MASK) >> AMDGPU_GPU_PAGE_SHIFT;
-       size = usr_addr->size >> AMDGPU_GPU_PAGE_SHIFT;
-
-       r = amdgpu_bo_reserve(vm->root.bo, false);
-       if (r)
-               return r;
-
-       va_map = amdgpu_vm_bo_lookup_mapping(vm, addr);
-       if (!va_map) {
-               r = -EINVAL;
-               goto err;
-       }
-       /* validate whether resident in the VM mapping range */
-       if (addr >= va_map->start && va_map->last - addr + 1 >= size) {
-               amdgpu_bo_unreserve(vm->root.bo);
-               return 0;
-       }
-
-       r = -EINVAL;
-err:
-       amdgpu_bo_unreserve(vm->root.bo);
-
-       return r;
-}
-
-static int amdgpu_trap_set_l2_trap_handler(struct amdgpu_device *adev, struct 
amdgpu_vm *vm,
-                                          struct amdgpu_trap_obj *cwsr_obj,
-                                          struct amdgpu_trap_usr_addr *tma,
-                                          struct amdgpu_trap_usr_addr *tba)
-{
-       uint64_t *l1tma;
-       int r;
-
-       if (!amdgpu_trap_is_enabled(adev))
-               return -EOPNOTSUPP;
-
-       if (!cwsr_obj || !cwsr_obj->tma_cpu_addr || !tma || !tba)
-               return -EINVAL;
-       r = amdgpu_trap_validate_user_addr(adev, vm, tma);
-       if (r)
-               return r;
-       r = amdgpu_trap_validate_user_addr(adev, vm, tba);
-       if (r)
-               return r;
-
-       l1tma = (uint64_t *)(cwsr_obj->tma_cpu_addr);
-       l1tma[0] = tma->addr;
-       l1tma[1] = tba->addr;
-
-       return 0;
-}
-
-/*
- * Userspace trap handler related ioctl
- */
-/**
- * amdgpu_trap_ioctl - Handle trap handler specific requests.
- *
- * @dev: drm device pointer
- * @data: request object
- * @filp: drm filp
- *
- * This function is used to perform cwsr and trap handler related operations
- * Returns 0 on success, error code on failure.
- */
-int amdgpu_trap_ioctl(struct drm_device *dev, void *data, struct drm_file 
*filp)
-{
-       struct amdgpu_device *adev = drm_to_adev(dev);
-       union drm_amdgpu_trap *cwsr = data;
-       struct amdgpu_fpriv *fpriv;
-       int r;
-
-       fpriv = (struct amdgpu_fpriv *)filp->driver_priv;
-
-       if (!fpriv->trap_obj)
-               return -EOPNOTSUPP;
-
-       switch (cwsr->in.op) {
-       case AMDGPU_TRAP_OP_SET_L2_TRAP: {
-               struct amdgpu_trap_usr_addr tba;
-               struct amdgpu_trap_usr_addr tma;
-
-               tba.addr = cwsr->in.l2trap.tba_va;
-               tba.size = cwsr->in.l2trap.tba_sz;
-               tma.addr = cwsr->in.l2trap.tma_va;
-               tma.size = cwsr->in.l2trap.tma_sz;
-               r = amdgpu_trap_set_l2_trap_handler(adev, &fpriv->vm, 
fpriv->trap_obj, &tma, &tba);
-       } break;
-       default:
-               return -EINVAL;
-       }
-
-       return r;
-}
 
 int amdgpu_trap_set_trap_debug_flag(struct amdgpu_device *adev,
                                    struct amdgpu_trap_obj *cwsr_obj,
@@ -625,4 +522,4 @@ int amdgpu_trap_set_trap_debug_flag(struct amdgpu_device 
*adev,
        l1tma[2] = enabled;
 
        return 0;
-}
\ No newline at end of file
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_trap.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_trap.h
index 6d4664469bad..495ffd3f1dd8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_trap.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_trap.h
@@ -92,8 +92,6 @@ static inline bool amdgpu_trap_has_dbg_wa(struct 
amdgpu_device *adev)
        return gc_ver >= IP_VERSION(11, 0, 0) && gc_ver <= IP_VERSION(11, 0, 3);
 }
 
-int amdgpu_trap_ioctl(struct drm_device *dev, void *data,
-                     struct drm_file *filp);
 int amdgpu_trap_set_trap_debug_flag(struct amdgpu_device *adev,
                                    struct amdgpu_trap_obj *cwsr_obj,
                                    bool enabled);
diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h
index 552cbf483543..d21c7a85e81e 100644
--- a/include/uapi/drm/amdgpu_drm.h
+++ b/include/uapi/drm/amdgpu_drm.h
@@ -59,7 +59,6 @@ extern "C" {
 #define DRM_AMDGPU_USERQ_WAIT          0x18
 #define DRM_AMDGPU_GEM_LIST_HANDLES    0x19
 #define DRM_AMDGPU_PROC_OPTIONS                0x1A
-#define DRM_AMDGPU_TRAP                        0x1B
 
 #define DRM_IOCTL_AMDGPU_GEM_CREATE    DRM_IOWR(DRM_COMMAND_BASE + 
DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create)
 #define DRM_IOCTL_AMDGPU_GEM_MMAP      DRM_IOWR(DRM_COMMAND_BASE + 
DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap)
@@ -82,7 +81,6 @@ extern "C" {
 #define DRM_IOCTL_AMDGPU_USERQ_WAIT    DRM_IOWR(DRM_COMMAND_BASE + 
DRM_AMDGPU_USERQ_WAIT, struct drm_amdgpu_userq_wait)
 #define DRM_IOCTL_AMDGPU_GEM_LIST_HANDLES DRM_IOWR(DRM_COMMAND_BASE + 
DRM_AMDGPU_GEM_LIST_HANDLES, struct drm_amdgpu_gem_list_handles)
 #define DRM_IOCTL_AMDGPU_PROC_OPTIONS  DRM_IOWR(DRM_COMMAND_BASE + 
DRM_AMDGPU_PROC_OPTIONS, struct drm_amdgpu_proc_options)
-#define DRM_IOCTL_AMDGPU_TRAP  DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_TRAP, 
union drm_amdgpu_trap)
 
 /**
  * DOC: memory domains
@@ -634,10 +632,86 @@ struct drm_amdgpu_userq_wait {
 #define AMDGPU_VM_OP_RESERVE_VMID      1
 #define AMDGPU_VM_OP_UNRESERVE_VMID    2
 
+/**
+ * AMDGPU_VM_OP_SET_L2_TRAP - install or replace the second-level trap
+ *   handler for all GFX and compute user queues belonging to this VM.
+ *
+ * The second-level trap handler is a per-VM setting. All shader waves
+ * executing under this VMID share the same handler regardless of queue
+ * type. GFX and compute queues from the same process share the same VMID
+ * so one SET_L2_TRAP covers all queue types for this DRM file.
+ *
+ * IMPORTANT: Both TBA and TMA buffers MUST be allocated with the
+ * AMDGPU_GEM_CREATE_VM_ALWAYS_VALID flag. The GPU jumps to TBA
+ * automatically on a hardware exception without any software job
+ * submission, so the kernel cannot track TBA/TMA in a job's used-buffer
+ * list. Without VM_ALWAYS_VALID the BO can be evicted and the page
+ * table entry becomes stale, causing a GPU page fault or memory
+ * corruption when the next shader exception fires.
+ *
+ * The TBA range must contain the handler executable code.
+ * The TMA range is the handler scratch memory buffer.
+ * Both ranges must be non-empty and fully mapped in the GPU virtual
+ * address space of this DRM file descriptor.
+ *
+ * Userspace should keep both mappings alive until
+ * AMDGPU_VM_OP_CLEAR_L2_TRAP succeeds. If either mapping is removed
+ * while the handler is active, the kernel waits for the VM to be idle,
+ * evicts all user queues, flushes the GPU TLB, clears the handler, and
+ * allows the unmap to proceed. UNMAP never returns an error for this
+ * condition. Queues whose trap handler VA was removed are not restarted.
+ *
+ * Returns:
+ *  0             on success;
+ *  -EOPNOTSUPP   if the first-level CWSR handler is unavailable;
+ *  -EINVAL       if TBA/TMA is invalid, not mapped, or not allocated
+ *                with AMDGPU_GEM_CREATE_VM_ALWAYS_VALID;
+ *  negative errno if the VM reservation fails.
+ */
+#define AMDGPU_VM_OP_SET_L2_TRAP       3
+
+/**
+ * AMDGPU_VM_OP_CLEAR_L2_TRAP - disable the second-level trap handler.
+ *
+ * All active user queues are evicted and the GPU TLB is flushed before
+ * TBA is zeroed to ensure no wave is executing inside the handler at the
+ * time of the clear. The l2trap input members are ignored.
+ *
+ * After this operation succeeds userspace may safely remove the TBA
+ * and TMA mappings.
+ *
+ * Returns:
+ *  0             on success;
+ *  -EOPNOTSUPP   if the first-level CWSR handler is unavailable;
+ *  negative errno if the VM reservation fails.
+ */
+#define AMDGPU_VM_OP_CLEAR_L2_TRAP     4
+
 struct drm_amdgpu_vm_in {
        /** AMDGPU_VM_OP_* */
        __u32   op;
        __u32   flags;
+       union {
+               struct {
+                       /** GPU VA of second-level trap handler code buffer */
+                       __u64 tba_va;
+                       /** TBA buffer size in bytes */
+                       __u32 tba_sz;
+                       /** Explicit padding; tma_va starts at offset 24 */
+                       __u32 _pad;
+                       /** GPU VA of second-level trap handler scratch buffer 
*/
+                       __u64 tma_va;
+                       /** TMA buffer size in bytes */
+                       __u32 tma_sz;
+                       /** Padding to fix total union size at 32 bytes */
+                       __u32 _pad2;
+               } l2trap;
+               /**
+                * Padding — keeps the union at a fixed 32-byte size for
+                * future ops. Zero-initialise for RESERVE/UNRESERVE_VMID.
+                */
+               __u64 _pad[4];
+       };
 };
 
 struct drm_amdgpu_vm_out {
@@ -1747,27 +1821,7 @@ struct drm_amdgpu_info_cwsr {
        __u32 min_save_area_size;
 };
 
-/* cwsr ioctl */
-#define AMDGPU_TRAP_OP_SET_L2_TRAP 1
 
-struct drm_amdgpu_trap_in {
-       /* AMDGPU_TRAP_OP_* */
-       __u32 op;
-       struct {
-               /* Level 2 trap handler base address */
-               __u64 tba_va;
-               /* Level 2 trap handler buffer size (in bytes) */
-               __u32 tba_sz;
-               /* Level 2 trap memory buffer address */
-               __u64 tma_va;
-               /* Level 2 trap memory buffer size (in bytes) */
-               __u32 tma_sz;
-       } l2trap;
-};
-
-union drm_amdgpu_trap {
-       struct drm_amdgpu_trap_in in;
-};
 /*
  * Supported GPU families
  */
-- 
2.34.1

Reply via email to