From 42dab3138176a944b09996441d837986f9ef13f8 Mon Sep 17 00:00:00 2001 From: "Mario Limonciello (AMD)" Date: Tue, 6 Jan 2026 11:00:16 -0600 Subject: drm/connector: Add a new 'panel_type' property If the driver can make an assertion whether a connected panel is an OLED panel or not then it can attach a property to the connector that userspace can use as a hint for color schemes. Reviewed-by: Leo Li Link: https://patch.msgid.link/20260106170017.68158-2-superm1@kernel.org Signed-off-by: Mario Limonciello (AMD) --- include/uapi/drm/drm_mode.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/uapi') diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index cbbbfc1dfe2b..3693d82b5279 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -166,6 +166,10 @@ extern "C" { #define DRM_MODE_LINK_STATUS_GOOD 0 #define DRM_MODE_LINK_STATUS_BAD 1 +/* Panel type property */ +#define DRM_MODE_PANEL_TYPE_UNKNOWN 0 +#define DRM_MODE_PANEL_TYPE_OLED 1 + /* * DRM_MODE_ROTATE_ * -- cgit From 3aecd55af5b83d16d84e3c333d4163999ee8ff51 Mon Sep 17 00:00:00 2001 From: Caterina Shablia Date: Wed, 28 Jan 2026 18:40:57 +0000 Subject: drm: add ARM interleaved 64k modifier This modifier is primarily intended to be used by panvk to implement sparse partially-resident images with better map and unmap performance, and no worse access performance, compared to implementing them in terms of U-interleaved. With this modifier, the plane is divided into 64k byte 1:1 or 2:1 -sided tiles. The 64k tiles are laid out linearly. Each 64k tile is divided into blocks of 16x16 texel blocks each, which themselves are laid out linearly within a 64k tile. Then within each such 16x16 block, texel blocks are laid out according to U order, similar to 16X16_BLOCK_U_INTERLEAVED. Unlike 16X16_BLOCK_U_INTERLEAVED, the layout does not depend on whether a format is compressed or not. The hardware features corresponding to this modifier are available starting with v10 (second gen Valhall.) The corresponding panvk MR can be found at: https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/38986 Previous version: https://lists.freedesktop.org/archives/dri-devel/2026-January/547072.html No changes since v2 Changes since v1: * Rewrite the description of the modifier to be hopefully unambiguous. Signed-off-by: Caterina Shablia Reviewed-by: Boris Brezillon Reviewed-by: Liviu Dudau Link: https://patch.msgid.link/20260128184058.807213-1-caterina.shablia@collabora.com Signed-off-by: Boris Brezillon --- include/uapi/drm/drm_fourcc.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'include/uapi') diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index e527b24bd824..452f901513ad 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -1422,6 +1422,22 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier) #define DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED \ DRM_FORMAT_MOD_ARM_CODE(DRM_FORMAT_MOD_ARM_TYPE_MISC, 1ULL) +/* + * ARM 64k interleaved modifier + * + * This is used by ARM Mali v10+ GPUs. With this modifier, the plane is divided + * into 64k byte 1:1 or 2:1 -sided tiles. The 64k tiles are laid out linearly. + * Each 64k tile is divided into blocks of 16x16 texel blocks, which are + * themselves laid out linearly within a 64k tile. Then within each 16x16 + * block, texel blocks are laid out according to U order, similar to + * 16X16_BLOCK_U_INTERLEAVED. + * + * Note that unlike 16X16_BLOCK_U_INTERLEAVED, the layout does not change + * depending on whether a format is compressed or not. + */ +#define DRM_FORMAT_MOD_ARM_INTERLEAVED_64K \ + DRM_FORMAT_MOD_ARM_CODE(DRM_FORMAT_MOD_ARM_TYPE_MISC, 2ULL) + /* * Allwinner tiled modifier * -- cgit From dc90ead44054736131f73b1dd319b8be06088d36 Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Fri, 30 Jan 2026 12:51:06 +0000 Subject: drm/xe/uapi: update used tracking kernel-doc In commit 4d0b035fd6da ("drm/xe/uapi: loosen used tracking restriction") we dropped the CAP_PERMON restriction but missed updating the corresponding kernel-doc. Fix that. v2 (Sanjay): - Don't drop the note around the extra cpu_visible_used expectations. Reported-by: Ulisses Furquim Fixes: 4d0b035fd6da ("drm/xe/uapi: loosen used tracking restriction") Signed-off-by: Matthew Auld Cc: Sanjay Yadav Reviewed-by: Sanjay Yadav Link: https://patch.msgid.link/20260130125105.451229-2-matthew.auld@intel.com --- include/uapi/drm/xe_drm.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'include/uapi') diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 077e66a682e2..c9e70f78e723 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -335,10 +335,6 @@ struct drm_xe_mem_region { __u64 total_size; /** * @used: Estimate of the memory used in bytes for this region. - * - * Requires CAP_PERFMON or CAP_SYS_ADMIN to get reliable - * accounting. Without this the value here will always equal - * zero. */ __u64 used; /** @@ -363,9 +359,7 @@ struct drm_xe_mem_region { * @cpu_visible_used: Estimate of CPU visible memory used, in * bytes. * - * Requires CAP_PERFMON or CAP_SYS_ADMIN to get reliable - * accounting. Without this the value here will always equal - * zero. Note this is only currently tracked for + * Note this is only currently tracked for * DRM_XE_MEM_REGION_CLASS_VRAM regions (for other types the value * here will always be zero). */ -- cgit From 196b2b95fec447c2c4460f753b277d840633fbef Mon Sep 17 00:00:00 2001 From: Mel Henning Date: Thu, 19 Feb 2026 15:05:54 -0500 Subject: drm/nouveau: Add DRM_IOCTL_NOUVEAU_GET_ZCULL_INFO Add kernel-side support for using the zcull hardware in nvidia gpus. zcull aims to improve memory bandwidth by using an early approximate depth test, similar to hierarchical Z on an AMD card. Add a new ioctl that exposes zcull information that has been read from the hardware. Userspace uses each of these parameters either in a heuristic for determining zcull region parameters or in the calculation of a buffer size. It appears the hardware hasn't changed its structure for these values since FERMI_C (circa 2011), so the assumption is that it won't change on us too quickly, and is therefore reasonable to include in UAPI. This bypasses the nvif layer and instead accesses nvkm_gr directly, which mirrors existing usage of nvkm_gr_units(). There is no nvif object for nvkm_gr yet, and adding one is not trivial. Signed-off-by: Mel Henning Link: https://patch.msgid.link/20260219-zcull3-v3-2-dbe6a716f104@darkrefraction.com Signed-off-by: Danilo Krummrich --- drivers/gpu/drm/nouveau/nouveau_abi16.c | 29 +++++++++++++++ drivers/gpu/drm/nouveau/nouveau_abi16.h | 1 + drivers/gpu/drm/nouveau/nouveau_drm.c | 1 + include/uapi/drm/nouveau_drm.h | 66 +++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+) (limited to 'include/uapi') diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index f9201f2e73a3..7860877d909b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -333,6 +333,35 @@ nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS) return 0; } +int +nouveau_abi16_ioctl_get_zcull_info(ABI16_IOCTL_ARGS) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nvkm_gr *gr = nvxx_gr(drm); + struct drm_nouveau_get_zcull_info *out = data; + + if (gr->has_zcull_info) { + const struct nvkm_gr_zcull_info *i = &gr->zcull_info; + + out->width_align_pixels = i->width_align_pixels; + out->height_align_pixels = i->height_align_pixels; + out->pixel_squares_by_aliquots = i->pixel_squares_by_aliquots; + out->aliquot_total = i->aliquot_total; + out->zcull_region_byte_multiplier = i->zcull_region_byte_multiplier; + out->zcull_region_header_size = i->zcull_region_header_size; + out->zcull_subregion_header_size = i->zcull_subregion_header_size; + out->subregion_count = i->subregion_count; + out->subregion_width_align_pixels = i->subregion_width_align_pixels; + out->subregion_height_align_pixels = i->subregion_height_align_pixels; + out->ctxsw_size = i->ctxsw_size; + out->ctxsw_align = i->ctxsw_align; + + return 0; + } else { + return -ENOTTY; + } +} + int nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS) { diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.h b/drivers/gpu/drm/nouveau/nouveau_abi16.h index af6b4e1cefd2..134b3ab58719 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.h +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.h @@ -6,6 +6,7 @@ struct drm_device *dev, void *data, struct drm_file *file_priv int nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS); +int nouveau_abi16_ioctl_get_zcull_info(ABI16_IOCTL_ARGS); int nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS); int nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS); int nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 17c114645d9f..5d8475e4895e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -1304,6 +1304,7 @@ nouveau_ioctls[] = { DRM_IOCTL_DEF_DRV(NOUVEAU_GROBJ_ALLOC, nouveau_abi16_ioctl_grobj_alloc, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(NOUVEAU_NOTIFIEROBJ_ALLOC, nouveau_abi16_ioctl_notifierobj_alloc, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(NOUVEAU_GPUOBJ_FREE, nouveau_abi16_ioctl_gpuobj_free, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(NOUVEAU_GET_ZCULL_INFO, nouveau_abi16_ioctl_get_zcull_info, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(NOUVEAU_SVM_INIT, nouveau_svmm_init, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(NOUVEAU_SVM_BIND, nouveau_svmm_bind, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_RENDER_ALLOW), diff --git a/include/uapi/drm/nouveau_drm.h b/include/uapi/drm/nouveau_drm.h index dd87f8f30793..1fa82fa6af38 100644 --- a/include/uapi/drm/nouveau_drm.h +++ b/include/uapi/drm/nouveau_drm.h @@ -432,6 +432,69 @@ struct drm_nouveau_exec { __u64 push_ptr; }; +struct drm_nouveau_get_zcull_info { + /** + * @width_align_pixels: required alignment for region widths, in pixels + * (typically #TPC's * 16). + */ + __u32 width_align_pixels; + /** + * @height_align_pixels: required alignment for region heights, in + * pixels (typically 32). + */ + __u32 height_align_pixels; + /** + * @pixel_squares_by_aliquots: the pixel area covered by an aliquot + * (typically #Zcull_banks * 16 * 16). + */ + __u32 pixel_squares_by_aliquots; + /** + * @aliquot_total: the total aliquot pool available in hardware + */ + __u32 aliquot_total; + /** + * @zcull_region_byte_multiplier: the size of an aliquot in bytes, which + * is used for save/restore operations on a region + */ + __u32 zcull_region_byte_multiplier; + /** + * @zcull_region_header_size: the region header size in bytes, which is + * used for save/restore operations on a region + */ + __u32 zcull_region_header_size; + /** + * @zcull_subregion_header_size: the subregion header size in bytes, + * which is used for save/restore operations on a region + */ + __u32 zcull_subregion_header_size; + /** + * @subregion_count: the total number of subregions the hardware + * supports + */ + __u32 subregion_count; + /** + * @subregion_width_align_pixels: required alignment for subregion + * widths, in pixels (typically #TPC's * 16). + */ + __u32 subregion_width_align_pixels; + /** + * @subregion_height_align_pixels: required alignment for subregion + * heights, in pixels + */ + __u32 subregion_height_align_pixels; + + /** + * @ctxsw_size: the size, in bytes, of a zcull context switching region. + * Will be zero if the kernel does not support zcull context switching. + */ + __u32 ctxsw_size; + /** + * @ctxsw_align: the alignment, in bytes, of a zcull context switching + * region + */ + __u32 ctxsw_align; +}; + #define DRM_NOUVEAU_GETPARAM 0x00 #define DRM_NOUVEAU_SETPARAM 0x01 /* deprecated */ #define DRM_NOUVEAU_CHANNEL_ALLOC 0x02 @@ -445,6 +508,7 @@ struct drm_nouveau_exec { #define DRM_NOUVEAU_VM_INIT 0x10 #define DRM_NOUVEAU_VM_BIND 0x11 #define DRM_NOUVEAU_EXEC 0x12 +#define DRM_NOUVEAU_GET_ZCULL_INFO 0x13 #define DRM_NOUVEAU_GEM_NEW 0x40 #define DRM_NOUVEAU_GEM_PUSHBUF 0x41 #define DRM_NOUVEAU_GEM_CPU_PREP 0x42 @@ -513,6 +577,8 @@ struct drm_nouveau_svm_bind { #define DRM_IOCTL_NOUVEAU_VM_INIT DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_VM_INIT, struct drm_nouveau_vm_init) #define DRM_IOCTL_NOUVEAU_VM_BIND DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_VM_BIND, struct drm_nouveau_vm_bind) #define DRM_IOCTL_NOUVEAU_EXEC DRM_IOWR(DRM_COMMAND_BASE + DRM_NOUVEAU_EXEC, struct drm_nouveau_exec) + +#define DRM_IOCTL_NOUVEAU_GET_ZCULL_INFO DRM_IOR (DRM_COMMAND_BASE + DRM_NOUVEAU_GET_ZCULL_INFO, struct drm_nouveau_get_zcull_info) #if defined(__cplusplus) } #endif -- cgit From 369cc88049855269b7620426bda4fb9ce2a2d1ca Mon Sep 17 00:00:00 2001 From: Thomas Hellström Date: Wed, 4 Feb 2026 16:33:20 +0100 Subject: drm/xe/uapi: Introduce a flag to disallow vm overcommit in fault mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some compute applications may try to allocate device memory to probe how much device memory is actually available, assuming that the application will be the only one running on the particular GPU. That strategy fails in fault mode since it allows VM overcommit. While this could be resolved in user-space it's further complicated by cgroups potentially restricting the amount of memory available to the application. Introduce a vm create flag, DRM_XE_VM_CREATE_NO_VM_OVERCOMMIT, that allows fault mode to mimic the behaviour of !fault mode WRT this. It blocks evicting same vm bos during VM_BIND processing. However, it does *not* block evicting same-vm bos during pagefault processing, preferring eviction rather than VM banning in OOM situations. Cc: John Falkowski Cc: Michal Mrozek Cc: Matthew Brost Signed-off-by: Thomas Hellström Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260204153320.17989-1-thomas.hellstrom@linux.intel.com --- drivers/gpu/drm/xe/xe_vm.c | 11 +++++++++-- drivers/gpu/drm/xe/xe_vm.h | 7 +++++++ drivers/gpu/drm/xe/xe_vm_types.h | 1 + include/uapi/drm/xe_drm.h | 6 ++++++ 4 files changed, 23 insertions(+), 2 deletions(-) (limited to 'include/uapi') diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index a46f11a71c37..550208ef63f8 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -1941,7 +1941,8 @@ find_ufence_get(struct xe_sync_entry *syncs, u32 num_syncs) #define ALL_DRM_XE_VM_CREATE_FLAGS (DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE | \ DRM_XE_VM_CREATE_FLAG_LR_MODE | \ - DRM_XE_VM_CREATE_FLAG_FAULT_MODE) + DRM_XE_VM_CREATE_FLAG_FAULT_MODE | \ + DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT) int xe_vm_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file) @@ -1980,12 +1981,18 @@ int xe_vm_create_ioctl(struct drm_device *dev, void *data, args->flags & DRM_XE_VM_CREATE_FLAG_FAULT_MODE)) return -EINVAL; + if (XE_IOCTL_DBG(xe, !(args->flags & DRM_XE_VM_CREATE_FLAG_FAULT_MODE) && + args->flags & DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT)) + return -EINVAL; + if (args->flags & DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE) flags |= XE_VM_FLAG_SCRATCH_PAGE; if (args->flags & DRM_XE_VM_CREATE_FLAG_LR_MODE) flags |= XE_VM_FLAG_LR_MODE; if (args->flags & DRM_XE_VM_CREATE_FLAG_FAULT_MODE) flags |= XE_VM_FLAG_FAULT_MODE; + if (args->flags & DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT) + flags |= XE_VM_FLAG_NO_VM_OVERCOMMIT; vm = xe_vm_create(xe, flags, xef); if (IS_ERR(vm)) @@ -2906,7 +2913,7 @@ static int vma_lock_and_validate(struct drm_exec *exec, struct xe_vma *vma, err = drm_exec_lock_obj(exec, &bo->ttm.base); if (!err && validate) err = xe_bo_validate(bo, vm, - !xe_vm_in_preempt_fence_mode(vm) && + xe_vm_allow_vm_eviction(vm) && res_evict, exec); } diff --git a/drivers/gpu/drm/xe/xe_vm.h b/drivers/gpu/drm/xe/xe_vm.h index 288115c7844a..f849e369432b 100644 --- a/drivers/gpu/drm/xe/xe_vm.h +++ b/drivers/gpu/drm/xe/xe_vm.h @@ -220,6 +220,13 @@ static inline bool xe_vm_in_preempt_fence_mode(struct xe_vm *vm) return xe_vm_in_lr_mode(vm) && !xe_vm_in_fault_mode(vm); } +static inline bool xe_vm_allow_vm_eviction(struct xe_vm *vm) +{ + return !xe_vm_in_lr_mode(vm) || + (xe_vm_in_fault_mode(vm) && + !(vm->flags & XE_VM_FLAG_NO_VM_OVERCOMMIT)); +} + int xe_vm_add_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q); void xe_vm_remove_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q); diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h index 43203e90ee3e..1f6f7e30e751 100644 --- a/drivers/gpu/drm/xe/xe_vm_types.h +++ b/drivers/gpu/drm/xe/xe_vm_types.h @@ -232,6 +232,7 @@ struct xe_vm { #define XE_VM_FLAG_TILE_ID(flags) FIELD_GET(GENMASK(7, 6), flags) #define XE_VM_FLAG_SET_TILE_ID(tile) FIELD_PREP(GENMASK(7, 6), (tile)->id) #define XE_VM_FLAG_GSC BIT(8) +#define XE_VM_FLAG_NO_VM_OVERCOMMIT BIT(9) unsigned long flags; /** diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index c9e70f78e723..ef2565048bdf 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -969,6 +969,11 @@ struct drm_xe_gem_mmap_offset { * demand when accessed, and also allows per-VM overcommit of memory. * The xe driver internally uses recoverable pagefaults to implement * this. + * - %DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT - Requires also + * DRM_XE_VM_CREATE_FLAG_FAULT_MODE. This disallows per-VM overcommit + * but only during a &DRM_IOCTL_XE_VM_BIND operation with the + * %DRM_XE_VM_BIND_FLAG_IMMEDIATE flag set. This may be useful for + * user-space naively probing the amount of available memory. */ struct drm_xe_vm_create { /** @extensions: Pointer to the first extension struct, if any */ @@ -977,6 +982,7 @@ struct drm_xe_vm_create { #define DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE (1 << 0) #define DRM_XE_VM_CREATE_FLAG_LR_MODE (1 << 1) #define DRM_XE_VM_CREATE_FLAG_FAULT_MODE (1 << 2) +#define DRM_XE_VM_CREATE_FLAG_NO_VM_OVERCOMMIT (1 << 3) /** @flags: Flags */ __u32 flags; -- cgit From d8e760b7996df37b6c1f25ca8a4a5645f144f63c Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Thu, 26 Feb 2026 21:14:27 +0530 Subject: drm/amdgpu: update type for num_syncobj_handles in drm_amdgpu_userq_signal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit update the type for num_syncobj_handles from __u64 to _u16 with required padding. This breaks the UAPI for big-endian platforms but this is deliberate and harmless since userqueues is still a beta feature. It is enabled via module parameter and need the right fw support to work. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher --- include/uapi/drm/amdgpu_drm.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include/uapi') diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index ebbd861ef0bc..3ab41571f511 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -479,7 +479,9 @@ struct drm_amdgpu_userq_signal { * @num_syncobj_handles: A count that represents the number of syncobj handles in * @syncobj_handles. */ - __u64 num_syncobj_handles; + __u16 num_syncobj_handles; + __u16 pad0; + __u32 pad1; /** * @bo_read_handles: The list of BO handles that the submitted user queue job * is using for read only. This will update BO fences in the kernel. -- cgit From c561d2320492e0dbe50a37437a525a2e91c471bd Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Thu, 26 Feb 2026 21:18:51 +0530 Subject: drm/amdgpu: update type for num_syncobj_handles in drm_amdgpu_userq_wait MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit update the type for num_syncobj_handles from __u32 to _u16 with required padding. This breaks the UAPI for big-endian platforms but this is deliberate and harmless since userqueues is still a beta feature. It is enabled via module parameter and need the right fw support to work. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher --- include/uapi/drm/amdgpu_drm.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/uapi') diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 3ab41571f511..9f3090db2f16 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -565,7 +565,8 @@ struct drm_amdgpu_userq_wait { * @num_syncobj_handles: A count that represents the number of syncobj handles in * @syncobj_handles. */ - __u32 num_syncobj_handles; + __u16 num_syncobj_handles; + __u16 pad0; /** * @num_bo_read_handles: A count that represents the number of read BO handles in * @bo_read_handles. -- cgit From c36218dc49f5e9ef9e3074670fdae7ac3a7e794f Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Wed, 4 Mar 2026 13:14:08 +0530 Subject: drm/ras: Introduce the DRM RAS infrastructure over generic netlink Introduces the DRM RAS infrastructure over generic netlink. The new interface allows drivers to expose RAS nodes and their associated error counters to userspace in a structured and extensible way. Each drm_ras node can register its own set of error counters, which are then discoverable and queryable through netlink operations. This lays the groundwork for reporting and managing hardware error states in a unified manner across different DRM drivers. Currently it only supports error-counter nodes. But it can be extended later. The registration is also not tied to any drm node, so it can be used by accel devices as well. It uses the new and mandatory YAML description format stored in Documentation/netlink/specs/. This forces a single generic netlink family namespace for the entire drm: "drm-ras". But multiple-endpoints are supported within the single family. Any modification to this API needs to be applied to Documentation/netlink/specs/drm_ras.yaml before regenerating the code: $ tools/net/ynl/pyynl/ynl_gen_c.py --spec \ Documentation/netlink/specs/drm_ras.yaml --mode uapi --header \ -o include/uapi/drm/drm_ras.h $ tools/net/ynl/pyynl/ynl_gen_c.py --spec \ Documentation/netlink/specs/drm_ras.yaml --mode kernel \ --header -o drivers/gpu/drm/drm_ras_nl.h $ tools/net/ynl/pyynl/ynl_gen_c.py --spec \ Documentation/netlink/specs/drm_ras.yaml \ --mode kernel --source -o drivers/gpu/drm/drm_ras_nl.c Cc: Zack McKevitt Cc: Lijo Lazar Cc: Hawking Zhang Cc: Jakub Kicinski Cc: David S. Miller Cc: Paolo Abeni Cc: Eric Dumazet Cc: netdev@vger.kernel.org Co-developed-by: Aravind Iddamsetty Signed-off-by: Aravind Iddamsetty Signed-off-by: Riana Tauro Reviewed-by: Zack McKevitt Acked-by: Jakub Kicinski Acked-by: Maarten Lankhorst Link: https://patch.msgid.link/20260304074412.464435-8-riana.tauro@intel.com Signed-off-by: Rodrigo Vivi --- Documentation/gpu/drm-ras.rst | 103 +++++++++ Documentation/gpu/index.rst | 1 + Documentation/netlink/specs/drm_ras.yaml | 115 ++++++++++ drivers/gpu/drm/Kconfig | 10 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_drv.c | 6 + drivers/gpu/drm/drm_ras.c | 354 +++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_ras_genl_family.c | 42 ++++ drivers/gpu/drm/drm_ras_nl.c | 56 +++++ drivers/gpu/drm/drm_ras_nl.h | 24 +++ include/drm/drm_ras.h | 75 +++++++ include/drm/drm_ras_genl_family.h | 17 ++ include/uapi/drm/drm_ras.h | 49 +++++ 13 files changed, 853 insertions(+) create mode 100644 Documentation/gpu/drm-ras.rst create mode 100644 Documentation/netlink/specs/drm_ras.yaml create mode 100644 drivers/gpu/drm/drm_ras.c create mode 100644 drivers/gpu/drm/drm_ras_genl_family.c create mode 100644 drivers/gpu/drm/drm_ras_nl.c create mode 100644 drivers/gpu/drm/drm_ras_nl.h create mode 100644 include/drm/drm_ras.h create mode 100644 include/drm/drm_ras_genl_family.h create mode 100644 include/uapi/drm/drm_ras.h (limited to 'include/uapi') diff --git a/Documentation/gpu/drm-ras.rst b/Documentation/gpu/drm-ras.rst new file mode 100644 index 000000000000..70b246a78fc8 --- /dev/null +++ b/Documentation/gpu/drm-ras.rst @@ -0,0 +1,103 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +============================ +DRM RAS over Generic Netlink +============================ + +The DRM RAS (Reliability, Availability, Serviceability) interface provides a +standardized way for GPU/accelerator drivers to expose error counters and +other reliability nodes to user space via Generic Netlink. This allows +diagnostic tools, monitoring daemons, or test infrastructure to query hardware +health in a uniform way across different DRM drivers. + +Key Goals: + +* Provide a standardized RAS solution for GPU and accelerator drivers, enabling + data center monitoring and reliability operations. +* Implement a single drm-ras Generic Netlink family to meet modern Netlink YAML + specifications and centralize all RAS-related communication in one namespace. +* Support a basic error counter interface, addressing the immediate, essential + monitoring needs. +* Offer a flexible, future-proof interface that can be extended to support + additional types of RAS data in the future. +* Allow multiple nodes per driver, enabling drivers to register separate + nodes for different IP blocks, sub-blocks, or other logical subdivisions + as applicable. + +Nodes +===== + +Nodes are logical abstractions representing an error type or error source within +the device. Currently, only error counter nodes is supported. + +Drivers are responsible for registering and unregistering nodes via the +`drm_ras_node_register()` and `drm_ras_node_unregister()` APIs. + +Node Management +------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_ras.c + :doc: DRM RAS Node Management +.. kernel-doc:: drivers/gpu/drm/drm_ras.c + :internal: + +Generic Netlink Usage +===================== + +The interface is implemented as a Generic Netlink family named ``drm-ras``. +User space tools can: + +* List registered nodes with the ``list-nodes`` command. +* List all error counters in an node with the ``get-error-counter`` command with ``node-id`` + as a parameter. +* Query specific error counter values with the ``get-error-counter`` command, using both + ``node-id`` and ``error-id`` as parameters. + +YAML-based Interface +-------------------- + +The interface is described in a YAML specification ``Documentation/netlink/specs/drm_ras.yaml`` + +This YAML is used to auto-generate user space bindings via +``tools/net/ynl/pyynl/ynl_gen_c.py``, and drives the structure of netlink +attributes and operations. + +Usage Notes +----------- + +* User space must first enumerate nodes to obtain their IDs. +* Node IDs or Node names can be used for all further queries, such as error counters. +* Error counters can be queried by either the Error ID or Error name. +* Query Parameters should be defined as part of the uAPI to ensure user interface stability. +* The interface supports future extension by adding new node types and + additional attributes. + +Example: List nodes using ynl + +.. code-block:: bash + + sudo ynl --family drm_ras --dump list-nodes + [{'device-name': '0000:03:00.0', + 'node-id': 0, + 'node-name': 'correctable-errors', + 'node-type': 'error-counter'}, + {'device-name': '0000:03:00.0', + 'node-id': 1, + 'node-name': 'uncorrectable-errors', + 'node-type': 'error-counter'}] + +Example: List all error counters using ynl + +.. code-block:: bash + + sudo ynl --family drm_ras --dump get-error-counter --json '{"node-id":0}' + [{'error-id': 1, 'error-name': 'error_name1', 'error-value': 0}, + {'error-id': 2, 'error-name': 'error_name2', 'error-value': 0}] + +Example: Query an error counter for a given node + +.. code-block:: bash + + sudo ynl --family drm_ras --do get-error-counter --json '{"node-id":0, "error-id":1}' + {'error-id': 1, 'error-name': 'error_name1', 'error-value': 0} + diff --git a/Documentation/gpu/index.rst b/Documentation/gpu/index.rst index 7dcb15850afd..60c73fdcfeed 100644 --- a/Documentation/gpu/index.rst +++ b/Documentation/gpu/index.rst @@ -9,6 +9,7 @@ GPU Driver Developer's Guide drm-mm drm-kms drm-kms-helpers + drm-ras drm-uapi drm-usage-stats driver-uapi diff --git a/Documentation/netlink/specs/drm_ras.yaml b/Documentation/netlink/specs/drm_ras.yaml new file mode 100644 index 000000000000..79af25dac3c5 --- /dev/null +++ b/Documentation/netlink/specs/drm_ras.yaml @@ -0,0 +1,115 @@ +# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) +--- +name: drm-ras +protocol: genetlink +uapi-header: drm/drm_ras.h + +doc: >- + DRM RAS (Reliability, Availability, Serviceability) over Generic Netlink. + Provides a standardized mechanism for DRM drivers to register "nodes" + representing hardware/software components capable of reporting error counters. + Userspace tools can query the list of nodes or individual error counters + via the Generic Netlink interface. + +definitions: + - + type: enum + name: node-type + value-start: 1 + entries: [error-counter] + doc: >- + Type of the node. Currently, only error-counter nodes are + supported, which expose reliability counters for a hardware/software + component. + +attribute-sets: + - + name: node-attrs + attributes: + - + name: node-id + type: u32 + doc: >- + Unique identifier for the node. + Assigned dynamically by the DRM RAS core upon registration. + - + name: device-name + type: string + doc: >- + Device name chosen by the driver at registration. + Can be a PCI BDF, UUID, or module name if unique. + - + name: node-name + type: string + doc: >- + Node name chosen by the driver at registration. + Can be an IP block name, or any name that identifies the + RAS node inside the device. + - + name: node-type + type: u32 + doc: Type of this node, identifying its function. + enum: node-type + - + name: error-counter-attrs + attributes: + - + name: node-id + type: u32 + doc: Node ID targeted by this error counter operation. + - + name: error-id + type: u32 + doc: Unique identifier for a specific error counter within an node. + - + name: error-name + type: string + doc: Name of the error. + - + name: error-value + type: u32 + doc: Current value of the requested error counter. + +operations: + list: + - + name: list-nodes + doc: >- + Retrieve the full list of currently registered DRM RAS nodes. + Each node includes its dynamically assigned ID, name, and type. + **Important:** User space must call this operation first to obtain + the node IDs. These IDs are required for all subsequent + operations on nodes, such as querying error counters. + attribute-set: node-attrs + flags: [admin-perm] + dump: + reply: + attributes: + - node-id + - device-name + - node-name + - node-type + - + name: get-error-counter + doc: >- + Retrieve error counter for a given node. + The response includes the id, the name, and even the current + value of each counter. + attribute-set: error-counter-attrs + flags: [admin-perm] + do: + request: + attributes: + - node-id + - error-id + reply: + attributes: &errorinfo + - error-id + - error-name + - error-value + dump: + request: + attributes: + - node-id + reply: + attributes: *errorinfo diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index d3d52310c9cc..18da88e050ce 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -130,6 +130,16 @@ config DRM_PANIC_SCREEN_QR_VERSION Smaller QR code are easier to read, but will contain less debugging data. Default is 40. +config DRM_RAS + bool "DRM RAS support" + depends on DRM + depends on NET + help + Enables the DRM RAS (Reliability, Availability and Serviceability) + support for DRM drivers. This provides a Generic Netlink interface + for error reporting and queries. + If in doubt, say "N". + config DRM_DEBUG_DP_MST_TOPOLOGY_REFS bool "Enable refcount backtrace history in the DP MST helpers" depends on STACKTRACE_SUPPORT diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 0c21029c446f..d1ad4ce873a3 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -95,6 +95,7 @@ drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o drm-$(CONFIG_DRM_PANIC) += drm_panic.o drm-$(CONFIG_DRM_DRAW) += drm_draw.o drm-$(CONFIG_DRM_PANIC_SCREEN_QR_CODE) += drm_panic_qr.o +drm-$(CONFIG_DRM_RAS) += drm_ras.o drm_ras_nl.o drm_ras_genl_family.o obj-$(CONFIG_DRM) += drm.o obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 2915118436ce..6b965c3d3307 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -53,6 +53,7 @@ #include #include #include +#include #include "drm_crtc_internal.h" #include "drm_internal.h" @@ -1223,6 +1224,7 @@ static const struct file_operations drm_stub_fops = { static void drm_core_exit(void) { + drm_ras_genl_family_unregister(); drm_privacy_screen_lookup_exit(); drm_panic_exit(); accel_core_exit(); @@ -1261,6 +1263,10 @@ static int __init drm_core_init(void) drm_privacy_screen_lookup_init(); + ret = drm_ras_genl_family_register(); + if (ret < 0) + goto error; + drm_core_init_complete = true; DRM_DEBUG("Initialized\n"); diff --git a/drivers/gpu/drm/drm_ras.c b/drivers/gpu/drm/drm_ras.c new file mode 100644 index 000000000000..b2fa5ab86d87 --- /dev/null +++ b/drivers/gpu/drm/drm_ras.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2026 Intel Corporation + */ + +#include +#include +#include +#include +#include + +#include + +#include "drm_ras_nl.h" + +/** + * DOC: DRM RAS Node Management + * + * This module provides the infrastructure to manage RAS (Reliability, + * Availability, and Serviceability) nodes for DRM drivers. Each + * DRM driver may register one or more RAS nodes, which represent + * logical components capable of reporting error counters and other + * reliability metrics. + * + * The nodes are stored in a global xarray `drm_ras_xa` to allow + * efficient lookup by ID. Nodes can be registered or unregistered + * dynamically at runtime. + * + * A Generic Netlink family `drm_ras` exposes two main operations to + * userspace: + * + * 1. LIST_NODES: Dump all currently registered RAS nodes. + * The user receives an array of node IDs, names, and types. + * + * 2. GET_ERROR_COUNTER: Get error counters of a given node. + * Userspace must provide Node ID, Error ID (Optional for specific counter). + * Returns all counters of a node if only Node ID is provided or specific + * error counters. + * + * Node registration: + * + * - drm_ras_node_register(): Registers a new node and assigns + * it a unique ID in the xarray. + * - drm_ras_node_unregister(): Removes a previously registered + * node from the xarray. + * + * Node type: + * + * - ERROR_COUNTER: + * + Currently, only error counters are supported. + * + The driver must implement the query_error_counter() callback to provide + * the name and the value of the error counter. + * + The driver must provide a error_counter_range.last value informing the + * last valid error ID. + * + The driver can provide a error_counter_range.first value informing the + * first valid error ID. + * + The error counters in the driver doesn't need to be contiguous, but the + * driver must return -ENOENT to the query_error_counter as an indication + * that the ID should be skipped and not listed in the netlink API. + * + * Netlink handlers: + * + * - drm_ras_nl_list_nodes_dumpit(): Implements the LIST_NODES + * operation, iterating over the xarray. + * - drm_ras_nl_get_error_counter_dumpit(): Implements the GET_ERROR_COUNTER dumpit + * operation, fetching all counters from a specific node. + * - drm_ras_nl_get_error_counter_doit(): Implements the GET_ERROR_COUNTER doit + * operation, fetching a counter value from a specific node. + */ + +static DEFINE_XARRAY_ALLOC(drm_ras_xa); + +/* + * The netlink callback context carries dump state across multiple dumpit calls + */ +struct drm_ras_ctx { + /* Which xarray id to restart the dump from */ + unsigned long restart; +}; + +/** + * drm_ras_nl_list_nodes_dumpit() - Dump all registered RAS nodes + * @skb: Netlink message buffer + * @cb: Callback context for multi-part dumps + * + * Iterates over all registered RAS nodes in the global xarray and appends + * their attributes (ID, name, type) to the given netlink message buffer. + * Uses @cb->ctx to track progress in case the message buffer fills up, allowing + * multi-part dump support. On buffer overflow, updates the context to resume + * from the last node on the next invocation. + * + * Return: 0 if all nodes fit in @skb, number of bytes added to @skb if + * the buffer filled up (requires multi-part continuation), or + * a negative error code on failure. + */ +int drm_ras_nl_list_nodes_dumpit(struct sk_buff *skb, + struct netlink_callback *cb) +{ + const struct genl_info *info = genl_info_dump(cb); + struct drm_ras_ctx *ctx = (void *)cb->ctx; + struct drm_ras_node *node; + struct nlattr *hdr; + unsigned long id; + int ret; + + xa_for_each_start(&drm_ras_xa, id, node, ctx->restart) { + hdr = genlmsg_iput(skb, info); + if (!hdr) { + ret = -EMSGSIZE; + break; + } + + ret = nla_put_u32(skb, DRM_RAS_A_NODE_ATTRS_NODE_ID, node->id); + if (ret) { + genlmsg_cancel(skb, hdr); + break; + } + + ret = nla_put_string(skb, DRM_RAS_A_NODE_ATTRS_DEVICE_NAME, + node->device_name); + if (ret) { + genlmsg_cancel(skb, hdr); + break; + } + + ret = nla_put_string(skb, DRM_RAS_A_NODE_ATTRS_NODE_NAME, + node->node_name); + if (ret) { + genlmsg_cancel(skb, hdr); + break; + } + + ret = nla_put_u32(skb, DRM_RAS_A_NODE_ATTRS_NODE_TYPE, + node->type); + if (ret) { + genlmsg_cancel(skb, hdr); + break; + } + + genlmsg_end(skb, hdr); + } + + if (ret == -EMSGSIZE) + ctx->restart = id; + + return ret; +} + +static int get_node_error_counter(u32 node_id, u32 error_id, + const char **name, u32 *value) +{ + struct drm_ras_node *node; + + node = xa_load(&drm_ras_xa, node_id); + if (!node || !node->query_error_counter) + return -ENOENT; + + if (error_id < node->error_counter_range.first || + error_id > node->error_counter_range.last) + return -EINVAL; + + return node->query_error_counter(node, error_id, name, value); +} + +static int msg_reply_value(struct sk_buff *msg, u32 error_id, + const char *error_name, u32 value) +{ + int ret; + + ret = nla_put_u32(msg, DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID, error_id); + if (ret) + return ret; + + ret = nla_put_string(msg, DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_NAME, + error_name); + if (ret) + return ret; + + return nla_put_u32(msg, DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_VALUE, + value); +} + +static int doit_reply_value(struct genl_info *info, u32 node_id, + u32 error_id) +{ + struct sk_buff *msg; + struct nlattr *hdr; + const char *error_name; + u32 value; + int ret; + + msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = genlmsg_iput(msg, info); + if (!hdr) { + nlmsg_free(msg); + return -EMSGSIZE; + } + + ret = get_node_error_counter(node_id, error_id, + &error_name, &value); + if (ret) + return ret; + + ret = msg_reply_value(msg, error_id, error_name, value); + if (ret) { + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + return ret; + } + + genlmsg_end(msg, hdr); + + return genlmsg_reply(msg, info); +} + +/** + * drm_ras_nl_get_error_counter_dumpit() - Dump all Error Counters + * @skb: Netlink message buffer + * @cb: Callback context for multi-part dumps + * + * Iterates over all error counters in a given Node and appends + * their attributes (ID, name, value) to the given netlink message buffer. + * Uses @cb->ctx to track progress in case the message buffer fills up, allowing + * multi-part dump support. On buffer overflow, updates the context to resume + * from the last node on the next invocation. + * + * Return: 0 if all errors fit in @skb, number of bytes added to @skb if + * the buffer filled up (requires multi-part continuation), or + * a negative error code on failure. + */ +int drm_ras_nl_get_error_counter_dumpit(struct sk_buff *skb, + struct netlink_callback *cb) +{ + const struct genl_info *info = genl_info_dump(cb); + struct drm_ras_ctx *ctx = (void *)cb->ctx; + struct drm_ras_node *node; + struct nlattr *hdr; + const char *error_name; + u32 node_id, error_id, value; + int ret; + + if (!info->attrs || GENL_REQ_ATTR_CHECK(info, DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID)) + return -EINVAL; + + node_id = nla_get_u32(info->attrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID]); + + node = xa_load(&drm_ras_xa, node_id); + if (!node) + return -ENOENT; + + for (error_id = max(node->error_counter_range.first, ctx->restart); + error_id <= node->error_counter_range.last; + error_id++) { + ret = get_node_error_counter(node_id, error_id, + &error_name, &value); + /* + * For non-contiguous range, driver return -ENOENT as indication + * to skip this ID when listing all errors. + */ + if (ret == -ENOENT) + continue; + if (ret) + return ret; + + hdr = genlmsg_iput(skb, info); + + if (!hdr) { + ret = -EMSGSIZE; + break; + } + + ret = msg_reply_value(skb, error_id, error_name, value); + if (ret) { + genlmsg_cancel(skb, hdr); + break; + } + + genlmsg_end(skb, hdr); + } + + if (ret == -EMSGSIZE) + ctx->restart = error_id; + + return ret; +} + +/** + * drm_ras_nl_get_error_counter_doit() - Query an error counter of an node + * @skb: Netlink message buffer + * @info: Generic Netlink info containing attributes of the request + * + * Extracts the node ID and error ID from the netlink attributes and + * retrieves the current value of the corresponding error counter. Sends the + * result back to the requesting user via the standard Genl reply. + * + * Return: 0 on success, or negative errno on failure. + */ +int drm_ras_nl_get_error_counter_doit(struct sk_buff *skb, + struct genl_info *info) +{ + u32 node_id, error_id; + + if (!info->attrs || + GENL_REQ_ATTR_CHECK(info, DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID) || + GENL_REQ_ATTR_CHECK(info, DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID)) + return -EINVAL; + + node_id = nla_get_u32(info->attrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID]); + error_id = nla_get_u32(info->attrs[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID]); + + return doit_reply_value(info, node_id, error_id); +} + +/** + * drm_ras_node_register() - Register a new RAS node + * @node: Node structure to register + * + * Adds the given RAS node to the global node xarray and assigns it + * a unique ID. Both @node->name and @node->type must be valid. + * + * Return: 0 on success, or negative errno on failure: + */ +int drm_ras_node_register(struct drm_ras_node *node) +{ + if (!node->device_name || !node->node_name) + return -EINVAL; + + /* Currently, only Error Counter Endpoints are supported */ + if (node->type != DRM_RAS_NODE_TYPE_ERROR_COUNTER) + return -EINVAL; + + /* Mandatory entries for Error Counter Node */ + if (node->type == DRM_RAS_NODE_TYPE_ERROR_COUNTER && + (!node->error_counter_range.last || !node->query_error_counter)) + return -EINVAL; + + return xa_alloc(&drm_ras_xa, &node->id, node, xa_limit_32b, GFP_KERNEL); +} +EXPORT_SYMBOL(drm_ras_node_register); + +/** + * drm_ras_node_unregister() - Unregister a previously registered node + * @node: Node structure to unregister + * + * Removes the given node from the global node xarray using its ID. + */ +void drm_ras_node_unregister(struct drm_ras_node *node) +{ + xa_erase(&drm_ras_xa, node->id); +} +EXPORT_SYMBOL(drm_ras_node_unregister); diff --git a/drivers/gpu/drm/drm_ras_genl_family.c b/drivers/gpu/drm/drm_ras_genl_family.c new file mode 100644 index 000000000000..6f406d3d48c5 --- /dev/null +++ b/drivers/gpu/drm/drm_ras_genl_family.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2026 Intel Corporation + */ + +#include +#include "drm_ras_nl.h" + +/* Track family registration so the drm_exit can be called at any time */ +static bool registered; + +/** + * drm_ras_genl_family_register() - Register drm-ras genl family + * + * Only to be called one at drm_drv_init() + */ +int drm_ras_genl_family_register(void) +{ + int ret; + + registered = false; + + ret = genl_register_family(&drm_ras_nl_family); + if (ret) + return ret; + + registered = true; + return 0; +} + +/** + * drm_ras_genl_family_unregister() - Unregister drm-ras genl family + * + * To be called one at drm_drv_exit() at any moment, but only once. + */ +void drm_ras_genl_family_unregister(void) +{ + if (registered) { + genl_unregister_family(&drm_ras_nl_family); + registered = false; + } +} diff --git a/drivers/gpu/drm/drm_ras_nl.c b/drivers/gpu/drm/drm_ras_nl.c new file mode 100644 index 000000000000..16803d0c4a44 --- /dev/null +++ b/drivers/gpu/drm/drm_ras_nl.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) +/* Do not edit directly, auto-generated from: */ +/* Documentation/netlink/specs/drm_ras.yaml */ +/* YNL-GEN kernel source */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ + +#include +#include + +#include "drm_ras_nl.h" + +#include + +/* DRM_RAS_CMD_GET_ERROR_COUNTER - do */ +static const struct nla_policy drm_ras_get_error_counter_do_nl_policy[DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID + 1] = { + [DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID] = { .type = NLA_U32, }, + [DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID] = { .type = NLA_U32, }, +}; + +/* DRM_RAS_CMD_GET_ERROR_COUNTER - dump */ +static const struct nla_policy drm_ras_get_error_counter_dump_nl_policy[DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID + 1] = { + [DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID] = { .type = NLA_U32, }, +}; + +/* Ops table for drm_ras */ +static const struct genl_split_ops drm_ras_nl_ops[] = { + { + .cmd = DRM_RAS_CMD_LIST_NODES, + .dumpit = drm_ras_nl_list_nodes_dumpit, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP, + }, + { + .cmd = DRM_RAS_CMD_GET_ERROR_COUNTER, + .doit = drm_ras_nl_get_error_counter_doit, + .policy = drm_ras_get_error_counter_do_nl_policy, + .maxattr = DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, + }, + { + .cmd = DRM_RAS_CMD_GET_ERROR_COUNTER, + .dumpit = drm_ras_nl_get_error_counter_dumpit, + .policy = drm_ras_get_error_counter_dump_nl_policy, + .maxattr = DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID, + .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DUMP, + }, +}; + +struct genl_family drm_ras_nl_family __ro_after_init = { + .name = DRM_RAS_FAMILY_NAME, + .version = DRM_RAS_FAMILY_VERSION, + .netnsok = true, + .parallel_ops = true, + .module = THIS_MODULE, + .split_ops = drm_ras_nl_ops, + .n_split_ops = ARRAY_SIZE(drm_ras_nl_ops), +}; diff --git a/drivers/gpu/drm/drm_ras_nl.h b/drivers/gpu/drm/drm_ras_nl.h new file mode 100644 index 000000000000..06ccd9342773 --- /dev/null +++ b/drivers/gpu/drm/drm_ras_nl.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* Do not edit directly, auto-generated from: */ +/* Documentation/netlink/specs/drm_ras.yaml */ +/* YNL-GEN kernel header */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ + +#ifndef _LINUX_DRM_RAS_GEN_H +#define _LINUX_DRM_RAS_GEN_H + +#include +#include + +#include + +int drm_ras_nl_list_nodes_dumpit(struct sk_buff *skb, + struct netlink_callback *cb); +int drm_ras_nl_get_error_counter_doit(struct sk_buff *skb, + struct genl_info *info); +int drm_ras_nl_get_error_counter_dumpit(struct sk_buff *skb, + struct netlink_callback *cb); + +extern struct genl_family drm_ras_nl_family; + +#endif /* _LINUX_DRM_RAS_GEN_H */ diff --git a/include/drm/drm_ras.h b/include/drm/drm_ras.h new file mode 100644 index 000000000000..5d50209e51db --- /dev/null +++ b/include/drm/drm_ras.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2026 Intel Corporation + */ + +#ifndef __DRM_RAS_H__ +#define __DRM_RAS_H__ + +#include + +/** + * struct drm_ras_node - A DRM RAS Node + */ +struct drm_ras_node { + /** @id: Unique identifier for the node. Dynamically assigned. */ + u32 id; + /** + * @device_name: Human-readable name of the device. Given by the driver. + */ + const char *device_name; + /** @node_name: Human-readable name of the node. Given by the driver. */ + const char *node_name; + /** @type: Type of the node (enum drm_ras_node_type). */ + enum drm_ras_node_type type; + + /* Error-Counter Related Callback and Variables */ + + /** @error_counter_range: Range of valid Error IDs for this node. */ + struct { + /** @first: First valid Error ID. */ + u32 first; + /** @last: Last valid Error ID. Mandatory entry. */ + u32 last; + } error_counter_range; + + /** + * @query_error_counter: + * + * This callback is used by drm-ras to query a specific error counter. + * Used for input check and to iterate all error counters in a node. + * + * Driver should expect query_error_counter() to be called with + * error_id from `error_counter_range.first` to + * `error_counter_range.last`. + * + * The @query_error_counter is a mandatory callback for + * error_counter_node. + * + * Returns: 0 on success, + * -ENOENT when error_id is not supported as an indication that + * drm_ras should silently skip this entry. Used for + * supporting non-contiguous error ranges. + * Driver is responsible for maintaining the list of + * supported error IDs in the range of first to last. + * Other negative values on errors that should terminate the + * netlink query. + */ + int (*query_error_counter)(struct drm_ras_node *node, u32 error_id, + const char **name, u32 *val); + + /** @priv: Driver private data */ + void *priv; +}; + +struct drm_device; + +#if IS_ENABLED(CONFIG_DRM_RAS) +int drm_ras_node_register(struct drm_ras_node *node); +void drm_ras_node_unregister(struct drm_ras_node *node); +#else +static inline int drm_ras_node_register(struct drm_ras_node *node) { return 0; } +static inline void drm_ras_node_unregister(struct drm_ras_node *node) { } +#endif + +#endif diff --git a/include/drm/drm_ras_genl_family.h b/include/drm/drm_ras_genl_family.h new file mode 100644 index 000000000000..910fb3943a75 --- /dev/null +++ b/include/drm/drm_ras_genl_family.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2026 Intel Corporation + */ + +#ifndef __DRM_RAS_GENL_FAMILY_H__ +#define __DRM_RAS_GENL_FAMILY_H__ + +#if IS_ENABLED(CONFIG_DRM_RAS) +int drm_ras_genl_family_register(void); +void drm_ras_genl_family_unregister(void); +#else +static inline int drm_ras_genl_family_register(void) { return 0; } +static inline void drm_ras_genl_family_unregister(void) { } +#endif + +#endif diff --git a/include/uapi/drm/drm_ras.h b/include/uapi/drm/drm_ras.h new file mode 100644 index 000000000000..5f40fa5b869d --- /dev/null +++ b/include/uapi/drm/drm_ras.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* Do not edit directly, auto-generated from: */ +/* Documentation/netlink/specs/drm_ras.yaml */ +/* YNL-GEN uapi header */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ + +#ifndef _UAPI_LINUX_DRM_RAS_H +#define _UAPI_LINUX_DRM_RAS_H + +#define DRM_RAS_FAMILY_NAME "drm-ras" +#define DRM_RAS_FAMILY_VERSION 1 + +/* + * Type of the node. Currently, only error-counter nodes are supported, which + * expose reliability counters for a hardware/software component. + */ +enum drm_ras_node_type { + DRM_RAS_NODE_TYPE_ERROR_COUNTER = 1, +}; + +enum { + DRM_RAS_A_NODE_ATTRS_NODE_ID = 1, + DRM_RAS_A_NODE_ATTRS_DEVICE_NAME, + DRM_RAS_A_NODE_ATTRS_NODE_NAME, + DRM_RAS_A_NODE_ATTRS_NODE_TYPE, + + __DRM_RAS_A_NODE_ATTRS_MAX, + DRM_RAS_A_NODE_ATTRS_MAX = (__DRM_RAS_A_NODE_ATTRS_MAX - 1) +}; + +enum { + DRM_RAS_A_ERROR_COUNTER_ATTRS_NODE_ID = 1, + DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_ID, + DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_NAME, + DRM_RAS_A_ERROR_COUNTER_ATTRS_ERROR_VALUE, + + __DRM_RAS_A_ERROR_COUNTER_ATTRS_MAX, + DRM_RAS_A_ERROR_COUNTER_ATTRS_MAX = (__DRM_RAS_A_ERROR_COUNTER_ATTRS_MAX - 1) +}; + +enum { + DRM_RAS_CMD_LIST_NODES = 1, + DRM_RAS_CMD_GET_ERROR_COUNTER, + + __DRM_RAS_CMD_MAX, + DRM_RAS_CMD_MAX = (__DRM_RAS_CMD_MAX - 1) +}; + +#endif /* _UAPI_LINUX_DRM_RAS_H */ -- cgit From b40db12b542f503b5ec689d18d473299d49eeb60 Mon Sep 17 00:00:00 2001 From: Riana Tauro Date: Wed, 4 Mar 2026 13:14:09 +0530 Subject: drm/xe/xe_drm_ras: Add support for XE DRM RAS Allocate correctable, uncorrectable nodes for every xe device. Each node contains error component, counters and respective query counter functions. Add basic functionality to create and register drm nodes. Below operations can be performed using Generic netlink DRM RAS interface: 1) List Nodes: $ sudo ynl --family drm_ras --dump list-nodes [{'device-name': '0000:03:00.0', 'node-id': 0, 'node-name': 'correctable-errors', 'node-type': 'error-counter'}, {'device-name': '0000:03:00.0', 'node-id': 1, 'node-name': 'uncorrectable-errors', 'node-type': 'error-counter'}] 2) Get Error counters: $ sudo ynl --family drm_ras --dump get-error-counter --json '{"node-id":0}' [{'error-id': 1, 'error-name': 'core-compute', 'error-value': 0}, {'error-id': 2, 'error-name': 'soc-internal', 'error-value': 0}] 3) Get specific Error counter: $ sudo ynl --family drm_ras --do get-error-counter --json '{"node-id":0, "error-id":1}' {'error-id': 1, 'error-name': 'core-compute', 'error-value': 0} Signed-off-by: Riana Tauro Reviewed-by: Raag Jadav Link: https://patch.msgid.link/20260304074412.464435-9-riana.tauro@intel.com Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/Makefile | 1 + drivers/gpu/drm/xe/xe_device_types.h | 4 + drivers/gpu/drm/xe/xe_drm_ras.c | 186 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/xe/xe_drm_ras.h | 15 +++ drivers/gpu/drm/xe/xe_drm_ras_types.h | 40 ++++++++ include/uapi/drm/xe_drm.h | 79 +++++++++++++++ 6 files changed, 325 insertions(+) create mode 100644 drivers/gpu/drm/xe/xe_drm_ras.c create mode 100644 drivers/gpu/drm/xe/xe_drm_ras.h create mode 100644 drivers/gpu/drm/xe/xe_drm_ras_types.h (limited to 'include/uapi') diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 4a7eaeeca293..a32d370c3d30 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -41,6 +41,7 @@ xe-y += xe_bb.o \ xe_device_sysfs.o \ xe_dma_buf.o \ xe_drm_client.o \ + xe_drm_ras.o \ xe_eu_stall.o \ xe_exec.o \ xe_exec_queue.o \ diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h index e9032014923d..3e04e80e0815 100644 --- a/drivers/gpu/drm/xe/xe_device_types.h +++ b/drivers/gpu/drm/xe/xe_device_types.h @@ -13,6 +13,7 @@ #include #include "xe_devcoredump_types.h" +#include "xe_drm_ras_types.h" #include "xe_heci_gsc.h" #include "xe_late_bind_fw_types.h" #include "xe_oa_types.h" @@ -511,6 +512,9 @@ struct xe_device { /** @pmu: performance monitoring unit */ struct xe_pmu pmu; + /** @ras: RAS structure for device */ + struct xe_drm_ras ras; + /** @i2c: I2C host controller */ struct xe_i2c *i2c; diff --git a/drivers/gpu/drm/xe/xe_drm_ras.c b/drivers/gpu/drm/xe/xe_drm_ras.c new file mode 100644 index 000000000000..e07dc23a155e --- /dev/null +++ b/drivers/gpu/drm/xe/xe_drm_ras.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2026 Intel Corporation + */ + +#include + +#include +#include +#include + +#include "xe_device_types.h" +#include "xe_drm_ras.h" + +static const char * const error_components[] = DRM_XE_RAS_ERROR_COMPONENT_NAMES; +static const char * const error_severity[] = DRM_XE_RAS_ERROR_SEVERITY_NAMES; + +static int hw_query_error_counter(struct xe_drm_ras_counter *info, + u32 error_id, const char **name, u32 *val) +{ + if (!info || !info[error_id].name) + return -ENOENT; + + *name = info[error_id].name; + *val = atomic_read(&info[error_id].counter); + + return 0; +} + +static int query_uncorrectable_error_counter(struct drm_ras_node *ep, u32 error_id, + const char **name, u32 *val) +{ + struct xe_device *xe = ep->priv; + struct xe_drm_ras *ras = &xe->ras; + struct xe_drm_ras_counter *info = ras->info[DRM_XE_RAS_ERR_SEV_UNCORRECTABLE]; + + return hw_query_error_counter(info, error_id, name, val); +} + +static int query_correctable_error_counter(struct drm_ras_node *ep, u32 error_id, + const char **name, u32 *val) +{ + struct xe_device *xe = ep->priv; + struct xe_drm_ras *ras = &xe->ras; + struct xe_drm_ras_counter *info = ras->info[DRM_XE_RAS_ERR_SEV_CORRECTABLE]; + + return hw_query_error_counter(info, error_id, name, val); +} + +static struct xe_drm_ras_counter *allocate_and_copy_counters(struct xe_device *xe) +{ + struct xe_drm_ras_counter *counter; + int i; + + counter = kcalloc(DRM_XE_RAS_ERR_COMP_MAX, sizeof(*counter), GFP_KERNEL); + if (!counter) + return ERR_PTR(-ENOMEM); + + for (i = DRM_XE_RAS_ERR_COMP_CORE_COMPUTE; i < DRM_XE_RAS_ERR_COMP_MAX; i++) { + if (!error_components[i]) + continue; + + counter[i].name = error_components[i]; + atomic_set(&counter[i].counter, 0); + } + + return counter; +} + +static int assign_node_params(struct xe_device *xe, struct drm_ras_node *node, + const enum drm_xe_ras_error_severity severity) +{ + struct pci_dev *pdev = to_pci_dev(xe->drm.dev); + struct xe_drm_ras *ras = &xe->ras; + const char *device_name; + + device_name = kasprintf(GFP_KERNEL, "%04x:%02x:%02x.%d", + pci_domain_nr(pdev->bus), pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + + if (!device_name) + return -ENOMEM; + + node->device_name = device_name; + node->node_name = error_severity[severity]; + node->type = DRM_RAS_NODE_TYPE_ERROR_COUNTER; + node->error_counter_range.first = DRM_XE_RAS_ERR_COMP_CORE_COMPUTE; + node->error_counter_range.last = DRM_XE_RAS_ERR_COMP_MAX - 1; + node->priv = xe; + + ras->info[severity] = allocate_and_copy_counters(xe); + if (IS_ERR(ras->info[severity])) + return PTR_ERR(ras->info[severity]); + + if (severity == DRM_XE_RAS_ERR_SEV_CORRECTABLE) + node->query_error_counter = query_correctable_error_counter; + else + node->query_error_counter = query_uncorrectable_error_counter; + + return 0; +} + +static void cleanup_node_param(struct xe_drm_ras *ras, const enum drm_xe_ras_error_severity severity) +{ + struct drm_ras_node *node = &ras->node[severity]; + + kfree(ras->info[severity]); + ras->info[severity] = NULL; + + kfree(node->device_name); + node->device_name = NULL; +} + +static int register_nodes(struct xe_device *xe) +{ + struct xe_drm_ras *ras = &xe->ras; + int i; + + for_each_error_severity(i) { + struct drm_ras_node *node = &ras->node[i]; + int ret; + + ret = assign_node_params(xe, node, i); + if (ret) { + cleanup_node_param(ras, i); + return ret; + } + + ret = drm_ras_node_register(node); + if (ret) { + cleanup_node_param(ras, i); + return ret; + } + } + + return 0; +} + +static void xe_drm_ras_unregister_nodes(struct drm_device *device, void *arg) +{ + struct xe_device *xe = arg; + struct xe_drm_ras *ras = &xe->ras; + int i; + + for_each_error_severity(i) { + struct drm_ras_node *node = &ras->node[i]; + + drm_ras_node_unregister(node); + cleanup_node_param(ras, i); + } +} + +/** + * xe_drm_ras_init() - Initialize DRM RAS + * @xe: xe device instance + * + * Allocate and register DRM RAS nodes per device + * + * Return: 0 on success, negative error code otherwise. + */ +int xe_drm_ras_init(struct xe_device *xe) +{ + struct xe_drm_ras *ras = &xe->ras; + struct drm_ras_node *node; + int err; + + node = drmm_kcalloc(&xe->drm, DRM_XE_RAS_ERR_SEV_MAX, sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + ras->node = node; + + err = register_nodes(xe); + if (err) { + drm_err(&xe->drm, "Failed to register DRM RAS nodes (%pe)\n", ERR_PTR(err)); + return err; + } + + err = drmm_add_action_or_reset(&xe->drm, xe_drm_ras_unregister_nodes, xe); + if (err) { + drm_err(&xe->drm, "Failed to add action for Xe DRM RAS (%pe)\n", ERR_PTR(err)); + return err; + } + + return 0; +} diff --git a/drivers/gpu/drm/xe/xe_drm_ras.h b/drivers/gpu/drm/xe/xe_drm_ras.h new file mode 100644 index 000000000000..5cc8f0124411 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_drm_ras.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2026 Intel Corporation + */ +#ifndef XE_DRM_RAS_H_ +#define XE_DRM_RAS_H_ + +struct xe_device; + +#define for_each_error_severity(i) \ + for (i = 0; i < DRM_XE_RAS_ERR_SEV_MAX; i++) + +int xe_drm_ras_init(struct xe_device *xe); + +#endif diff --git a/drivers/gpu/drm/xe/xe_drm_ras_types.h b/drivers/gpu/drm/xe/xe_drm_ras_types.h new file mode 100644 index 000000000000..7acc5e7377b2 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_drm_ras_types.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2026 Intel Corporation + */ + +#ifndef _XE_DRM_RAS_TYPES_H_ +#define _XE_DRM_RAS_TYPES_H_ + +#include +#include + +struct drm_ras_node; + +/** + * struct xe_drm_ras_counter - XE RAS counter + * + * This structure contains error component and counter information + */ +struct xe_drm_ras_counter { + /** @name: error component name */ + const char *name; + + /** @counter: count of error */ + atomic_t counter; +}; + +/** + * struct xe_drm_ras - XE DRM RAS structure + * + * This structure has details of error counters + */ +struct xe_drm_ras { + /** @node: DRM RAS node */ + struct drm_ras_node *node; + + /** @info: info array for all types of errors */ + struct xe_drm_ras_counter *info[DRM_XE_RAS_ERR_SEV_MAX]; +}; + +#endif diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index ef2565048bdf..b0264c32ceb2 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -2357,6 +2357,85 @@ struct drm_xe_exec_queue_set_property { __u64 reserved[2]; }; +/** + * DOC: Xe DRM RAS + * + * The enums and strings defined below map to the attributes of the DRM RAS Netlink Interface. + * Refer to Documentation/netlink/specs/drm_ras.yaml for complete interface specification. + * + * Node Registration + * ================= + * + * The driver registers DRM RAS nodes for each error severity level. + * enum drm_xe_ras_error_severity defines the node-id, while DRM_XE_RAS_ERROR_SEVERITY_NAMES maps + * node-id to node-name. + * + * Error Classification + * ==================== + * + * Each node contains a list of error counters. Each error is identified by a error-id and + * an error-name. enum drm_xe_ras_error_component defines the error-id, while + * DRM_XE_RAS_ERROR_COMPONENT_NAMES maps error-id to error-name. + * + * User Interface + * ============== + * + * To retrieve error values of a error counter, userspace applications should + * follow the below steps: + * + * 1. Use command LIST_NODES to enumerate all available nodes + * 2. Select node by node-id or node-name + * 3. Use command GET_ERROR_COUNTERS to list errors of specific node + * 4. Query specific error values using either error-id or error-name + * + * .. code-block:: C + * + * // Lookup tables for ID-to-name resolution + * static const char *nodes[] = DRM_XE_RAS_ERROR_SEVERITY_NAMES; + * static const char *errors[] = DRM_XE_RAS_ERROR_COMPONENT_NAMES; + * + */ + +/** + * enum drm_xe_ras_error_severity - DRM RAS error severity. + */ +enum drm_xe_ras_error_severity { + /** @DRM_XE_RAS_ERR_SEV_CORRECTABLE: Correctable Error */ + DRM_XE_RAS_ERR_SEV_CORRECTABLE = 0, + /** @DRM_XE_RAS_ERR_SEV_UNCORRECTABLE: Uncorrectable Error */ + DRM_XE_RAS_ERR_SEV_UNCORRECTABLE, + /** @DRM_XE_RAS_ERR_SEV_MAX: Max severity */ + DRM_XE_RAS_ERR_SEV_MAX /* non-ABI */ +}; + +/** + * enum drm_xe_ras_error_component - DRM RAS error component. + */ +enum drm_xe_ras_error_component { + /** @DRM_XE_RAS_ERR_COMP_CORE_COMPUTE: Core Compute Error */ + DRM_XE_RAS_ERR_COMP_CORE_COMPUTE = 1, + /** @DRM_XE_RAS_ERR_COMP_SOC_INTERNAL: SoC Internal Error */ + DRM_XE_RAS_ERR_COMP_SOC_INTERNAL, + /** @DRM_XE_RAS_ERR_COMP_MAX: Max Error */ + DRM_XE_RAS_ERR_COMP_MAX /* non-ABI */ +}; + +/* + * Error severity to name mapping. + */ +#define DRM_XE_RAS_ERROR_SEVERITY_NAMES { \ + [DRM_XE_RAS_ERR_SEV_CORRECTABLE] = "correctable-errors", \ + [DRM_XE_RAS_ERR_SEV_UNCORRECTABLE] = "uncorrectable-errors", \ +} + +/* + * Error component to name mapping. + */ +#define DRM_XE_RAS_ERROR_COMPONENT_NAMES { \ + [DRM_XE_RAS_ERR_COMP_CORE_COMPUTE] = "core-compute", \ + [DRM_XE_RAS_ERR_COMP_SOC_INTERNAL] = "soc-internal" \ +} + #if defined(__cplusplus) } #endif -- cgit From 0e07b16371b6eef9b5a4a1fd3e7942938811072e Mon Sep 17 00:00:00 2001 From: Lionel Landwerlin Date: Fri, 6 Mar 2026 09:55:03 +0200 Subject: drm/xe: Allow per queue programming of COMMON_SLICE_CHICKEN3 bit13 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to i915's commit cebc13de7e704b1355bea208a9f9cdb042c74588 ("drm/i915: Whitelist COMMON_SLICE_CHICKEN3 for UMD access"), except that instead of putting the register on the allowlist for UMD to program, the KMD is doing the programming at context initialization based on a queue creation flag. This is a recommended tuning setting for both gen12 and Xe_HP platforms. If a render queue is created with DRM_XE_EXEC_QUEUE_SET_STATE_CACHE_PERF_FIX, COMMON_SLICE_CHICKEN3 will be programmed at initialization to enable the render color cache to key with BTP+BTI (binding table pool + binding table entry) instead of just BTI (binding table entry). This enables the UMD to avoid emitting render-target-cache-flush + stall-at-pixel-scoreboard every time a binding table entry pointing to a render target is changed. v2: Use xe_lrc_write_ring() v3: Update xe_query.c to report availability v4: Rename defines to add DISABLE_ v5: update commit message v6: rebase Mesa MR: https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/39982 Bspec: 73993, 73994, 72161, 31870, 68331 Acked-by: Rodrigo Vivi Reviewed-by: José Roberto de Souza Signed-off-by: Lionel Landwerlin Signed-off-by: José Roberto de Souza Link: https://patch.msgid.link/20260306075504.1288676-1-lionel.g.landwerlin@intel.com --- drivers/gpu/drm/xe/regs/xe_gt_regs.h | 1 + drivers/gpu/drm/xe/xe_exec_queue.c | 19 ++++++++++++++++++- drivers/gpu/drm/xe/xe_exec_queue_types.h | 2 ++ drivers/gpu/drm/xe/xe_lrc.c | 9 +++++++++ drivers/gpu/drm/xe/xe_lrc.h | 1 + drivers/gpu/drm/xe/xe_query.c | 2 ++ include/uapi/drm/xe_drm.h | 8 ++++++++ 7 files changed, 41 insertions(+), 1 deletion(-) (limited to 'include/uapi') diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h index 8e6df9dcd137..4ee88f629c02 100644 --- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h +++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h @@ -183,6 +183,7 @@ #define COMMON_SLICE_CHICKEN3 XE_REG(0x7304, XE_REG_OPTION_MASKED) #define XEHP_COMMON_SLICE_CHICKEN3 XE_REG_MCR(0x7304, XE_REG_OPTION_MASKED) +#define DISABLE_STATE_CACHE_PERF_FIX REG_BIT(13) #define DG1_FLOAT_POINT_BLEND_OPT_STRICT_MODE_EN REG_BIT(12) #define XEHP_DUAL_SIMD8_SEQ_MERGE_DISABLE REG_BIT(12) #define BLEND_EMB_FIX_DISABLE_IN_RCC REG_BIT(11) diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c index 6166b1a81433..a11021d36f87 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue.c +++ b/drivers/gpu/drm/xe/xe_exec_queue.c @@ -353,6 +353,9 @@ static int __xe_exec_queue_init(struct xe_exec_queue *q, u32 exec_queue_flags) if (!(exec_queue_flags & EXEC_QUEUE_FLAG_KERNEL)) flags |= XE_LRC_CREATE_USER_CTX; + if (q->flags & EXEC_QUEUE_FLAG_DISABLE_STATE_CACHE_PERF_FIX) + flags |= XE_LRC_DISABLE_STATE_CACHE_PERF_FIX; + err = q->ops->init(q); if (err) return err; @@ -978,6 +981,17 @@ static int exec_queue_set_multi_queue_priority(struct xe_device *xe, struct xe_e return q->ops->set_multi_queue_priority(q, value); } +static int exec_queue_set_state_cache_perf_fix(struct xe_device *xe, struct xe_exec_queue *q, + u64 value) +{ + if (XE_IOCTL_DBG(xe, q->class != XE_ENGINE_CLASS_RENDER)) + return -EOPNOTSUPP; + + q->flags |= value != 0 ? EXEC_QUEUE_FLAG_DISABLE_STATE_CACHE_PERF_FIX : 0; + + return 0; +} + typedef int (*xe_exec_queue_set_property_fn)(struct xe_device *xe, struct xe_exec_queue *q, u64 value); @@ -990,6 +1004,8 @@ static const xe_exec_queue_set_property_fn exec_queue_set_property_funcs[] = { [DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP] = exec_queue_set_multi_group, [DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY] = exec_queue_set_multi_queue_priority, + [DRM_XE_EXEC_QUEUE_SET_DISABLE_STATE_CACHE_PERF_FIX] = + exec_queue_set_state_cache_perf_fix, }; /** @@ -1085,7 +1101,8 @@ static int exec_queue_user_ext_set_property(struct xe_device *xe, ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_PXP_TYPE && ext.property != DRM_XE_EXEC_QUEUE_SET_HANG_REPLAY_STATE && ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP && - ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY)) + ext.property != DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY && + ext.property != DRM_XE_EXEC_QUEUE_SET_DISABLE_STATE_CACHE_PERF_FIX)) return -EINVAL; idx = array_index_nospec(ext.property, ARRAY_SIZE(exec_queue_set_property_funcs)); diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h index a1f3938f4173..8ce78e0b1d50 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue_types.h +++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h @@ -134,6 +134,8 @@ struct xe_exec_queue { #define EXEC_QUEUE_FLAG_LOW_LATENCY BIT(5) /* for migration (kernel copy, clear, bind) jobs */ #define EXEC_QUEUE_FLAG_MIGRATE BIT(6) +/* for programming COMMON_SLICE_CHICKEN3 on first submission */ +#define EXEC_QUEUE_FLAG_DISABLE_STATE_CACHE_PERF_FIX BIT(7) /** * @flags: flags for this exec queue, should statically setup aside from ban diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index ebab5d78f7cc..a1f856eff4ee 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -14,6 +14,7 @@ #include "instructions/xe_gfxpipe_commands.h" #include "instructions/xe_gfx_state_commands.h" #include "regs/xe_engine_regs.h" +#include "regs/xe_gt_regs.h" #include "regs/xe_lrc_layout.h" #include "xe_bb.h" #include "xe_bo.h" @@ -1446,6 +1447,7 @@ static int xe_lrc_ctx_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, struct struct xe_device *xe = gt_to_xe(gt); struct iosys_map map; u32 arb_enable; + u32 state_cache_perf_fix[3]; int err; /* @@ -1546,6 +1548,13 @@ static int xe_lrc_ctx_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe, struct arb_enable = MI_ARB_ON_OFF | MI_ARB_ENABLE; xe_lrc_write_ring(lrc, &arb_enable, sizeof(arb_enable)); + if (init_flags & XE_LRC_DISABLE_STATE_CACHE_PERF_FIX) { + state_cache_perf_fix[0] = MI_LOAD_REGISTER_IMM | MI_LRI_NUM_REGS(1); + state_cache_perf_fix[1] = COMMON_SLICE_CHICKEN3.addr; + state_cache_perf_fix[2] = _MASKED_BIT_ENABLE(DISABLE_STATE_CACHE_PERF_FIX); + xe_lrc_write_ring(lrc, state_cache_perf_fix, sizeof(state_cache_perf_fix)); + } + map = __xe_lrc_seqno_map(lrc); xe_map_write32(lrc_to_xe(lrc), &map, lrc->fence_ctx.next_seqno - 1); diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h index 48f7c26cf129..e7c975f9e2d9 100644 --- a/drivers/gpu/drm/xe/xe_lrc.h +++ b/drivers/gpu/drm/xe/xe_lrc.h @@ -49,6 +49,7 @@ struct xe_lrc_snapshot { #define XE_LRC_CREATE_RUNALONE BIT(0) #define XE_LRC_CREATE_PXP BIT(1) #define XE_LRC_CREATE_USER_CTX BIT(2) +#define XE_LRC_DISABLE_STATE_CACHE_PERF_FIX BIT(3) struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm, void *replay_state, u32 ring_size, u16 msix_vec, u32 flags); diff --git a/drivers/gpu/drm/xe/xe_query.c b/drivers/gpu/drm/xe/xe_query.c index 34db266b723f..4852fdcb4b95 100644 --- a/drivers/gpu/drm/xe/xe_query.c +++ b/drivers/gpu/drm/xe/xe_query.c @@ -340,6 +340,8 @@ static int query_config(struct xe_device *xe, struct drm_xe_device_query *query) DRM_XE_QUERY_CONFIG_FLAG_HAS_NO_COMPRESSION_HINT; config->info[DRM_XE_QUERY_CONFIG_FLAGS] |= DRM_XE_QUERY_CONFIG_FLAG_HAS_LOW_LATENCY; + config->info[DRM_XE_QUERY_CONFIG_FLAGS] |= + DRM_XE_QUERY_CONFIG_FLAG_HAS_DISABLE_STATE_CACHE_PERF_FIX; config->info[DRM_XE_QUERY_CONFIG_MIN_ALIGNMENT] = xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K ? SZ_64K : SZ_4K; config->info[DRM_XE_QUERY_CONFIG_VA_BITS] = xe->info.va_bits; diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index b0264c32ceb2..f074871b4d96 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -406,6 +406,9 @@ struct drm_xe_query_mem_regions { * - %DRM_XE_QUERY_CONFIG_FLAG_HAS_NO_COMPRESSION_HINT - Flag is set if the * device supports the userspace hint %DRM_XE_GEM_CREATE_FLAG_NO_COMPRESSION. * This is exposed only on Xe2+. + * - %DRM_XE_QUERY_CONFIG_FLAG_HAS_DISABLE_STATE_CACHE_PERF_FIX - Flag is set + * if a queue can be creaed with + * %DRM_XE_EXEC_QUEUE_SET_DISABLE_STATE_CACHE_PERF_FIX * - %DRM_XE_QUERY_CONFIG_MIN_ALIGNMENT - Minimal memory alignment * required by this device, typically SZ_4K or SZ_64K * - %DRM_XE_QUERY_CONFIG_VA_BITS - Maximum bits of a virtual address @@ -425,6 +428,7 @@ struct drm_xe_query_config { #define DRM_XE_QUERY_CONFIG_FLAG_HAS_LOW_LATENCY (1 << 1) #define DRM_XE_QUERY_CONFIG_FLAG_HAS_CPU_ADDR_MIRROR (1 << 2) #define DRM_XE_QUERY_CONFIG_FLAG_HAS_NO_COMPRESSION_HINT (1 << 3) + #define DRM_XE_QUERY_CONFIG_FLAG_HAS_DISABLE_STATE_CACHE_PERF_FIX (1 << 4) #define DRM_XE_QUERY_CONFIG_MIN_ALIGNMENT 2 #define DRM_XE_QUERY_CONFIG_VA_BITS 3 #define DRM_XE_QUERY_CONFIG_MAX_EXEC_QUEUE_PRIORITY 4 @@ -1285,6 +1289,9 @@ struct drm_xe_vm_bind { * - %DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY - Set the queue * priority within the multi-queue group. Current valid priority values are 0–2 * (default is 1), with higher values indicating higher priority. + * - %DRM_XE_EXEC_QUEUE_SET_DISABLE_STATE_CACHE_PERF_FIX - Set the queue to + * enable render color cache keying on BTP+BTI instead of just BTI + * (only valid for render queues). * * The example below shows how to use @drm_xe_exec_queue_create to create * a simple exec_queue (no parallel submission) of class @@ -1329,6 +1336,7 @@ struct drm_xe_exec_queue_create { #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_GROUP 4 #define DRM_XE_MULTI_GROUP_CREATE (1ull << 63) #define DRM_XE_EXEC_QUEUE_SET_PROPERTY_MULTI_QUEUE_PRIORITY 5 +#define DRM_XE_EXEC_QUEUE_SET_DISABLE_STATE_CACHE_PERF_FIX 6 /** @extensions: Pointer to the first extension struct, if any */ __u64 extensions; -- cgit From f66d6cc6891e41be96380261943837b1909107b3 Mon Sep 17 00:00:00 2001 From: "Mario Limonciello (AMD)" Date: Wed, 11 Mar 2026 10:18:42 -0700 Subject: accel/amdxdna: Support sensors for column utilization The AMD PMF driver provides realtime column utilization (npu_busy) metrics for the NPU. Extend the DRM_IOCTL_AMDXDNA_GET_INFO sensor query to expose these metrics to userspace. Add AMDXDNA_SENSOR_TYPE_COLUMN_UTILIZATION to the sensor type enum and update aie2_get_sensors() to return both the total power and up to 8 column utilization sensors if the user buffer permits. Signed-off-by: Mario Limonciello (AMD) Reviewed-by: Lizhi Hou [lizhi: support legacy tool which uses small buffer. checkpatch cleanup] Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260311171842.473453-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_pci.c | 34 +++++++++++++++++++++++++++++----- drivers/accel/amdxdna/aie2_pci.h | 8 ++++++++ include/uapi/drm/amdxdna_accel.h | 3 ++- 3 files changed, 39 insertions(+), 6 deletions(-) (limited to 'include/uapi') diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c index a2e586512e26..c57c785a2d15 100644 --- a/drivers/accel/amdxdna/aie2_pci.c +++ b/drivers/accel/amdxdna/aie2_pci.c @@ -787,16 +787,18 @@ static int aie2_get_clock_metadata(struct amdxdna_client *client, static int aie2_get_sensors(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) { + struct amdxdna_dev_hdl *ndev = client->xdna->dev_handle; struct amdxdna_drm_query_sensor sensor = {}; + struct amd_pmf_npu_metrics npu_metrics; + u32 sensors_count = 0, i; int ret; - if (args->buffer_size < sizeof(sensor)) - return -EINVAL; - - ret = AIE2_GET_PMF_NPU_DATA(npu_power, sensor.input); + ret = AIE2_GET_PMF_NPU_METRICS(&npu_metrics); if (ret) return ret; + sensor.type = AMDXDNA_SENSOR_TYPE_POWER; + sensor.input = npu_metrics.npu_power; sensor.unitm = -3; scnprintf(sensor.label, sizeof(sensor.label), "Total Power"); scnprintf(sensor.units, sizeof(sensor.units), "mW"); @@ -804,7 +806,29 @@ static int aie2_get_sensors(struct amdxdna_client *client, if (copy_to_user(u64_to_user_ptr(args->buffer), &sensor, sizeof(sensor))) return -EFAULT; - args->buffer_size = sizeof(sensor); + sensors_count++; + if (args->buffer_size <= sensors_count * sizeof(sensor)) + goto out; + + for (i = 0; i < min_t(u32, ndev->total_col, 8); i++) { + memset(&sensor, 0, sizeof(sensor)); + sensor.input = npu_metrics.npu_busy[i]; + sensor.type = AMDXDNA_SENSOR_TYPE_COLUMN_UTILIZATION; + sensor.unitm = 0; + scnprintf(sensor.label, sizeof(sensor.label), "Column %d Utilization", i); + scnprintf(sensor.units, sizeof(sensor.units), "%%"); + + if (copy_to_user(u64_to_user_ptr(args->buffer) + sensors_count * sizeof(sensor), + &sensor, sizeof(sensor))) + return -EFAULT; + + sensors_count++; + if (args->buffer_size <= sensors_count * sizeof(sensor)) + goto out; + } + +out: + args->buffer_size = sensors_count * sizeof(sensor); return 0; } diff --git a/drivers/accel/amdxdna/aie2_pci.h b/drivers/accel/amdxdna/aie2_pci.h index 1bb88711bedb..0ae174862592 100644 --- a/drivers/accel/amdxdna/aie2_pci.h +++ b/drivers/accel/amdxdna/aie2_pci.h @@ -48,6 +48,7 @@ }) #if IS_ENABLED(CONFIG_AMD_PMF) +#define AIE2_GET_PMF_NPU_METRICS(metrics) amd_pmf_get_npu_data(metrics) #define AIE2_GET_PMF_NPU_DATA(field, val) \ ({ \ struct amd_pmf_npu_metrics _npu_metrics; \ @@ -58,6 +59,13 @@ (_ret); \ }) #else +#define AIE2_GET_PMF_NPU_METRICS(metrics) \ +({ \ + typeof(metrics) _m = metrics; \ + memset(_m, 0xff, sizeof(*_m)); \ + (-EOPNOTSUPP); \ +}) + #define SENSOR_DEFAULT_npu_power U32_MAX #define AIE2_GET_PMF_NPU_DATA(field, val) \ ({ \ diff --git a/include/uapi/drm/amdxdna_accel.h b/include/uapi/drm/amdxdna_accel.h index 9c44db2b3dcd..5bd13f4435f5 100644 --- a/include/uapi/drm/amdxdna_accel.h +++ b/include/uapi/drm/amdxdna_accel.h @@ -353,7 +353,8 @@ struct amdxdna_drm_query_clock_metadata { }; enum amdxdna_sensor_type { - AMDXDNA_SENSOR_TYPE_POWER + AMDXDNA_SENSOR_TYPE_POWER, + AMDXDNA_SENSOR_TYPE_COLUMN_UTILIZATION }; /** -- cgit From 2270bd7124f4d25497d58c293cd40ea014ddaf01 Mon Sep 17 00:00:00 2001 From: Nitin Gote Date: Wed, 4 Mar 2026 18:08:00 +0530 Subject: drm/xe: add VM_BIND DECOMPRESS uapi flag Add a new VM_BIND flag, DRM_XE_VM_BIND_FLAG_DECOMPRESS, that lets userspace express intent for the driver to perform on-device in-place decompression for the GPU mapping created by a MAP bind operation. This flag is used by subsequent driver changes to trigger scheduling of GPU work that resolves compressed VRAM pages into an uncompressed PAT VM mapping. Behavior and semantics: - Valid only for DRM_XE_VM_BIND_OP_MAP. IOCTLs using this flag on other ops are rejected (-EINVAL). - The bind's pat_index must select the device "no-compression" PAT entry; otherwise the ioctl is rejected (-EINVAL). - Only meaningful for VRAM-backed BOs on devices that support Flat CCS and the required hardware generation (driver will return -EOPNOTSUPP if not). - On success the driver schedules a migrate/resolve and installs the returned dma_fence into the BO's kernel reservation (DMA_RESV_USAGE_KERNEL). Compute PR: https://github.com/intel/compute-runtime/pull/898 v3: Rebase on latest drm-tip and add compute pr info v2: Add kernel doc (Matt) Cc: Matthew Brost Cc: Matthew Auld Cc: Mrozek, Michal Reviewed-by: Matthew Brost Signed-off-by: Nitin Gote Acked-by: Michal Mrozek Signed-off-by: Matthew Auld Link: https://patch.msgid.link/20260304123758.3050386-6-nitin.r.gote@intel.com --- include/uapi/drm/xe_drm.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include/uapi') diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index f074871b4d96..0497b85fa12a 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -1057,6 +1057,13 @@ struct drm_xe_vm_destroy { * not invoke autoreset. Neither will stack variables going out of scope. * Therefore it's recommended to always explicitly reset the madvises when * freeing the memory backing a region used in a &DRM_IOCTL_XE_MADVISE call. + * - DRM_XE_VM_BIND_FLAG_DECOMPRESS - Request on-device decompression for a MAP. + * When set on a MAP bind operation, request the driver schedule an on-device + * in-place decompression (via the migrate/resolve path) for the GPU mapping + * created by this bind. Only valid for DRM_XE_VM_BIND_OP_MAP; usage on + * other ops is rejected. The bind's pat_index must select the device's + * "no-compression" PAT. Only meaningful for VRAM-backed BOs on devices that + * support Flat CCS and the required HW generation XE2+. * * The @prefetch_mem_region_instance for %DRM_XE_VM_BIND_OP_PREFETCH can also be: * - %DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC, which ensures prefetching occurs in @@ -1164,6 +1171,7 @@ struct drm_xe_vm_bind_op { #define DRM_XE_VM_BIND_FLAG_CHECK_PXP (1 << 4) #define DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR (1 << 5) #define DRM_XE_VM_BIND_FLAG_MADVISE_AUTORESET (1 << 6) +#define DRM_XE_VM_BIND_FLAG_DECOMPRESS (1 << 7) /** @flags: Bind flags */ __u32 flags; -- cgit From 341a2c99c87ce6f62c6f4423fa641a39f0966bff Mon Sep 17 00:00:00 2001 From: Nitin Gote Date: Thu, 12 Mar 2026 21:32:45 +0530 Subject: drm/xe/uapi: Fix kernel-doc for DRM_XE_VM_BIND_FLAG_DECOMPRESS There is kernel-doc warning for DRM_XE_VM_BIND_FLAG_DECOMPRESS: ./include/uapi/drm/xe_drm.h:1060: WARNING: Block quote ends without a blank line; unexpected unindent. Fix the warning by adding the missing '%' prefix to DRM_XE_VM_BIND_FLAG_DECOMPRESS in the kernel-doc list entry for struct drm_xe_vm_bind_op. Fixes: 2270bd7124f4 ("drm/xe: add VM_BIND DECOMPRESS uapi flag") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603121515.gEMrFlTL-lkp@intel.com/ Cc: Matthew Auld Signed-off-by: Nitin Gote Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260312160244.809849-2-nitin.r.gote@intel.com Signed-off-by: Tejas Upadhyay --- include/uapi/drm/xe_drm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/uapi') diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 0497b85fa12a..f8b2afb20540 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -1057,7 +1057,7 @@ struct drm_xe_vm_destroy { * not invoke autoreset. Neither will stack variables going out of scope. * Therefore it's recommended to always explicitly reset the madvises when * freeing the memory backing a region used in a &DRM_IOCTL_XE_MADVISE call. - * - DRM_XE_VM_BIND_FLAG_DECOMPRESS - Request on-device decompression for a MAP. + * - %DRM_XE_VM_BIND_FLAG_DECOMPRESS - Request on-device decompression for a MAP. * When set on a MAP bind operation, request the driver schedule an on-device * in-place decompression (via the migrate/resolve path) for the GPU mapping * created by this bind. Only valid for DRM_XE_VM_BIND_OP_MAP; usage on -- cgit From de9e2b3d88af36411301c049a1b049f3e4fe0757 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 3 Mar 2026 21:24:17 +0200 Subject: uapi: Provide DIV_ROUND_CLOSEST() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently DIV_ROUND_CLOSEST() is only available for the kernel via include/linux/math.h. Expose it to userland as well by adding __KERNEL_DIV_ROUND_CLOSEST() as a common definition in uapi. Additionally, ensure it allows building ISO C applications by switching from the 'typeof' GNU extension to the ISO-friendly __typeof__. Reviewed-by: Nícolas F. R. A. Prado Tested-by: Diederik de Haas Acked-by: Andy Shevchenko Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260303-rk3588-bgcolor-v8-1-fee377037ad1@collabora.com Signed-off-by: Daniel Stone --- include/linux/math.h | 18 +----------------- include/uapi/linux/const.h | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 17 deletions(-) (limited to 'include/uapi') diff --git a/include/linux/math.h b/include/linux/math.h index 6dc1d1d32fbc..1e8fb3efbc8c 100644 --- a/include/linux/math.h +++ b/include/linux/math.h @@ -89,23 +89,7 @@ } \ ) -/* - * Divide positive or negative dividend by positive or negative divisor - * and round to closest integer. Result is undefined for negative - * divisors if the dividend variable type is unsigned and for negative - * dividends if the divisor variable type is unsigned. - */ -#define DIV_ROUND_CLOSEST(x, divisor)( \ -{ \ - typeof(x) __x = x; \ - typeof(divisor) __d = divisor; \ - (((typeof(x))-1) > 0 || \ - ((typeof(divisor))-1) > 0 || \ - (((__x) > 0) == ((__d) > 0))) ? \ - (((__x) + ((__d) / 2)) / (__d)) : \ - (((__x) - ((__d) / 2)) / (__d)); \ -} \ -) +#define DIV_ROUND_CLOSEST __KERNEL_DIV_ROUND_CLOSEST /* * Same as above but for u64 dividends. divisor must be a 32-bit * number. diff --git a/include/uapi/linux/const.h b/include/uapi/linux/const.h index b8f629ef135f..565f309b9df8 100644 --- a/include/uapi/linux/const.h +++ b/include/uapi/linux/const.h @@ -50,4 +50,22 @@ #define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +/* + * Divide positive or negative dividend by positive or negative divisor + * and round to closest integer. Result is undefined for negative + * divisors if the dividend variable type is unsigned and for negative + * dividends if the divisor variable type is unsigned. + */ +#define __KERNEL_DIV_ROUND_CLOSEST(x, divisor) \ +({ \ + __typeof__(x) __x = x; \ + __typeof__(divisor) __d = divisor; \ + \ + (((__typeof__(x))-1) > 0 || \ + ((__typeof__(divisor))-1) > 0 || \ + (((__x) > 0) == ((__d) > 0))) ? \ + (((__x) + ((__d) / 2)) / (__d)) : \ + (((__x) - ((__d) / 2)) / (__d)); \ +}) + #endif /* _UAPI_LINUX_CONST_H */ -- cgit From 4c684596cde44d03dfd9624c86e1de4db0dcf121 Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 3 Mar 2026 21:24:18 +0200 Subject: drm: Add CRTC background color property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some display controllers can be hardware programmed to show non-black colors for pixels that are either not covered by any plane or are exposed through transparent regions of higher planes. This feature can help reduce memory bandwidth usage, e.g. in compositors managing a UI with a solid background color while using smaller planes to render the remaining content. To support this capability, introduce the BACKGROUND_COLOR standard DRM mode property, which can be attached to a CRTC through the drm_crtc_attach_background_color_property() helper function. Additionally, define a 64-bit ARGB format value to be built with the help of a couple of dedicated DRM_ARGB64_PREP*() helpers. Individual color components can be extracted with desired precision using the corresponding DRM_ARGB64_GET*() macros. Co-developed-by: Matt Roper Signed-off-by: Matt Roper Reviewed-by: Nícolas F. R. A. Prado Tested-by: Diederik de Haas Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260303-rk3588-bgcolor-v8-2-fee377037ad1@collabora.com Signed-off-by: Daniel Stone --- drivers/gpu/drm/drm_atomic.c | 1 + drivers/gpu/drm/drm_atomic_state_helper.c | 1 + drivers/gpu/drm/drm_atomic_uapi.c | 4 ++ drivers/gpu/drm/drm_blend.c | 39 +++++++++++++-- drivers/gpu/drm/drm_mode_config.c | 6 +++ include/drm/drm_blend.h | 4 +- include/drm/drm_crtc.h | 12 +++++ include/drm/drm_mode_config.h | 5 ++ include/uapi/drm/drm_mode.h | 80 +++++++++++++++++++++++++++++++ 9 files changed, 147 insertions(+), 5 deletions(-) (limited to 'include/uapi') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index dd9f27cfe991..6a395e5e3885 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -475,6 +475,7 @@ static void drm_atomic_crtc_print_state(struct drm_printer *p, drm_printf(p, "\tconnector_mask=%x\n", state->connector_mask); drm_printf(p, "\tencoder_mask=%x\n", state->encoder_mask); drm_printf(p, "\tmode: " DRM_MODE_FMT "\n", DRM_MODE_ARG(&state->mode)); + drm_printf(p, "\tbackground_color=%llx\n", state->background_color); if (crtc->funcs->atomic_print_state) crtc->funcs->atomic_print_state(p, state); diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index bd6faa09f83b..76746ad4a1bb 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -75,6 +75,7 @@ __drm_atomic_helper_crtc_state_reset(struct drm_crtc_state *crtc_state, struct drm_crtc *crtc) { crtc_state->crtc = crtc; + crtc_state->background_color = DRM_ARGB64_PREP(0xffff, 0, 0, 0); } EXPORT_SYMBOL(__drm_atomic_helper_crtc_state_reset); diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 87de41fb4459..5bd5bf6661df 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -454,6 +454,8 @@ static int drm_atomic_crtc_set_property(struct drm_crtc *crtc, &replaced); state->color_mgmt_changed |= replaced; return ret; + } else if (property == config->background_color_property) { + state->background_color = val; } else if (property == config->prop_out_fence_ptr) { s32 __user *fence_ptr = u64_to_user_ptr(val); @@ -501,6 +503,8 @@ drm_atomic_crtc_get_property(struct drm_crtc *crtc, *val = (state->ctm) ? state->ctm->base.id : 0; else if (property == config->gamma_lut_property) *val = (state->gamma_lut) ? state->gamma_lut->base.id : 0; + else if (property == config->background_color_property) + *val = state->background_color; else if (property == config->prop_out_fence_ptr) *val = 0; else if (property == crtc->scaling_filter_property) diff --git a/drivers/gpu/drm/drm_blend.c b/drivers/gpu/drm/drm_blend.c index 3b1f5f72885e..1f3af27d2418 100644 --- a/drivers/gpu/drm/drm_blend.c +++ b/drivers/gpu/drm/drm_blend.c @@ -191,10 +191,6 @@ * plane does not expose the "alpha" property, then this is * assumed to be 1.0 * - * Note that all the property extensions described here apply either to the - * plane or the CRTC (e.g. for the background color, which currently is not - * exposed and assumed to be black). - * * SCALING_FILTER: * Indicates scaling filter to be used for plane scaler * @@ -207,6 +203,25 @@ * * Drivers can set up this property for a plane by calling * drm_plane_create_scaling_filter_property + * + * The property extensions described above all apply to the plane. Drivers + * may also expose the following crtc property extension: + * + * BACKGROUND_COLOR: + * Background color is set up with drm_crtc_attach_background_color_property(), + * and expects a 64-bit ARGB value following DRM_FORMAT_ARGB16161616, as + * generated by the DRM_ARGB64_PREP*() helpers. It controls the color of a + * full-screen layer that exists below all planes. This color will be used + * for pixels not covered by any plane and may also be blended with plane + * contents as allowed by a plane's alpha values. + * The background color defaults to black, and is assumed to be black for + * drivers that do not expose this property. Although background color + * isn't a plane, it is assumed that the color provided here undergoes the + * CRTC degamma/CSC/gamma transformations applied after the planes blending. + * Note that the color value includes an alpha channel, hence non-opaque + * background color values are allowed, but since physically transparent + * monitors do not (yet) exists, the final alpha value may not reach the + * video sink or it may simply ignore it. */ /** @@ -621,3 +636,19 @@ int drm_plane_create_blend_mode_property(struct drm_plane *plane, return 0; } EXPORT_SYMBOL(drm_plane_create_blend_mode_property); + +/** + * drm_crtc_attach_background_color_property - attach background color property + * @crtc: drm crtc + * + * Attaches the background color property to @crtc. The property defaults to + * solid black and will accept 64-bit ARGB values in the format generated by + * DRM_ARGB64_PREP*() helpers. + */ +void drm_crtc_attach_background_color_property(struct drm_crtc *crtc) +{ + drm_object_attach_property(&crtc->base, + crtc->dev->mode_config.background_color_property, + DRM_ARGB64_PREP(0xffff, 0, 0, 0)); +} +EXPORT_SYMBOL(drm_crtc_attach_background_color_property); diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 84ae8a23a367..66f7dc37b597 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -380,6 +380,12 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) return -ENOMEM; dev->mode_config.gamma_lut_size_property = prop; + prop = drm_property_create_range(dev, 0, + "BACKGROUND_COLOR", 0, U64_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.background_color_property = prop; + prop = drm_property_create(dev, DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_BLOB, "IN_FORMATS", 0); diff --git a/include/drm/drm_blend.h b/include/drm/drm_blend.h index 88bdfec3bd88..c7e888767c81 100644 --- a/include/drm/drm_blend.h +++ b/include/drm/drm_blend.h @@ -31,8 +31,9 @@ #define DRM_MODE_BLEND_COVERAGE 1 #define DRM_MODE_BLEND_PIXEL_NONE 2 -struct drm_device; struct drm_atomic_state; +struct drm_crtc; +struct drm_device; struct drm_plane; static inline bool drm_rotation_90_or_270(unsigned int rotation) @@ -58,4 +59,5 @@ int drm_atomic_normalize_zpos(struct drm_device *dev, struct drm_atomic_state *state); int drm_plane_create_blend_mode_property(struct drm_plane *plane, unsigned int supported_modes); +void drm_crtc_attach_background_color_property(struct drm_crtc *crtc); #endif diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 66278ffeebd6..312fc1e745d2 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -274,6 +274,18 @@ struct drm_crtc_state { */ struct drm_property_blob *gamma_lut; + /** + * @background_color: + * + * RGB value representing the CRTC's background color. The background + * color (aka "canvas color") of a CRTC is the color that will be used + * for pixels not covered by a plane, or covered by transparent pixels + * of a plane. The value here should be built using DRM_ARGB64_PREP*() + * helpers, while the individual color components can be extracted with + * desired precision via the DRM_ARGB64_GET*() macros. + */ + u64 background_color; + /** * @target_vblank: * diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 5e1dd0cfccde..687c0ee163d2 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -836,6 +836,11 @@ struct drm_mode_config { * gamma LUT as supported by the driver (read-only). */ struct drm_property *gamma_lut_size_property; + /** + * @background_color_property: Optional CRTC property to set the + * background color. + */ + struct drm_property *background_color_property; /** * @suggested_x_property: Optional connector property with a hint for diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 3693d82b5279..a4bdc4bd11bc 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -27,6 +27,9 @@ #ifndef _DRM_MODE_H #define _DRM_MODE_H +#include +#include + #include "drm.h" #if defined(__cplusplus) @@ -1549,6 +1552,83 @@ struct drm_mode_closefb { __u32 pad; }; +/* + * Put 16-bit ARGB values into a standard 64-bit representation that can be + * used for ioctl parameters, inter-driver communication, etc. + * + * If the component values being provided contain less than 16 bits of + * precision, use a conversion ratio to get a better color approximation. + * The ratio is computed as (2^16 - 1) / (2^bpc - 1), where bpc and 16 are + * the input and output precision, respectively. + * Also note bpc must be greater than 0. + */ +#define __DRM_ARGB64_PREP(c, shift) \ + (((__u64)(c) & __GENMASK(15, 0)) << (shift)) + +#define __DRM_ARGB64_PREP_BPC(c, shift, bpc) \ +({ \ + __u16 mask = __GENMASK((bpc) - 1, 0); \ + __u16 conv = __KERNEL_DIV_ROUND_CLOSEST((mask & (c)) * \ + __GENMASK(15, 0), mask);\ + __DRM_ARGB64_PREP(conv, shift); \ +}) + +#define DRM_ARGB64_PREP(alpha, red, green, blue) \ +( \ + __DRM_ARGB64_PREP(alpha, 48) | \ + __DRM_ARGB64_PREP(red, 32) | \ + __DRM_ARGB64_PREP(green, 16) | \ + __DRM_ARGB64_PREP(blue, 0) \ +) + +#define DRM_ARGB64_PREP_BPC(alpha, red, green, blue, bpc) \ +({ \ + __typeof__(bpc) __bpc = bpc; \ + __DRM_ARGB64_PREP_BPC(alpha, 48, __bpc) | \ + __DRM_ARGB64_PREP_BPC(red, 32, __bpc) | \ + __DRM_ARGB64_PREP_BPC(green, 16, __bpc) | \ + __DRM_ARGB64_PREP_BPC(blue, 0, __bpc); \ +}) + +/* + * Extract the specified color component from a standard 64-bit ARGB value. + * + * If the requested precision is less than 16 bits, make use of a conversion + * ratio calculated as (2^bpc - 1) / (2^16 - 1), where bpc and 16 are the + * output and input precision, respectively. + * + * If speed is more important than accuracy, use DRM_ARGB64_GET*_BPCS() + * instead of DRM_ARGB64_GET*_BPC() in order to replace the expensive + * division with a simple bit right-shift operation. + */ +#define __DRM_ARGB64_GET(c, shift) \ + ((__u16)(((__u64)(c) >> (shift)) & __GENMASK(15, 0))) + +#define __DRM_ARGB64_GET_BPC(c, shift, bpc) \ +({ \ + __u16 comp = __DRM_ARGB64_GET(c, shift); \ + __KERNEL_DIV_ROUND_CLOSEST(comp * __GENMASK((bpc) - 1, 0), \ + __GENMASK(15, 0)); \ +}) + +#define __DRM_ARGB64_GET_BPCS(c, shift, bpc) \ + (__DRM_ARGB64_GET(c, shift) >> (16 - (bpc))) + +#define DRM_ARGB64_GETA(c) __DRM_ARGB64_GET(c, 48) +#define DRM_ARGB64_GETR(c) __DRM_ARGB64_GET(c, 32) +#define DRM_ARGB64_GETG(c) __DRM_ARGB64_GET(c, 16) +#define DRM_ARGB64_GETB(c) __DRM_ARGB64_GET(c, 0) + +#define DRM_ARGB64_GETA_BPC(c, bpc) __DRM_ARGB64_GET_BPC(c, 48, bpc) +#define DRM_ARGB64_GETR_BPC(c, bpc) __DRM_ARGB64_GET_BPC(c, 32, bpc) +#define DRM_ARGB64_GETG_BPC(c, bpc) __DRM_ARGB64_GET_BPC(c, 16, bpc) +#define DRM_ARGB64_GETB_BPC(c, bpc) __DRM_ARGB64_GET_BPC(c, 0, bpc) + +#define DRM_ARGB64_GETA_BPCS(c, bpc) __DRM_ARGB64_GET_BPCS(c, 48, bpc) +#define DRM_ARGB64_GETR_BPCS(c, bpc) __DRM_ARGB64_GET_BPCS(c, 32, bpc) +#define DRM_ARGB64_GETG_BPCS(c, bpc) __DRM_ARGB64_GET_BPCS(c, 16, bpc) +#define DRM_ARGB64_GETB_BPCS(c, bpc) __DRM_ARGB64_GET_BPCS(c, 0, bpc) + #if defined(__cplusplus) } #endif -- cgit From d76856beb4a4a6c42244054cd780c00f2d33de4e Mon Sep 17 00:00:00 2001 From: Max Zhen Date: Fri, 20 Mar 2026 14:06:14 -0700 Subject: accel/amdxdna: Refactor GEM BO handling and add helper APIs for address retrieval Refactor amdxdna GEM buffer object (BO) handling to simplify address management and unify BO type semantics. Introduce helper APIs to retrieve commonly used BO addresses: - User virtual address (UVA) - Kernel virtual address (KVA) - Device address (IOVA/PA) These helpers centralize address lookup logic and avoid duplicating BO-specific handling across submission and execution paths. This also improves readability and reduces the risk of inconsistent address handling in future changes. As part of the refactor: - Rename SHMEM BO type to SHARE to better reflect its usage. - Merge CMD BO handling into SHARE, removing special-case logic for command buffers. - Consolidate BO type handling paths to reduce code duplication and simplify maintenance. No functional change is intended. The refactor prepares the driver for future enhancements by providing a cleaner abstraction for BO address management. Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260320210615.1973016-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_ctx.c | 8 +- drivers/accel/amdxdna/aie2_message.c | 33 +-- drivers/accel/amdxdna/amdxdna_ctx.c | 23 +- drivers/accel/amdxdna/amdxdna_ctx.h | 15 +- drivers/accel/amdxdna/amdxdna_gem.c | 400 +++++++++++++++----------------- drivers/accel/amdxdna/amdxdna_gem.h | 32 +-- drivers/accel/amdxdna/amdxdna_pci_drv.c | 2 +- drivers/accel/amdxdna/amdxdna_ubuf.c | 17 +- drivers/accel/amdxdna/amdxdna_ubuf.h | 5 - include/uapi/drm/amdxdna_accel.h | 9 +- 10 files changed, 266 insertions(+), 278 deletions(-) (limited to 'include/uapi') diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index 6292349868c5..66dbbfd322a2 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -79,7 +79,7 @@ static int aie2_hwctx_restart(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hw } ret = aie2_map_host_buf(xdna->dev_handle, hwctx->fw_ctx_id, - amdxdna_obj_dma_addr(hwctx->client, heap), + amdxdna_obj_dma_addr(heap), heap->mem.size); if (ret) { XDNA_ERR(xdna, "Map host buf failed, ret %d", ret); @@ -659,14 +659,14 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx) .size = MAX_CHAIN_CMDBUF_SIZE, }; - abo = amdxdna_drm_alloc_dev_bo(&xdna->ddev, &args, client->filp); + abo = amdxdna_drm_create_dev_bo(&xdna->ddev, &args, client->filp); if (IS_ERR(abo)) { ret = PTR_ERR(abo); goto free_cmd_bufs; } XDNA_DBG(xdna, "Command buf %d addr 0x%llx size 0x%lx", - i, abo->mem.dev_addr, abo->mem.size); + i, amdxdna_gem_dev_addr(abo), abo->mem.size); priv->cmd_buf[i] = abo; } @@ -707,7 +707,7 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx) } ret = aie2_map_host_buf(xdna->dev_handle, hwctx->fw_ctx_id, - amdxdna_obj_dma_addr(hwctx->client, heap), + amdxdna_obj_dma_addr(heap), heap->mem.size); if (ret) { XDNA_ERR(xdna, "Map host buffer failed, ret %d", ret); diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c index 4ec591306854..7e219a5eda56 100644 --- a/drivers/accel/amdxdna/aie2_message.c +++ b/drivers/accel/amdxdna/aie2_message.c @@ -548,10 +548,10 @@ int aie2_config_cu(struct amdxdna_hwctx *hwctx, } req.cfgs[i] = FIELD_PREP(AIE2_MSG_CFG_CU_PDI_ADDR, - abo->mem.dev_addr >> shift); + amdxdna_gem_dev_addr(abo) >> shift); req.cfgs[i] |= FIELD_PREP(AIE2_MSG_CFG_CU_FUNC, cu->cu_func); XDNA_DBG(xdna, "CU %d full addr 0x%llx, cfg 0x%x", i, - abo->mem.dev_addr, req.cfgs[i]); + amdxdna_gem_dev_addr(abo), req.cfgs[i]); drm_gem_object_put(gobj); } req.num_cus = hwctx->cus->num_cus; @@ -998,6 +998,7 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx, struct mailbox_channel *chann = hwctx->priv->mbox_chann; struct amdxdna_client *client = hwctx->client; struct amdxdna_gem_obj *cmd_abo = job->cmd_bo; + void *cmd_buf = amdxdna_gem_vmap(cmdbuf_abo); struct amdxdna_dev *xdna = client->xdna; struct amdxdna_cmd_chain *payload; struct xdna_mailbox_msg msg; @@ -1009,6 +1010,9 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx, u32 op; u32 i; + if (!cmd_buf) + return -ENOMEM; + op = amdxdna_cmd_get_op(cmd_abo); payload = amdxdna_cmd_get_payload(cmd_abo, &payload_len); if (op != ERT_CMD_CHAIN) { @@ -1032,15 +1036,14 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx, u32 boh = (u32)(payload->data[i]); struct amdxdna_gem_obj *abo; - abo = amdxdna_gem_get_obj(client, boh, AMDXDNA_BO_CMD); + abo = amdxdna_gem_get_obj(client, boh, AMDXDNA_BO_SHARE); if (!abo) { XDNA_ERR(xdna, "Failed to find cmd BO %d", boh); return -ENOENT; } size = cmdbuf_abo->mem.size - offset; - ret = aie2_cmdlist_fill_slot(cmdbuf_abo->mem.kva + offset, - abo, &size, &op); + ret = aie2_cmdlist_fill_slot(cmd_buf + offset, abo, &size, &op); amdxdna_gem_put_obj(abo); if (ret) return ret; @@ -1050,16 +1053,16 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx, XDNA_DBG(xdna, "Total %d commands:", ccnt); print_hex_dump_debug("cmdbufs: ", DUMP_PREFIX_OFFSET, 16, 4, - cmdbuf_abo->mem.kva, offset, false); + cmd_buf, offset, false); msg.opcode = EXEC_MSG_OPS(xdna)->get_chain_msg_op(op); if (msg.opcode == MSG_OP_MAX_OPCODE) return -EOPNOTSUPP; /* The offset is the accumulated total size of the cmd buffer */ - EXEC_MSG_OPS(xdna)->init_chain_req(&req, cmdbuf_abo->mem.dev_addr, + EXEC_MSG_OPS(xdna)->init_chain_req(&req, amdxdna_gem_dev_addr(cmdbuf_abo), offset, ccnt); - drm_clflush_virt_range(cmdbuf_abo->mem.kva, offset); + drm_clflush_virt_range(cmd_buf, offset); msg.handle = job; msg.notify_cb = notify_cb; @@ -1084,27 +1087,29 @@ int aie2_cmdlist_single_execbuf(struct amdxdna_hwctx *hwctx, struct mailbox_channel *chann = hwctx->priv->mbox_chann; struct amdxdna_dev *xdna = hwctx->client->xdna; struct amdxdna_gem_obj *cmd_abo = job->cmd_bo; + void *cmd_buf = amdxdna_gem_vmap(cmdbuf_abo); struct xdna_mailbox_msg msg; union exec_chain_req req; u32 op = ERT_INVALID_CMD; size_t size; int ret; + if (!cmd_buf) + return -ENOMEM; + size = cmdbuf_abo->mem.size; - ret = aie2_cmdlist_fill_slot(cmdbuf_abo->mem.kva, cmd_abo, &size, &op); + ret = aie2_cmdlist_fill_slot(cmd_buf, cmd_abo, &size, &op); if (ret) return ret; - print_hex_dump_debug("cmdbuf: ", DUMP_PREFIX_OFFSET, 16, 4, - cmdbuf_abo->mem.kva, size, false); + print_hex_dump_debug("cmdbuf: ", DUMP_PREFIX_OFFSET, 16, 4, cmd_buf, size, false); msg.opcode = EXEC_MSG_OPS(xdna)->get_chain_msg_op(op); if (msg.opcode == MSG_OP_MAX_OPCODE) return -EOPNOTSUPP; - EXEC_MSG_OPS(xdna)->init_chain_req(&req, cmdbuf_abo->mem.dev_addr, - size, 1); - drm_clflush_virt_range(cmdbuf_abo->mem.kva, size); + EXEC_MSG_OPS(xdna)->init_chain_req(&req, amdxdna_gem_dev_addr(cmdbuf_abo), size, 1); + drm_clflush_virt_range(cmd_buf, size); msg.handle = job; msg.notify_cb = notify_cb; diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c index 4b921715176d..55598343b422 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.c +++ b/drivers/accel/amdxdna/amdxdna_ctx.c @@ -94,9 +94,12 @@ int amdxdna_hwctx_walk(struct amdxdna_client *client, void *arg, void *amdxdna_cmd_get_payload(struct amdxdna_gem_obj *abo, u32 *size) { - struct amdxdna_cmd *cmd = abo->mem.kva; + struct amdxdna_cmd *cmd = amdxdna_gem_vmap(abo); u32 num_masks, count; + if (!cmd) + return NULL; + if (amdxdna_cmd_get_op(abo) == ERT_CMD_CHAIN) num_masks = 0; else @@ -118,10 +121,13 @@ void *amdxdna_cmd_get_payload(struct amdxdna_gem_obj *abo, u32 *size) u32 amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo) { - struct amdxdna_cmd *cmd = abo->mem.kva; + struct amdxdna_cmd *cmd = amdxdna_gem_vmap(abo); u32 num_masks, i; u32 *cu_mask; + if (!cmd) + return INVALID_CU_IDX; + if (amdxdna_cmd_get_op(abo) == ERT_CMD_CHAIN) return INVALID_CU_IDX; @@ -141,19 +147,24 @@ int amdxdna_cmd_set_error(struct amdxdna_gem_obj *abo, void *err_data, size_t size) { struct amdxdna_client *client = job->hwctx->client; - struct amdxdna_cmd *cmd = abo->mem.kva; + struct amdxdna_cmd *cmd = amdxdna_gem_vmap(abo); struct amdxdna_cmd_chain *cc = NULL; + if (!cmd) + return -ENOMEM; + cmd->header &= ~AMDXDNA_CMD_STATE; cmd->header |= FIELD_PREP(AMDXDNA_CMD_STATE, error_state); if (amdxdna_cmd_get_op(abo) == ERT_CMD_CHAIN) { cc = amdxdna_cmd_get_payload(abo, NULL); cc->error_index = (cmd_idx < cc->command_count) ? cmd_idx : 0; - abo = amdxdna_gem_get_obj(client, cc->data[0], AMDXDNA_BO_CMD); + abo = amdxdna_gem_get_obj(client, cc->data[0], AMDXDNA_BO_SHARE); if (!abo) return -EINVAL; - cmd = abo->mem.kva; + cmd = amdxdna_gem_vmap(abo); + if (!cmd) + return -ENOMEM; } memset(cmd->data, 0xff, abo->mem.size - sizeof(*cmd)); @@ -472,7 +483,7 @@ int amdxdna_cmd_submit(struct amdxdna_client *client, job->drv_cmd = drv_cmd; if (cmd_bo_hdl != AMDXDNA_INVALID_BO_HANDLE) { - job->cmd_bo = amdxdna_gem_get_obj(client, cmd_bo_hdl, AMDXDNA_BO_CMD); + job->cmd_bo = amdxdna_gem_get_obj(client, cmd_bo_hdl, AMDXDNA_BO_SHARE); if (!job->cmd_bo) { XDNA_ERR(xdna, "Failed to get cmd bo from %d", cmd_bo_hdl); ret = -EINVAL; diff --git a/drivers/accel/amdxdna/amdxdna_ctx.h b/drivers/accel/amdxdna/amdxdna_ctx.h index 57db1527a93b..a8557d7e8923 100644 --- a/drivers/accel/amdxdna/amdxdna_ctx.h +++ b/drivers/accel/amdxdna/amdxdna_ctx.h @@ -158,7 +158,10 @@ struct amdxdna_sched_job { static inline u32 amdxdna_cmd_get_op(struct amdxdna_gem_obj *abo) { - struct amdxdna_cmd *cmd = abo->mem.kva; + struct amdxdna_cmd *cmd = amdxdna_gem_vmap(abo); + + if (!cmd) + return ERT_INVALID_CMD; return FIELD_GET(AMDXDNA_CMD_OPCODE, cmd->header); } @@ -166,7 +169,10 @@ amdxdna_cmd_get_op(struct amdxdna_gem_obj *abo) static inline void amdxdna_cmd_set_state(struct amdxdna_gem_obj *abo, enum ert_cmd_state s) { - struct amdxdna_cmd *cmd = abo->mem.kva; + struct amdxdna_cmd *cmd = amdxdna_gem_vmap(abo); + + if (!cmd) + return; cmd->header &= ~AMDXDNA_CMD_STATE; cmd->header |= FIELD_PREP(AMDXDNA_CMD_STATE, s); @@ -175,7 +181,10 @@ amdxdna_cmd_set_state(struct amdxdna_gem_obj *abo, enum ert_cmd_state s) static inline enum ert_cmd_state amdxdna_cmd_get_state(struct amdxdna_gem_obj *abo) { - struct amdxdna_cmd *cmd = abo->mem.kva; + struct amdxdna_cmd *cmd = amdxdna_gem_vmap(abo); + + if (!cmd) + return ERT_CMD_STATE_INVALID; return FIELD_GET(AMDXDNA_CMD_STATE, cmd->header); } diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index d80cf164740c..27712704e42d 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -30,7 +30,6 @@ amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo) struct amdxdna_dev *xdna = client->xdna; struct amdxdna_mem *mem = &abo->mem; struct amdxdna_gem_obj *heap; - u64 offset; u32 align; int ret; @@ -42,7 +41,7 @@ amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo) goto unlock_out; } - if (heap->mem.userptr == AMDXDNA_INVALID_ADDR) { + if (amdxdna_gem_uva(heap) == AMDXDNA_INVALID_ADDR) { XDNA_ERR(xdna, "Invalid dev heap userptr"); ret = -EINVAL; goto unlock_out; @@ -64,11 +63,6 @@ amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo) goto unlock_out; } - mem->dev_addr = abo->mm_node.start; - offset = mem->dev_addr - heap->mem.dev_addr; - mem->userptr = heap->mem.userptr + offset; - mem->kva = heap->mem.kva + offset; - drm_gem_object_get(to_gobj(heap)); unlock_out: @@ -77,13 +71,6 @@ unlock_out: return ret; } -static void -amdxdna_gem_destroy_obj(struct amdxdna_gem_obj *abo) -{ - mutex_destroy(&abo->lock); - kfree(abo); -} - static void amdxdna_gem_heap_free(struct amdxdna_gem_obj *abo) { @@ -99,6 +86,105 @@ amdxdna_gem_heap_free(struct amdxdna_gem_obj *abo) mutex_unlock(&abo->client->mm_lock); } +static struct amdxdna_gem_obj * +amdxdna_gem_create_obj(struct drm_device *dev, size_t size) +{ + struct amdxdna_gem_obj *abo; + + abo = kzalloc_obj(*abo); + if (!abo) + return ERR_PTR(-ENOMEM); + + abo->pinned = false; + abo->assigned_hwctx = AMDXDNA_INVALID_CTX_HANDLE; + mutex_init(&abo->lock); + + abo->mem.dma_addr = AMDXDNA_INVALID_ADDR; + abo->mem.uva = AMDXDNA_INVALID_ADDR; + abo->mem.size = size; + INIT_LIST_HEAD(&abo->mem.umap_list); + + return abo; +} + +static void +amdxdna_gem_destroy_obj(struct amdxdna_gem_obj *abo) +{ + mutex_destroy(&abo->lock); + kfree(abo); +} + +/* + * Obtains a kernel virtual address on the BO (usually of small size). + * The mapping is established on the first call and stays valid until + * amdxdna_gem_vunmap() is called. + */ +void *amdxdna_gem_vmap(struct amdxdna_gem_obj *abo) +{ + struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL); + int ret; + + if (abo->mem.kva) + return abo->mem.kva; + + /* The first call to get the kva, taking slow path. */ + guard(mutex)(&abo->lock); + + if (!abo->mem.kva) { + ret = drm_gem_vmap(to_gobj(abo), &map); + if (ret) + XDNA_ERR(abo->client->xdna, "Vmap bo failed, ret %d", ret); + else + abo->mem.kva = map.vaddr; + } + return abo->mem.kva; +} + +/* + * Free mapping established through amdxdna_gem_vmap() + */ +static void amdxdna_gem_vunmap(struct amdxdna_gem_obj *abo) +{ + guard(mutex)(&abo->lock); + + if (abo->mem.kva) { + struct iosys_map map = IOSYS_MAP_INIT_VADDR(abo->mem.kva); + + drm_gem_vunmap(to_gobj(abo), &map); + abo->mem.kva = NULL; + } +} + +/* + * Obtain the user virtual address for accessing the BO. + * It can be used for device to access the BO when PASID is enabled. + */ +u64 amdxdna_gem_uva(struct amdxdna_gem_obj *abo) +{ + if (abo->type == AMDXDNA_BO_DEV) { + struct amdxdna_gem_obj *heap = abo->client->dev_heap; + u64 off = amdxdna_dev_bo_offset(abo); + + if (amdxdna_gem_uva(heap) != AMDXDNA_INVALID_ADDR) + return amdxdna_gem_uva(heap) + off; + return AMDXDNA_INVALID_ADDR; + } + + return abo->mem.uva; +} + +/* + * Obtain the address for device to access the BO. + */ +u64 amdxdna_gem_dev_addr(struct amdxdna_gem_obj *abo) +{ + if (abo->type == AMDXDNA_BO_DEV_HEAP) + return abo->client->xdna->dev_info->dev_mem_base; + if (abo->type == AMDXDNA_BO_DEV) + return abo->mm_node.start; + return amdxdna_obj_dma_addr(abo); +} + static bool amdxdna_hmm_invalidate(struct mmu_interval_notifier *mni, const struct mmu_notifier_range *range, unsigned long cur_seq) @@ -161,16 +247,19 @@ static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo, static void amdxdna_umap_release(struct kref *ref) { struct amdxdna_umap *mapp = container_of(ref, struct amdxdna_umap, refcnt); + struct amdxdna_gem_obj *abo = mapp->abo; struct vm_area_struct *vma = mapp->vma; struct amdxdna_dev *xdna; mmu_interval_notifier_remove(&mapp->notifier); - if (is_import_bo(mapp->abo) && vma->vm_file && vma->vm_file->f_mapping) + if (is_import_bo(abo) && vma->vm_file && vma->vm_file->f_mapping) mapping_clear_unevictable(vma->vm_file->f_mapping); xdna = to_xdna_dev(to_gobj(mapp->abo)->dev); down_write(&xdna->notifier_lock); list_del(&mapp->node); + if (list_empty(&abo->mem.umap_list)) + abo->mem.uva = AMDXDNA_INVALID_ADDR; up_write(&xdna->notifier_lock); kvfree(mapp->range.hmm_pfns); @@ -232,13 +321,13 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, mapp->abo = abo; kref_init(&mapp->refcnt); - if (abo->mem.userptr == AMDXDNA_INVALID_ADDR) - abo->mem.userptr = addr; INIT_WORK(&mapp->hmm_unreg_work, amdxdna_hmm_unreg_work); if (is_import_bo(abo) && vma->vm_file && vma->vm_file->f_mapping) mapping_set_unevictable(vma->vm_file->f_mapping); down_write(&xdna->notifier_lock); + if (list_empty(&abo->mem.umap_list)) + abo->mem.uva = addr; list_add_tail(&mapp->node, &abo->mem.umap_list); up_write(&xdna->notifier_lock); @@ -256,10 +345,11 @@ static void amdxdna_gem_dev_obj_free(struct drm_gem_object *gobj) struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev); struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); - XDNA_DBG(xdna, "BO type %d xdna_addr 0x%llx", abo->type, abo->mem.dev_addr); + XDNA_DBG(xdna, "BO type %d xdna_addr 0x%llx", abo->type, amdxdna_gem_dev_addr(abo)); if (abo->pinned) amdxdna_gem_unpin(abo); + amdxdna_gem_vunmap(abo); amdxdna_gem_heap_free(abo); drm_gem_object_release(gobj); amdxdna_gem_destroy_obj(abo); @@ -390,35 +480,6 @@ static const struct dma_buf_ops amdxdna_dmabuf_ops = { .vunmap = drm_gem_dmabuf_vunmap, }; -static int amdxdna_gem_obj_vmap(struct amdxdna_gem_obj *abo, void **vaddr) -{ - struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL); - int ret; - - if (is_import_bo(abo)) - ret = dma_buf_vmap_unlocked(abo->dma_buf, &map); - else - ret = drm_gem_vmap(to_gobj(abo), &map); - - *vaddr = map.vaddr; - return ret; -} - -static void amdxdna_gem_obj_vunmap(struct amdxdna_gem_obj *abo) -{ - struct iosys_map map; - - if (!abo->mem.kva) - return; - - iosys_map_set_vaddr(&map, abo->mem.kva); - - if (is_import_bo(abo)) - dma_buf_vunmap_unlocked(abo->dma_buf, &map); - else - drm_gem_vunmap(to_gobj(abo), &map); -} - static struct dma_buf *amdxdna_gem_prime_export(struct drm_gem_object *gobj, int flags) { struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); @@ -452,7 +513,7 @@ static void amdxdna_gem_obj_free(struct drm_gem_object *gobj) struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev); struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); - XDNA_DBG(xdna, "BO type %d xdna_addr 0x%llx", abo->type, abo->mem.dev_addr); + XDNA_DBG(xdna, "BO type %d xdna_addr 0x%llx", abo->type, amdxdna_gem_dev_addr(abo)); amdxdna_hmm_unregister(abo, NULL); flush_workqueue(xdna->notifier_wq); @@ -463,15 +524,16 @@ static void amdxdna_gem_obj_free(struct drm_gem_object *gobj) if (abo->type == AMDXDNA_BO_DEV_HEAP) drm_mm_takedown(&abo->mm); - amdxdna_gem_obj_vunmap(abo); + if (amdxdna_iova_on(xdna)) + amdxdna_iommu_unmap_bo(xdna, abo); + + amdxdna_gem_vunmap(abo); mutex_destroy(&abo->lock); - if (is_import_bo(abo)) { + if (is_import_bo(abo)) amdxdna_imported_obj_free(abo); - return; - } - - drm_gem_shmem_free(&abo->base); + else + drm_gem_shmem_free(&abo->base); } static int amdxdna_gem_obj_open(struct drm_gem_object *gobj, struct drm_file *filp) @@ -481,43 +543,38 @@ static int amdxdna_gem_obj_open(struct drm_gem_object *gobj, struct drm_file *fi int ret; guard(mutex)(&abo->lock); - if (abo->ref) { - abo->ref++; - return 0; - } + if (!abo->client) + abo->client = filp->driver_priv; if (amdxdna_iova_on(xdna)) { ret = amdxdna_iommu_map_bo(xdna, abo); if (ret) return ret; } - abo->ref++; return 0; } -static void amdxdna_gem_obj_close(struct drm_gem_object *gobj, struct drm_file *filp) +static int amdxdna_gem_dev_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map) { - struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev); - struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); + struct amdxdna_gem_obj *abo = to_xdna_obj(obj); + void *base = amdxdna_gem_vmap(abo->client->dev_heap); + u64 offset = amdxdna_dev_bo_offset(abo); - guard(mutex)(&abo->lock); - abo->ref--; - if (abo->ref) - return; - - if (amdxdna_iova_on(xdna)) - amdxdna_iommu_unmap_bo(xdna, abo); + if (!base) + return -ENOMEM; + iosys_map_set_vaddr(map, base + offset); + return 0; } static const struct drm_gem_object_funcs amdxdna_gem_dev_obj_funcs = { .free = amdxdna_gem_dev_obj_free, + .vmap = amdxdna_gem_dev_obj_vmap, }; static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = { .free = amdxdna_gem_obj_free, .open = amdxdna_gem_obj_open, - .close = amdxdna_gem_obj_close, .print_info = drm_gem_shmem_object_print_info, .pin = drm_gem_shmem_object_pin, .unpin = drm_gem_shmem_object_unpin, @@ -529,31 +586,9 @@ static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = { .export = amdxdna_gem_prime_export, }; -static struct amdxdna_gem_obj * -amdxdna_gem_create_obj(struct drm_device *dev, size_t size) -{ - struct amdxdna_gem_obj *abo; - - abo = kzalloc_obj(*abo); - if (!abo) - return ERR_PTR(-ENOMEM); - - abo->pinned = false; - abo->assigned_hwctx = AMDXDNA_INVALID_CTX_HANDLE; - mutex_init(&abo->lock); - - abo->mem.userptr = AMDXDNA_INVALID_ADDR; - abo->mem.dev_addr = AMDXDNA_INVALID_ADDR; - abo->mem.dma_addr = AMDXDNA_INVALID_ADDR; - abo->mem.size = size; - INIT_LIST_HEAD(&abo->mem.umap_list); - - return abo; -} - /* For drm_driver->gem_create_object callback */ struct drm_gem_object * -amdxdna_gem_create_object_cb(struct drm_device *dev, size_t size) +amdxdna_gem_create_shmem_object_cb(struct drm_device *dev, size_t size) { struct amdxdna_gem_obj *abo; @@ -567,8 +602,9 @@ amdxdna_gem_create_object_cb(struct drm_device *dev, size_t size) } static struct amdxdna_gem_obj * -amdxdna_gem_create_shmem_object(struct drm_device *dev, size_t size) +amdxdna_gem_create_shmem_object(struct drm_device *dev, struct amdxdna_drm_create_bo *args) { + size_t size = args->size; struct drm_gem_shmem_object *shmem = drm_gem_shmem_create(dev, size); if (IS_ERR(shmem)) @@ -582,7 +618,6 @@ static struct amdxdna_gem_obj * amdxdna_gem_create_ubuf_object(struct drm_device *dev, struct amdxdna_drm_create_bo *args) { struct amdxdna_dev *xdna = to_xdna_dev(dev); - enum amdxdna_ubuf_flag flags = 0; struct amdxdna_drm_va_tbl va_tbl; struct drm_gem_object *gobj; struct dma_buf *dma_buf; @@ -593,10 +628,7 @@ amdxdna_gem_create_ubuf_object(struct drm_device *dev, struct amdxdna_drm_create } if (va_tbl.num_entries) { - if (args->type == AMDXDNA_BO_CMD) - flags |= AMDXDNA_UBUF_FLAG_MAP_DMA; - - dma_buf = amdxdna_get_ubuf(dev, flags, va_tbl.num_entries, + dma_buf = amdxdna_get_ubuf(dev, va_tbl.num_entries, u64_to_user_ptr(args->vaddr + sizeof(va_tbl))); } else { dma_buf = dma_buf_get(va_tbl.dmabuf_fd); @@ -616,18 +648,6 @@ amdxdna_gem_create_ubuf_object(struct drm_device *dev, struct amdxdna_drm_create return to_xdna_obj(gobj); } -static struct amdxdna_gem_obj * -amdxdna_gem_create_object(struct drm_device *dev, - struct amdxdna_drm_create_bo *args) -{ - size_t aligned_sz = PAGE_ALIGN(args->size); - - if (args->vaddr) - return amdxdna_gem_create_ubuf_object(dev, args); - - return amdxdna_gem_create_shmem_object(dev, aligned_sz); -} - struct drm_gem_object * amdxdna_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf) { @@ -660,7 +680,8 @@ amdxdna_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf) abo = to_xdna_obj(gobj); abo->attach = attach; abo->dma_buf = dma_buf; - abo->type = AMDXDNA_BO_SHMEM; + abo->type = AMDXDNA_BO_SHARE; + gobj->resv = dma_buf->resv; return gobj; @@ -675,92 +696,92 @@ put_buf: } static struct amdxdna_gem_obj * -amdxdna_drm_alloc_shmem(struct drm_device *dev, - struct amdxdna_drm_create_bo *args, - struct drm_file *filp) +amdxdna_drm_create_share_bo(struct drm_device *dev, + struct amdxdna_drm_create_bo *args, struct drm_file *filp) { - struct amdxdna_client *client = filp->driver_priv; struct amdxdna_gem_obj *abo; - abo = amdxdna_gem_create_object(dev, args); + if (args->vaddr) + abo = amdxdna_gem_create_ubuf_object(dev, args); + else + abo = amdxdna_gem_create_shmem_object(dev, args); if (IS_ERR(abo)) return ERR_CAST(abo); - abo->client = client; - abo->type = AMDXDNA_BO_SHMEM; + if (args->type == AMDXDNA_BO_DEV_HEAP) + abo->type = AMDXDNA_BO_DEV_HEAP; + else + abo->type = AMDXDNA_BO_SHARE; return abo; } static struct amdxdna_gem_obj * -amdxdna_drm_create_dev_heap(struct drm_device *dev, - struct amdxdna_drm_create_bo *args, - struct drm_file *filp) +amdxdna_drm_create_dev_heap_bo(struct drm_device *dev, + struct amdxdna_drm_create_bo *args, struct drm_file *filp) { struct amdxdna_client *client = filp->driver_priv; struct amdxdna_dev *xdna = to_xdna_dev(dev); struct amdxdna_gem_obj *abo; int ret; - if (args->size > xdna->dev_info->dev_mem_size) { - XDNA_DBG(xdna, "Invalid dev heap size 0x%llx, limit 0x%lx", + WARN_ON(!is_power_of_2(xdna->dev_info->dev_mem_size)); + XDNA_DBG(xdna, "Requested dev heap size 0x%llx", args->size); + if (!args->size || !IS_ALIGNED(args->size, xdna->dev_info->dev_mem_size)) { + XDNA_ERR(xdna, "The dev heap size 0x%llx is not multiple of 0x%lx", args->size, xdna->dev_info->dev_mem_size); return ERR_PTR(-EINVAL); } + /* HEAP BO is a special case of SHARE BO. */ + abo = amdxdna_drm_create_share_bo(dev, args, filp); + if (IS_ERR(abo)) + return ERR_CAST(abo); + + /* Set up heap for this client. */ mutex_lock(&client->mm_lock); + if (client->dev_heap) { XDNA_DBG(client->xdna, "dev heap is already created"); ret = -EBUSY; goto mm_unlock; } - - abo = amdxdna_gem_create_object(dev, args); - if (IS_ERR(abo)) { - ret = PTR_ERR(abo); - goto mm_unlock; - } - - abo->type = AMDXDNA_BO_DEV_HEAP; - abo->client = client; - abo->mem.dev_addr = client->xdna->dev_info->dev_mem_base; - drm_mm_init(&abo->mm, abo->mem.dev_addr, abo->mem.size); - - ret = amdxdna_gem_obj_vmap(abo, &abo->mem.kva); - if (ret) { - XDNA_ERR(xdna, "Vmap heap bo failed, ret %d", ret); - goto release_obj; - } - client->dev_heap = abo; drm_gem_object_get(to_gobj(abo)); + + drm_mm_init(&abo->mm, xdna->dev_info->dev_mem_base, abo->mem.size); + mutex_unlock(&client->mm_lock); return abo; -release_obj: - drm_gem_object_put(to_gobj(abo)); mm_unlock: mutex_unlock(&client->mm_lock); + drm_gem_object_put(to_gobj(abo)); return ERR_PTR(ret); } struct amdxdna_gem_obj * -amdxdna_drm_alloc_dev_bo(struct drm_device *dev, - struct amdxdna_drm_create_bo *args, - struct drm_file *filp) +amdxdna_drm_create_dev_bo(struct drm_device *dev, + struct amdxdna_drm_create_bo *args, struct drm_file *filp) { + size_t aligned_sz = PAGE_ALIGN(args->size); struct amdxdna_client *client = filp->driver_priv; struct amdxdna_dev *xdna = to_xdna_dev(dev); - size_t aligned_sz = PAGE_ALIGN(args->size); struct amdxdna_gem_obj *abo; + struct drm_gem_object *gobj; int ret; - abo = amdxdna_gem_create_obj(&xdna->ddev, aligned_sz); + if (!aligned_sz) { + XDNA_ERR(xdna, "Invalid BO size 0x%llx", args->size); + return ERR_PTR(-EINVAL); + } + + abo = amdxdna_gem_create_obj(dev, aligned_sz); if (IS_ERR(abo)) return abo; - - to_gobj(abo)->funcs = &amdxdna_gem_dev_obj_funcs; + gobj = to_gobj(abo); + gobj->funcs = &amdxdna_gem_dev_obj_funcs; abo->type = AMDXDNA_BO_DEV; abo->client = client; @@ -770,31 +791,7 @@ amdxdna_drm_alloc_dev_bo(struct drm_device *dev, amdxdna_gem_destroy_obj(abo); return ERR_PTR(ret); } - - drm_gem_private_object_init(&xdna->ddev, to_gobj(abo), aligned_sz); - - return abo; -} - -static struct amdxdna_gem_obj * -amdxdna_drm_create_cmd_bo(struct drm_device *dev, - struct amdxdna_drm_create_bo *args, - struct drm_file *filp) -{ - struct amdxdna_dev *xdna = to_xdna_dev(dev); - struct amdxdna_gem_obj *abo; - - if (args->size < sizeof(struct amdxdna_cmd)) { - XDNA_DBG(xdna, "Command BO size 0x%llx too small", args->size); - return ERR_PTR(-EINVAL); - } - - abo = amdxdna_gem_create_object(dev, args); - if (IS_ERR(abo)) - return ERR_CAST(abo); - - abo->type = AMDXDNA_BO_CMD; - abo->client = filp->driver_priv; + drm_gem_private_object_init(dev, gobj, aligned_sz); return abo; } @@ -812,17 +809,16 @@ int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_f XDNA_DBG(xdna, "BO arg type %d vaddr 0x%llx size 0x%llx flags 0x%llx", args->type, args->vaddr, args->size, args->flags); switch (args->type) { - case AMDXDNA_BO_SHMEM: - abo = amdxdna_drm_alloc_shmem(dev, args, filp); + case AMDXDNA_BO_CMD: + fallthrough; + case AMDXDNA_BO_SHARE: + abo = amdxdna_drm_create_share_bo(dev, args, filp); break; case AMDXDNA_BO_DEV_HEAP: - abo = amdxdna_drm_create_dev_heap(dev, args, filp); + abo = amdxdna_drm_create_dev_heap_bo(dev, args, filp); break; case AMDXDNA_BO_DEV: - abo = amdxdna_drm_alloc_dev_bo(dev, args, filp); - break; - case AMDXDNA_BO_CMD: - abo = amdxdna_drm_create_cmd_bo(dev, args, filp); + abo = amdxdna_drm_create_dev_bo(dev, args, filp); break; default: return -EINVAL; @@ -838,8 +834,8 @@ int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_f } XDNA_DBG(xdna, "BO hdl %d type %d userptr 0x%llx xdna_addr 0x%llx size 0x%lx", - args->handle, args->type, abo->mem.userptr, - abo->mem.dev_addr, abo->mem.size); + args->handle, args->type, amdxdna_gem_uva(abo), + amdxdna_gem_dev_addr(abo), abo->mem.size); put_obj: /* Dereference object reference. Handle holds it now. */ drm_gem_object_put(to_gobj(abo)); @@ -890,38 +886,19 @@ void amdxdna_gem_unpin(struct amdxdna_gem_obj *abo) struct amdxdna_gem_obj *amdxdna_gem_get_obj(struct amdxdna_client *client, u32 bo_hdl, u8 bo_type) { - struct amdxdna_dev *xdna = client->xdna; struct amdxdna_gem_obj *abo; struct drm_gem_object *gobj; - int ret; gobj = drm_gem_object_lookup(client->filp, bo_hdl); if (!gobj) { - XDNA_DBG(xdna, "Can not find bo %d", bo_hdl); + XDNA_DBG(client->xdna, "Can not find bo %d", bo_hdl); return NULL; } abo = to_xdna_obj(gobj); - if (bo_type != AMDXDNA_BO_INVALID && abo->type != bo_type) - goto put_obj; - - if (bo_type != AMDXDNA_BO_CMD || abo->mem.kva) + if (bo_type == AMDXDNA_BO_INVALID || abo->type == bo_type) return abo; - if (abo->mem.size > SZ_32K) { - XDNA_ERR(xdna, "Cmd bo is too big %ld", abo->mem.size); - goto put_obj; - } - - ret = amdxdna_gem_obj_vmap(abo, &abo->mem.kva); - if (ret) { - XDNA_ERR(xdna, "Vmap cmd bo failed, ret %d", ret); - goto put_obj; - } - - return abo; - -put_obj: drm_gem_object_put(gobj); return NULL; } @@ -944,11 +921,8 @@ int amdxdna_drm_get_bo_info_ioctl(struct drm_device *dev, void *data, struct drm } abo = to_xdna_obj(gobj); - args->vaddr = abo->mem.userptr; - if (abo->mem.dev_addr != AMDXDNA_INVALID_ADDR) - args->xdna_addr = abo->mem.dev_addr; - else - args->xdna_addr = abo->mem.dma_addr; + args->vaddr = amdxdna_gem_uva(abo); + args->xdna_addr = amdxdna_gem_dev_addr(abo); if (abo->type != AMDXDNA_BO_DEV) args->map_offset = drm_vma_node_offset_addr(&gobj->vma_node); @@ -993,8 +967,8 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev, if (is_import_bo(abo)) drm_clflush_sg(abo->base.sgt); - else if (abo->mem.kva) - drm_clflush_virt_range(abo->mem.kva + args->offset, args->size); + else if (amdxdna_gem_vmap(abo)) + drm_clflush_virt_range(amdxdna_gem_vmap(abo) + args->offset, args->size); else if (abo->base.pages) drm_clflush_pages(abo->base.pages, gobj->size >> PAGE_SHIFT); else diff --git a/drivers/accel/amdxdna/amdxdna_gem.h b/drivers/accel/amdxdna/amdxdna_gem.h index fbeb622e7cf9..a77d9344f8a4 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.h +++ b/drivers/accel/amdxdna/amdxdna_gem.h @@ -24,15 +24,16 @@ struct amdxdna_umap { }; struct amdxdna_mem { - u64 userptr; void *kva; - u64 dev_addr; u64 dma_addr; size_t size; - struct page **pages; - u32 nr_pages; struct list_head umap_list; bool map_invalid; + /* + * Cache the first mmap uva as PASID addr, which can be accessed by driver + * without taking notifier_lock. + */ + u64 uva; }; struct amdxdna_gem_obj { @@ -40,11 +41,10 @@ struct amdxdna_gem_obj { struct amdxdna_client *client; u8 type; bool pinned; - struct mutex lock; /* Protects: pinned */ + struct mutex lock; /* Protects: pinned, mem.kva */ struct amdxdna_mem mem; - u32 ref; - /* Below members is uninitialized when needed */ + /* Below members are initialized when needed */ struct drm_mm mm; /* For AMDXDNA_BO_DEV_HEAP */ struct drm_mm_node mm_node; /* For AMDXDNA_BO_DEV */ u32 assigned_hwctx; @@ -67,27 +67,29 @@ static inline void amdxdna_gem_put_obj(struct amdxdna_gem_obj *abo) drm_gem_object_put(to_gobj(abo)); } +void *amdxdna_gem_vmap(struct amdxdna_gem_obj *abo); +u64 amdxdna_gem_uva(struct amdxdna_gem_obj *abo); +u64 amdxdna_gem_dev_addr(struct amdxdna_gem_obj *abo); + static inline u64 amdxdna_dev_bo_offset(struct amdxdna_gem_obj *abo) { - return abo->mem.dev_addr - abo->client->dev_heap->mem.dev_addr; + return amdxdna_gem_dev_addr(abo) - amdxdna_gem_dev_addr(abo->client->dev_heap); } -static inline u64 amdxdna_obj_dma_addr(struct amdxdna_client *client, - struct amdxdna_gem_obj *abo) +static inline u64 amdxdna_obj_dma_addr(struct amdxdna_gem_obj *abo) { - return amdxdna_pasid_on(client) ? abo->mem.userptr : abo->mem.dma_addr; + return amdxdna_pasid_on(abo->client) ? amdxdna_gem_uva(abo) : abo->mem.dma_addr; } void amdxdna_umap_put(struct amdxdna_umap *mapp); struct drm_gem_object * -amdxdna_gem_create_object_cb(struct drm_device *dev, size_t size); +amdxdna_gem_create_shmem_object_cb(struct drm_device *dev, size_t size); struct drm_gem_object * amdxdna_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf); struct amdxdna_gem_obj * -amdxdna_drm_alloc_dev_bo(struct drm_device *dev, - struct amdxdna_drm_create_bo *args, - struct drm_file *filp); +amdxdna_drm_create_dev_bo(struct drm_device *dev, + struct amdxdna_drm_create_bo *args, struct drm_file *filp); int amdxdna_gem_pin_nolock(struct amdxdna_gem_obj *abo); int amdxdna_gem_pin(struct amdxdna_gem_obj *abo); diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c b/drivers/accel/amdxdna/amdxdna_pci_drv.c index 5143b8c9b92b..d83be00daf2b 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.c +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c @@ -245,7 +245,7 @@ const struct drm_driver amdxdna_drm_drv = { .ioctls = amdxdna_drm_ioctls, .num_ioctls = ARRAY_SIZE(amdxdna_drm_ioctls), - .gem_create_object = amdxdna_gem_create_object_cb, + .gem_create_object = amdxdna_gem_create_shmem_object_cb, .gem_prime_import = amdxdna_gem_prime_import, }; diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c index fb71d6e3f44d..fb999aa25318 100644 --- a/drivers/accel/amdxdna/amdxdna_ubuf.c +++ b/drivers/accel/amdxdna/amdxdna_ubuf.c @@ -17,7 +17,6 @@ struct amdxdna_ubuf_priv { struct page **pages; u64 nr_pages; - enum amdxdna_ubuf_flag flags; struct mm_struct *mm; }; @@ -37,11 +36,9 @@ static struct sg_table *amdxdna_ubuf_map(struct dma_buf_attachment *attach, if (ret) goto err_free_sg; - if (ubuf->flags & AMDXDNA_UBUF_FLAG_MAP_DMA) { - ret = dma_map_sgtable(attach->dev, sg, direction, 0); - if (ret) - goto err_free_table; - } + ret = dma_map_sgtable(attach->dev, sg, direction, 0); + if (ret) + goto err_free_table; return sg; @@ -56,11 +53,7 @@ static void amdxdna_ubuf_unmap(struct dma_buf_attachment *attach, struct sg_table *sg, enum dma_data_direction direction) { - struct amdxdna_ubuf_priv *ubuf = attach->dmabuf->priv; - - if (ubuf->flags & AMDXDNA_UBUF_FLAG_MAP_DMA) - dma_unmap_sgtable(attach->dev, sg, direction, 0); - + dma_unmap_sgtable(attach->dev, sg, direction, 0); sg_free_table(sg); kfree(sg); } @@ -133,7 +126,6 @@ static const struct dma_buf_ops amdxdna_ubuf_dmabuf_ops = { }; struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, - enum amdxdna_ubuf_flag flags, u32 num_entries, void __user *va_entries) { struct amdxdna_dev *xdna = to_xdna_dev(dev); @@ -152,7 +144,6 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, if (!ubuf) return ERR_PTR(-ENOMEM); - ubuf->flags = flags; ubuf->mm = current->mm; mmgrab(ubuf->mm); diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.h b/drivers/accel/amdxdna/amdxdna_ubuf.h index e5cb3bdb3ec9..8900a6dc4371 100644 --- a/drivers/accel/amdxdna/amdxdna_ubuf.h +++ b/drivers/accel/amdxdna/amdxdna_ubuf.h @@ -8,12 +8,7 @@ #include #include -enum amdxdna_ubuf_flag { - AMDXDNA_UBUF_FLAG_MAP_DMA = 1, -}; - struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev, - enum amdxdna_ubuf_flag flags, u32 num_entries, void __user *va_entries); #endif /* _AMDXDNA_UBUF_H_ */ diff --git a/include/uapi/drm/amdxdna_accel.h b/include/uapi/drm/amdxdna_accel.h index 5bd13f4435f5..bddaaaf945cf 100644 --- a/include/uapi/drm/amdxdna_accel.h +++ b/include/uapi/drm/amdxdna_accel.h @@ -156,10 +156,11 @@ struct amdxdna_drm_config_hwctx { enum amdxdna_bo_type { AMDXDNA_BO_INVALID = 0, - AMDXDNA_BO_SHMEM, - AMDXDNA_BO_DEV_HEAP, - AMDXDNA_BO_DEV, - AMDXDNA_BO_CMD, + AMDXDNA_BO_SHMEM = 1, /* Be compatible with legacy application code. */ + AMDXDNA_BO_SHARE = 1, + AMDXDNA_BO_DEV_HEAP = 2, + AMDXDNA_BO_DEV = 3, + AMDXDNA_BO_CMD = 4, }; /** -- cgit From 4f39a194d41e6b8cb61a91a7bb01b17be59a7d73 Mon Sep 17 00:00:00 2001 From: Tejas Upadhyay Date: Thu, 5 Mar 2026 17:49:06 +0530 Subject: drm/xe/xe3p_lpg: Restrict UAPI to enable L2 flush optimization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When set, starting xe3p_lpg, the L2 flush optimization feature will control whether L2 is in Persistent or Transient mode through monitoring of media activity. To enable L2 flush optimization include new feature flag GUC_CTL_ENABLE_L2FLUSH_OPT for Novalake platforms when media type is detected. Tighten UAPI validation to restrict userptr, svm and dmabuf mappings to be either 2WAY or XA+1WAY V5(Thomas): logic correction V4(MattA): Modify uapi doc and commit V3(MattA): check valid op and pat_index value V2(MattA): validate dma-buf bos and madvise pat-index Acked-by: José Roberto de Souza Acked-by: Michal Mrozek Acked-by: Carl Zhang Reviewed-by: Thomas Hellström Reviewed-by: Matthew Auld Link: https://patch.msgid.link/20260305121902.1892593-9-tejas.upadhyay@intel.com Signed-off-by: Tejas Upadhyay --- drivers/gpu/drm/xe/xe_guc.c | 3 +++ drivers/gpu/drm/xe/xe_guc_fwif.h | 1 + drivers/gpu/drm/xe/xe_vm.c | 8 ++++++++ drivers/gpu/drm/xe/xe_vm_madvise.c | 23 +++++++++++++++++++++++ include/uapi/drm/xe_drm.h | 4 +++- 5 files changed, 38 insertions(+), 1 deletion(-) (limited to 'include/uapi') diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c index 576f3d500390..ccebb437e37f 100644 --- a/drivers/gpu/drm/xe/xe_guc.c +++ b/drivers/gpu/drm/xe/xe_guc.c @@ -98,6 +98,9 @@ static u32 guc_ctl_feature_flags(struct xe_guc *guc) if (xe_guc_using_main_gamctrl_queues(guc)) flags |= GUC_CTL_MAIN_GAMCTRL_QUEUES; + if (GRAPHICS_VER(xe) >= 35 && !IS_DGFX(xe) && xe_gt_is_media_type(guc_to_gt(guc))) + flags |= GUC_CTL_ENABLE_L2FLUSH_OPT; + return flags; } diff --git a/drivers/gpu/drm/xe/xe_guc_fwif.h b/drivers/gpu/drm/xe/xe_guc_fwif.h index bb8f71d38611..b73fae063fac 100644 --- a/drivers/gpu/drm/xe/xe_guc_fwif.h +++ b/drivers/gpu/drm/xe/xe_guc_fwif.h @@ -67,6 +67,7 @@ struct guc_update_exec_queue_policy { #define GUC_CTL_ENABLE_PSMI_LOGGING BIT(7) #define GUC_CTL_MAIN_GAMCTRL_QUEUES BIT(9) #define GUC_CTL_DISABLE_SCHEDULER BIT(14) +#define GUC_CTL_ENABLE_L2FLUSH_OPT BIT(15) #define GUC_CTL_DEBUG 3 #define GUC_LOG_VERBOSITY REG_GENMASK(1, 0) diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index c0d8f5db019d..e24436287786 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -3492,6 +3492,10 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm, op == DRM_XE_VM_BIND_OP_MAP_USERPTR) || XE_IOCTL_DBG(xe, coh_mode == XE_COH_NONE && op == DRM_XE_VM_BIND_OP_MAP_USERPTR) || + XE_IOCTL_DBG(xe, xe_device_is_l2_flush_optimized(xe) && + (op == DRM_XE_VM_BIND_OP_MAP_USERPTR || + is_cpu_addr_mirror) && + (pat_index != 19 && coh_mode != XE_COH_2WAY)) || XE_IOCTL_DBG(xe, comp_en && op == DRM_XE_VM_BIND_OP_MAP_USERPTR) || XE_IOCTL_DBG(xe, op == DRM_XE_VM_BIND_OP_MAP_USERPTR && @@ -3633,6 +3637,10 @@ static int xe_vm_bind_ioctl_validate_bo(struct xe_device *xe, struct xe_bo *bo, if (XE_IOCTL_DBG(xe, bo->ttm.base.import_attach && comp_en)) return -EINVAL; + if (XE_IOCTL_DBG(xe, bo->ttm.base.import_attach && xe_device_is_l2_flush_optimized(xe) && + (pat_index != 19 && coh_mode != XE_COH_2WAY))) + return -EINVAL; + /* If a BO is protected it can only be mapped if the key is still valid */ if ((bind_flags & DRM_XE_VM_BIND_FLAG_CHECK_PXP) && xe_bo_is_protected(bo) && op != DRM_XE_VM_BIND_OP_UNMAP && op != DRM_XE_VM_BIND_OP_UNMAP_ALL) diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c index 431be53be56f..e564b12c02d9 100644 --- a/drivers/gpu/drm/xe/xe_vm_madvise.c +++ b/drivers/gpu/drm/xe/xe_vm_madvise.c @@ -419,6 +419,7 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil struct xe_vmas_in_madvise_range madvise_range = {.addr = args->start, .range = args->range, }; struct xe_madvise_details details; + u16 pat_index, coh_mode; struct xe_vm *vm; struct drm_exec exec; int err, attr_type; @@ -455,6 +456,17 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil if (err || !madvise_range.num_vmas) goto madv_fini; + if (args->type == DRM_XE_MEM_RANGE_ATTR_PAT) { + pat_index = array_index_nospec(args->pat_index.val, xe->pat.n_entries); + coh_mode = xe_pat_index_get_coh_mode(xe, pat_index); + if (XE_IOCTL_DBG(xe, madvise_range.has_svm_userptr_vmas && + xe_device_is_l2_flush_optimized(xe) && + (pat_index != 19 && coh_mode != XE_COH_2WAY))) { + err = -EINVAL; + goto madv_fini; + } + } + if (madvise_range.has_bo_vmas) { if (args->type == DRM_XE_MEM_RANGE_ATTR_ATOMIC) { if (!check_bo_args_are_sane(vm, madvise_range.vmas, @@ -472,6 +484,17 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil if (!bo) continue; + + if (args->type == DRM_XE_MEM_RANGE_ATTR_PAT) { + if (XE_IOCTL_DBG(xe, bo->ttm.base.import_attach && + xe_device_is_l2_flush_optimized(xe) && + (pat_index != 19 && + coh_mode != XE_COH_2WAY))) { + err = -EINVAL; + goto err_fini; + } + } + err = drm_exec_lock_obj(&exec, &bo->ttm.base); drm_exec_retry_on_contention(&exec); if (err) diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index f8b2afb20540..7014dde1c9c4 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -1114,7 +1114,9 @@ struct drm_xe_vm_bind_op { * incoherent GT access is possible. * * Note: For userptr and externally imported dma-buf the kernel expects - * either 1WAY or 2WAY for the @pat_index. + * either 1WAY or 2WAY for the @pat_index. Starting from NVL-P, for + * userptr, svm, madvise and externally imported dma-buf the kernel expects + * either 2WAY or 1WAY and XA @pat_index. * * For DRM_XE_VM_BIND_FLAG_NULL bindings there are no KMD restrictions * on the @pat_index. For such mappings there is no actual memory being -- cgit From 1dfa2fd98f17d639f47ceb1d3b07926131db5c16 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Tue, 24 Mar 2026 14:25:57 +0100 Subject: drm/panthor: extend timestamp query with flags Flags now control which data user space wants to query, there is more information sources, and there's ability to query duration of multiple timestamp reads. New sources: - CPU's monotonic, - CPU's monotonic raw, - GPU's cycle count These changes should make the implementation of VK_KHR_calibrated_timestamps more accurate and much simpler. Signed-off-by: Marcin Slusarz Reviewed-by: Boris Brezillon Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patch.msgid.link/20260324132557.1707286-1-marcin.slusarz@arm.com --- drivers/gpu/drm/panthor/panthor_drv.c | 134 ++++++++++++++++++++++++++++++++-- include/uapi/drm/panthor_drm.h | 63 +++++++++++++++- 2 files changed, 189 insertions(+), 8 deletions(-) (limited to 'include/uapi') diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index 1bcec6a2e3e0..87d27c3c1456 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include #include #include @@ -761,22 +763,135 @@ static void panthor_submit_ctx_cleanup(struct panthor_submit_ctx *ctx, kvfree(ctx->jobs); } +#define VALID_TIMESTAMP_QUERY_FLAGS \ + (DRM_PANTHOR_TIMESTAMP_GPU | \ + DRM_PANTHOR_TIMESTAMP_CPU_TYPE_MASK | \ + DRM_PANTHOR_TIMESTAMP_GPU_OFFSET | \ + DRM_PANTHOR_TIMESTAMP_GPU_CYCLE_COUNT | \ + DRM_PANTHOR_TIMESTAMP_FREQ | \ + DRM_PANTHOR_TIMESTAMP_DURATION) + static int panthor_query_timestamp_info(struct panthor_device *ptdev, struct drm_panthor_timestamp_info *arg) { int ret; + u32 flags; + unsigned long irq_flags; + struct timespec64 cpu_ts; + u64 query_start_time; + bool minimize_interruption; + u32 timestamp_types = 0; + + if (arg->flags != 0) { + flags = arg->flags; + } else { + /* + * If flags are 0, then ask for the same things that we asked + * for before flags were added. + */ + flags = DRM_PANTHOR_TIMESTAMP_GPU | + DRM_PANTHOR_TIMESTAMP_GPU_OFFSET | + DRM_PANTHOR_TIMESTAMP_FREQ; + } + + switch (flags & DRM_PANTHOR_TIMESTAMP_CPU_TYPE_MASK) { + case DRM_PANTHOR_TIMESTAMP_CPU_NONE: + break; + case DRM_PANTHOR_TIMESTAMP_CPU_MONOTONIC: + case DRM_PANTHOR_TIMESTAMP_CPU_MONOTONIC_RAW: + timestamp_types++; + break; + default: + return -EINVAL; + } + + if (flags & ~VALID_TIMESTAMP_QUERY_FLAGS) + return -EINVAL; + + if (flags & DRM_PANTHOR_TIMESTAMP_GPU) + timestamp_types++; + if (flags & DRM_PANTHOR_TIMESTAMP_GPU_CYCLE_COUNT) + timestamp_types++; + + /* If user asked to obtain timestamps from more than one source, + * then it very likely means they want them to be as close as possible. + * If they asked for duration, then that likely means that they + * want to know how long obtaining timestamp takes, without random + * events, like process scheduling or interrupts. + */ + minimize_interruption = + (flags & DRM_PANTHOR_TIMESTAMP_DURATION) || + (timestamp_types >= 2); ret = panthor_device_resume_and_get(ptdev); if (ret) return ret; + if (flags & DRM_PANTHOR_TIMESTAMP_FREQ) { #ifdef CONFIG_ARM_ARCH_TIMER - arg->timestamp_frequency = arch_timer_get_cntfrq(); + arg->timestamp_frequency = arch_timer_get_cntfrq(); #else - arg->timestamp_frequency = 0; + arg->timestamp_frequency = 0; #endif - arg->current_timestamp = gpu_read64_counter(ptdev, GPU_TIMESTAMP); - arg->timestamp_offset = gpu_read64(ptdev, GPU_TIMESTAMP_OFFSET); + } else { + arg->timestamp_frequency = 0; + } + + if (flags & DRM_PANTHOR_TIMESTAMP_GPU_OFFSET) + arg->timestamp_offset = gpu_read64(ptdev, GPU_TIMESTAMP_OFFSET); + else + arg->timestamp_offset = 0; + + if (minimize_interruption) { + preempt_disable(); + local_irq_save(irq_flags); + } + + if (flags & DRM_PANTHOR_TIMESTAMP_DURATION) + query_start_time = local_clock(); + else + query_start_time = 0; + + if (flags & DRM_PANTHOR_TIMESTAMP_GPU) + arg->current_timestamp = gpu_read64_counter(ptdev, GPU_TIMESTAMP); + else + arg->current_timestamp = 0; + + switch (flags & DRM_PANTHOR_TIMESTAMP_CPU_TYPE_MASK) { + case DRM_PANTHOR_TIMESTAMP_CPU_MONOTONIC: + ktime_get_ts64(&cpu_ts); + break; + case DRM_PANTHOR_TIMESTAMP_CPU_MONOTONIC_RAW: + ktime_get_raw_ts64(&cpu_ts); + break; + default: + break; + } + + if (flags & DRM_PANTHOR_TIMESTAMP_GPU_CYCLE_COUNT) + arg->cycle_count = gpu_read64_counter(ptdev, GPU_CYCLE_COUNT); + else + arg->cycle_count = 0; + + if (flags & DRM_PANTHOR_TIMESTAMP_DURATION) + arg->duration_nsec = local_clock() - query_start_time; + else + arg->duration_nsec = 0; + + if (minimize_interruption) { + local_irq_restore(irq_flags); + preempt_enable(); + } + + if (flags & DRM_PANTHOR_TIMESTAMP_CPU_TYPE_MASK) { + timens_add_monotonic(&cpu_ts); + + arg->cpu_timestamp_sec = cpu_ts.tv_sec; + arg->cpu_timestamp_nsec = cpu_ts.tv_nsec; + } else { + arg->cpu_timestamp_sec = 0; + arg->cpu_timestamp_nsec = 0; + } pm_runtime_put(ptdev->base.dev); return 0; @@ -851,8 +966,14 @@ static int panthor_ioctl_dev_query(struct drm_device *ddev, void *data, struct d return PANTHOR_UOBJ_SET(args->pointer, args->size, ptdev->csif_info); case DRM_PANTHOR_DEV_QUERY_TIMESTAMP_INFO: - ret = panthor_query_timestamp_info(ptdev, ×tamp_info); + ret = copy_struct_from_user(×tamp_info, + sizeof(timestamp_info), + u64_to_user_ptr(args->pointer), + args->size); + if (ret) + return ret; + ret = panthor_query_timestamp_info(ptdev, ×tamp_info); if (ret) return ret; @@ -1680,6 +1801,7 @@ static void panthor_debugfs_init(struct drm_minor *minor) * - adds DRM_IOCTL_PANTHOR_BO_SYNC ioctl * - adds DRM_IOCTL_PANTHOR_BO_QUERY_INFO ioctl * - adds drm_panthor_gpu_info::selected_coherency + * - 1.8 - extends DEV_QUERY_TIMESTAMP_INFO with flags */ static const struct drm_driver panthor_drm_driver = { .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ | @@ -1693,7 +1815,7 @@ static const struct drm_driver panthor_drm_driver = { .name = "panthor", .desc = "Panthor DRM driver", .major = 1, - .minor = 7, + .minor = 8, .gem_create_object = panthor_gem_create_object, .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h index b401ac585d6a..0e455d91e77d 100644 --- a/include/uapi/drm/panthor_drm.h +++ b/include/uapi/drm/panthor_drm.h @@ -409,6 +409,38 @@ struct drm_panthor_csif_info { __u32 pad; }; +/** + * enum drm_panthor_timestamp_info_flags - drm_panthor_timestamp_info.flags + */ +enum drm_panthor_timestamp_info_flags { + /** @DRM_PANTHOR_TIMESTAMP_GPU: Query GPU time. */ + DRM_PANTHOR_TIMESTAMP_GPU = 1 << 0, + + /** @DRM_PANTHOR_TIMESTAMP_CPU_NONE: Don't query CPU time. */ + DRM_PANTHOR_TIMESTAMP_CPU_NONE = 0 << 1, + + /** @DRM_PANTHOR_TIMESTAMP_CPU_MONOTONIC: Query CPU time using CLOCK_MONOTONIC. */ + DRM_PANTHOR_TIMESTAMP_CPU_MONOTONIC = 1 << 1, + + /** @DRM_PANTHOR_TIMESTAMP_CPU_MONOTONIC_RAW: Query CPU time using CLOCK_MONOTONIC_RAW. */ + DRM_PANTHOR_TIMESTAMP_CPU_MONOTONIC_RAW = 2 << 1, + + /** @DRM_PANTHOR_TIMESTAMP_CPU_TYPE_MASK: Space reserved for CPU clock type. */ + DRM_PANTHOR_TIMESTAMP_CPU_TYPE_MASK = 7 << 1, + + /** @DRM_PANTHOR_TIMESTAMP_GPU_OFFSET: Query GPU offset. */ + DRM_PANTHOR_TIMESTAMP_GPU_OFFSET = 1 << 4, + + /** @DRM_PANTHOR_TIMESTAMP_GPU_CYCLE_COUNT: Query GPU cycle count. */ + DRM_PANTHOR_TIMESTAMP_GPU_CYCLE_COUNT = 1 << 5, + + /** @DRM_PANTHOR_TIMESTAMP_FREQ: Query timestamp frequency. */ + DRM_PANTHOR_TIMESTAMP_FREQ = 1 << 6, + + /** @DRM_PANTHOR_TIMESTAMP_DURATION: Return duration of time query. */ + DRM_PANTHOR_TIMESTAMP_DURATION = 1 << 7, +}; + /** * struct drm_panthor_timestamp_info - Timestamp information * @@ -421,11 +453,38 @@ struct drm_panthor_timestamp_info { */ __u64 timestamp_frequency; - /** @current_timestamp: The current timestamp. */ + /** @current_timestamp: The current GPU timestamp. */ __u64 current_timestamp; - /** @timestamp_offset: The offset of the timestamp timer. */ + /** @timestamp_offset: The offset of the GPU timestamp timer. */ __u64 timestamp_offset; + + /** + * @flags: Bitmask of drm_panthor_timestamp_info_flags. + * + * If set to 0, then it is interpreted as: + * DRM_PANTHOR_TIMESTAMP_GPU | + * DRM_PANTHOR_TIMESTAMP_GPU_OFFSET | + * DRM_PANTHOR_TIMESTAMP_FREQ + * + * Note: these flags are exclusive to each other (only one can be used): + * - DRM_PANTHOR_TIMESTAMP_CPU_NONE + * - DRM_PANTHOR_TIMESTAMP_CPU_MONOTONIC + * - DRM_PANTHOR_TIMESTAMP_CPU_MONOTONIC_RAW + */ + __u32 flags; + + /** @duration_nsec: Duration of time query. */ + __u32 duration_nsec; + + /** @cycle_count: Value of GPU_CYCLE_COUNT. */ + __u64 cycle_count; + + /** @cpu_timestamp_sec: Seconds part of CPU timestamp. */ + __u64 cpu_timestamp_sec; + + /** @cpu_timestamp_nsec: Nanseconds part of CPU timestamp. */ + __u64 cpu_timestamp_nsec; }; /** -- cgit From 1f513a3ec3a945efb30c963681ac32ac7b99928b Mon Sep 17 00:00:00 2001 From: Max Zhen Date: Tue, 24 Mar 2026 09:31:59 -0700 Subject: accel/amdxdna: Add per-process BO memory usage query support Add support for querying per-process buffer object (BO) memory usage through the amdxdna GET_ARRAY UAPI. Introduce a new query type, DRM_AMDXDNA_BO_USAGE, along with struct amdxdna_drm_bo_usage to report BO memory usage statistics, including heap, total, and internal usage. Track BO memory usage on a per-client basis by maintaining counters in GEM open/close and heap allocation/free paths. This ensures the reported statistics reflect the current memory footprint of each process. Wire the new query into the GET_ARRAY implementation to expose the usage information to userspace. Link: https://github.com/amd/xdna-driver/commit/0546f2aaadbdacf1c3556410ecd71622044cd916 Signed-off-by: Max Zhen Reviewed-by: Lizhi Hou Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260324163159.2425461-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/aie2_pci.c | 4 + drivers/accel/amdxdna/amdxdna_gem.c | 134 +++++++++++++++++++++++++++++--- drivers/accel/amdxdna/amdxdna_gem.h | 7 +- drivers/accel/amdxdna/amdxdna_pci_drv.c | 6 +- drivers/accel/amdxdna/amdxdna_pci_drv.h | 4 + include/uapi/drm/amdxdna_accel.h | 35 +++++++++ 6 files changed, 177 insertions(+), 13 deletions(-) (limited to 'include/uapi') diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c index 9e39bfe75971..f1ac4e00bd9f 100644 --- a/drivers/accel/amdxdna/aie2_pci.c +++ b/drivers/accel/amdxdna/aie2_pci.c @@ -865,6 +865,7 @@ static int aie2_hwctx_status_cb(struct amdxdna_hwctx *hwctx, void *arg) tmp->command_submissions = hwctx->priv->seq; tmp->command_completions = hwctx->priv->completed; tmp->pasid = hwctx->client->pasid; + tmp->heap_usage = hwctx->client->heap_usage; tmp->priority = hwctx->qos.priority; tmp->gops = hwctx->qos.gops; tmp->fps = hwctx->qos.fps; @@ -1148,6 +1149,9 @@ static int aie2_get_array(struct amdxdna_client *client, case DRM_AMDXDNA_HW_LAST_ASYNC_ERR: ret = aie2_get_array_async_error(xdna->dev_handle, args); break; + case DRM_AMDXDNA_BO_USAGE: + ret = amdxdna_drm_get_bo_usage(&xdna->ddev, args); + break; default: XDNA_ERR(xdna, "Not supported request parameter %u", args->param); ret = -EOPNOTSUPP; diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 27712704e42d..238ee244d4a6 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -63,6 +63,8 @@ amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo) goto unlock_out; } + client->heap_usage += mem->size; + drm_gem_object_get(to_gobj(heap)); unlock_out: @@ -74,16 +76,17 @@ unlock_out: static void amdxdna_gem_heap_free(struct amdxdna_gem_obj *abo) { + struct amdxdna_client *client = abo->client; struct amdxdna_gem_obj *heap; - mutex_lock(&abo->client->mm_lock); + mutex_lock(&client->mm_lock); drm_mm_remove_node(&abo->mm_node); - - heap = abo->client->dev_heap; + client->heap_usage -= abo->mem.size; + heap = client->dev_heap; drm_gem_object_put(to_gobj(heap)); - mutex_unlock(&abo->client->mm_lock); + mutex_unlock(&client->mm_lock); } static struct amdxdna_gem_obj * @@ -102,6 +105,8 @@ amdxdna_gem_create_obj(struct drm_device *dev, size_t size) abo->mem.dma_addr = AMDXDNA_INVALID_ADDR; abo->mem.uva = AMDXDNA_INVALID_ADDR; abo->mem.size = size; + abo->open_ref = 0; + abo->internal = false; INIT_LIST_HEAD(&abo->mem.umap_list); return abo; @@ -508,13 +513,55 @@ static void amdxdna_imported_obj_free(struct amdxdna_gem_obj *abo) kfree(abo); } +static inline bool +amdxdna_gem_skip_bo_usage(struct amdxdna_gem_obj *abo) +{ + /* Do not count imported BOs since the buffer is not allocated by us. */ + if (is_import_bo(abo)) + return true; + + /* Already counted as part of HEAP BO */ + if (abo->type == AMDXDNA_BO_DEV) + return true; + + return false; +} + +static void +amdxdna_gem_add_bo_usage(struct amdxdna_gem_obj *abo) +{ + struct amdxdna_client *client = abo->client; + + if (amdxdna_gem_skip_bo_usage(abo)) + return; + + guard(mutex)(&client->mm_lock); + + client->total_bo_usage += abo->mem.size; + if (abo->internal) + client->total_int_bo_usage += abo->mem.size; +} + +static void +amdxdna_gem_del_bo_usage(struct amdxdna_gem_obj *abo) +{ + struct amdxdna_client *client = abo->client; + + if (amdxdna_gem_skip_bo_usage(abo)) + return; + + guard(mutex)(&client->mm_lock); + + client->total_bo_usage -= abo->mem.size; + if (abo->internal) + client->total_int_bo_usage -= abo->mem.size; +} + static void amdxdna_gem_obj_free(struct drm_gem_object *gobj) { struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev); struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); - XDNA_DBG(xdna, "BO type %d xdna_addr 0x%llx", abo->type, amdxdna_gem_dev_addr(abo)); - amdxdna_hmm_unregister(abo, NULL); flush_workqueue(xdna->notifier_wq); @@ -543,9 +590,13 @@ static int amdxdna_gem_obj_open(struct drm_gem_object *gobj, struct drm_file *fi int ret; guard(mutex)(&abo->lock); + abo->open_ref++; - if (!abo->client) + if (abo->open_ref == 1) { + /* Attached to the client when first opened by it. */ abo->client = filp->driver_priv; + amdxdna_gem_add_bo_usage(abo); + } if (amdxdna_iova_on(xdna)) { ret = amdxdna_iommu_map_bo(xdna, abo); if (ret) @@ -555,6 +606,20 @@ static int amdxdna_gem_obj_open(struct drm_gem_object *gobj, struct drm_file *fi return 0; } +static void amdxdna_gem_obj_close(struct drm_gem_object *gobj, struct drm_file *filp) +{ + struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); + + guard(mutex)(&abo->lock); + abo->open_ref--; + + if (abo->open_ref == 0) { + amdxdna_gem_del_bo_usage(abo); + /* Detach from the client when last closed by it. */ + abo->client = NULL; + } +} + static int amdxdna_gem_dev_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map) { struct amdxdna_gem_obj *abo = to_xdna_obj(obj); @@ -575,6 +640,7 @@ static const struct drm_gem_object_funcs amdxdna_gem_dev_obj_funcs = { static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = { .free = amdxdna_gem_obj_free, .open = amdxdna_gem_obj_open, + .close = amdxdna_gem_obj_close, .print_info = drm_gem_shmem_object_print_info, .pin = drm_gem_shmem_object_pin, .unpin = drm_gem_shmem_object_unpin, @@ -708,10 +774,13 @@ amdxdna_drm_create_share_bo(struct drm_device *dev, if (IS_ERR(abo)) return ERR_CAST(abo); - if (args->type == AMDXDNA_BO_DEV_HEAP) + if (args->type == AMDXDNA_BO_DEV_HEAP) { abo->type = AMDXDNA_BO_DEV_HEAP; - else + abo->internal = true; + } else { abo->type = AMDXDNA_BO_SHARE; + abo->internal = args->type == AMDXDNA_BO_CMD; + } return abo; } @@ -783,6 +852,11 @@ amdxdna_drm_create_dev_bo(struct drm_device *dev, gobj = to_gobj(abo); gobj->funcs = &amdxdna_gem_dev_obj_funcs; abo->type = AMDXDNA_BO_DEV; + abo->internal = true; + /* + * DEV BOs cannot be alive when client is gone, it's OK to + * always establish the connection. + */ abo->client = client; ret = amdxdna_gem_heap_alloc(abo); @@ -826,7 +900,7 @@ int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_f if (IS_ERR(abo)) return PTR_ERR(abo); - /* ready to publish object to userspace */ + /* Ready to publish object to userspace and count for BO usage. */ ret = drm_gem_handle_create(filp, to_gobj(abo), &args->handle); if (ret) { XDNA_ERR(xdna, "Create handle failed"); @@ -986,3 +1060,43 @@ put_obj: drm_gem_object_put(gobj); return ret; } + +int amdxdna_drm_get_bo_usage(struct drm_device *dev, struct amdxdna_drm_get_array *args) +{ + size_t min_sz = min(args->element_size, sizeof(struct amdxdna_drm_bo_usage)); + char __user *buf = u64_to_user_ptr(args->buffer); + struct amdxdna_dev *xdna = to_xdna_dev(dev); + struct amdxdna_client *tmp_client; + struct amdxdna_drm_bo_usage tmp; + + drm_WARN_ON(dev, !mutex_is_locked(&xdna->dev_lock)); + + if (args->num_element != 1) + return -EINVAL; + + if (copy_from_user(&tmp, buf, min_sz)) + return -EFAULT; + + if (!tmp.pid) + return -EINVAL; + + tmp.total_usage = 0; + tmp.internal_usage = 0; + tmp.heap_usage = 0; + + list_for_each_entry(tmp_client, &xdna->client_list, node) { + if (tmp.pid != tmp_client->pid) + continue; + + mutex_lock(&tmp_client->mm_lock); + tmp.total_usage += tmp_client->total_bo_usage; + tmp.internal_usage += tmp_client->total_int_bo_usage; + tmp.heap_usage += tmp_client->heap_usage; + mutex_unlock(&tmp_client->mm_lock); + } + + if (copy_to_user(buf, &tmp, min_sz)) + return -EFAULT; + + return 0; +} diff --git a/drivers/accel/amdxdna/amdxdna_gem.h b/drivers/accel/amdxdna/amdxdna_gem.h index a77d9344f8a4..4fc48a1189d2 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.h +++ b/drivers/accel/amdxdna/amdxdna_gem.h @@ -41,8 +41,9 @@ struct amdxdna_gem_obj { struct amdxdna_client *client; u8 type; bool pinned; - struct mutex lock; /* Protects: pinned, mem.kva */ + struct mutex lock; /* Protects: pinned, mem.kva, open_ref */ struct amdxdna_mem mem; + int open_ref; /* Below members are initialized when needed */ struct drm_mm mm; /* For AMDXDNA_BO_DEV_HEAP */ @@ -50,6 +51,9 @@ struct amdxdna_gem_obj { u32 assigned_hwctx; struct dma_buf *dma_buf; struct dma_buf_attachment *attach; + + /* True, if BO is managed by XRT, not application */ + bool internal; }; #define to_gobj(obj) (&(obj)->base.base) @@ -98,5 +102,6 @@ void amdxdna_gem_unpin(struct amdxdna_gem_obj *abo); int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); int amdxdna_drm_get_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); +int amdxdna_drm_get_bo_usage(struct drm_device *dev, struct amdxdna_drm_get_array *args); #endif /* _AMDXDNA_GEM_H_ */ diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c b/drivers/accel/amdxdna/amdxdna_pci_drv.c index d83be00daf2b..b50a7d1f8a11 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.c +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c @@ -36,9 +36,10 @@ MODULE_FIRMWARE("amdnpu/17f0_11/npu_7.sbin"); * 0.5: Support getting telemetry data * 0.6: Support preemption * 0.7: Support getting power and utilization data + * 0.8: Support BO usage query */ #define AMDXDNA_DRIVER_MAJOR 0 -#define AMDXDNA_DRIVER_MINOR 7 +#define AMDXDNA_DRIVER_MINOR 8 /* * Bind the driver base on (vendor_id, device_id) pair and later use the @@ -120,11 +121,12 @@ static void amdxdna_client_cleanup(struct amdxdna_client *client) amdxdna_hwctx_remove_all(client); xa_destroy(&client->hwctx_xa); cleanup_srcu_struct(&client->hwctx_srcu); - mutex_destroy(&client->mm_lock); if (client->dev_heap) drm_gem_object_put(to_gobj(client->dev_heap)); + mutex_destroy(&client->mm_lock); + if (!IS_ERR_OR_NULL(client->sva)) iommu_sva_unbind_device(client->sva); mmdrop(client->mm); diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.h b/drivers/accel/amdxdna/amdxdna_pci_drv.h index e91d14ae5190..0661749917d6 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.h +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.h @@ -138,6 +138,10 @@ struct amdxdna_client { struct iommu_sva *sva; int pasid; struct mm_struct *mm; + + size_t heap_usage; + size_t total_bo_usage; + size_t total_int_bo_usage; }; #define amdxdna_for_each_hwctx(client, hwctx_id, entry) \ diff --git a/include/uapi/drm/amdxdna_accel.h b/include/uapi/drm/amdxdna_accel.h index bddaaaf945cf..61d3686fa3b1 100644 --- a/include/uapi/drm/amdxdna_accel.h +++ b/include/uapi/drm/amdxdna_accel.h @@ -591,8 +591,37 @@ struct amdxdna_async_error { __u64 ex_err_code; }; +/** + * struct amdxdna_drm_bo_usage - all types of BO usage + * BOs managed by XRT/SHIM/driver is counted as internal. + * Others are counted as external which are managed by applications. + * + * Among all types of BOs: + * AMDXDNA_BO_DEV_HEAP - is counted for internal. + * AMDXDNA_BO_SHARE - is counted for external. + * AMDXDNA_BO_CMD - is counted for internal. + * AMDXDNA_BO_DEV - is counted by heap_usage only, not internal + * or external. It does not add to the total memory + * footprint since its mem comes from heap which is + * already counted as internal. + */ +struct amdxdna_drm_bo_usage { + /** @pid: The ID of the process to query from. */ + __s64 pid; + /** @total_usage: Total BO size used by process. */ + __u64 total_usage; + /** @internal_usage: Total internal BO size used by process. */ + __u64 internal_usage; + /** @heap_usage: Total device BO size used by process. */ + __u64 heap_usage; +}; + +/* + * Supported params in struct amdxdna_drm_get_array + */ #define DRM_AMDXDNA_HW_CONTEXT_ALL 0 #define DRM_AMDXDNA_HW_LAST_ASYNC_ERR 2 +#define DRM_AMDXDNA_BO_USAGE 6 /** * struct amdxdna_drm_get_array - Get information array. @@ -605,6 +634,12 @@ struct amdxdna_drm_get_array { * * %DRM_AMDXDNA_HW_CONTEXT_ALL: * Returns all created hardware contexts. + * + * %DRM_AMDXDNA_HW_LAST_ASYNC_ERR: + * Returns last async error. + * + * %DRM_AMDXDNA_BO_USAGE: + * Returns usage of heap/internal/external BOs. */ __u32 param; /** -- cgit From 716c11d462c598328edca2565e8d25457c6ee3d6 Mon Sep 17 00:00:00 2001 From: Jonathan Cavitt Date: Tue, 24 Mar 2026 15:29:38 +0000 Subject: drm/xe/uapi: Define drm_xe_vm_get_property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add initial declarations for the drm_xe_vm_get_property ioctl. v2: - Expand kernel docs for drm_xe_vm_get_property (Jianxun) v3: - Remove address type external definitions (Jianxun) - Add fault type to xe_drm_fault struct (Jianxun) v4: - Remove engine class and instance (Ivan) v5: - Add declares for fault type, access type, and fault level (Matt Brost, Ivan) v6: - Fix inconsistent use of whitespace in defines v7: - Rebase and refactor (jcavitt) v8: - Rebase (jcavitt) v9: - Clarify address is canonical (José) v10: - s/uAPI/Link in the commit log links Link: https://github.com/intel/compute-runtime/pull/878 Signed-off-by: Jonathan Cavitt Reviewed-by: Shuicheng Lin Acked-by: Matthew Brost Acked-by: Ivan Briano Acked-by: José Roberto de Souza Cc: Zhang Jianxun Cc: Ivan Briano Cc: Matthew Brost Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260324152935.72444-8-jonathan.cavitt@intel.com --- include/uapi/drm/xe_drm.h | 86 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) (limited to 'include/uapi') diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 7014dde1c9c4..6c99514a85e1 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -83,6 +83,7 @@ extern "C" { * - &DRM_IOCTL_XE_OBSERVATION * - &DRM_IOCTL_XE_MADVISE * - &DRM_IOCTL_XE_VM_QUERY_MEM_RANGE_ATTRS + * - &DRM_IOCTL_XE_VM_GET_PROPERTY */ /* @@ -107,6 +108,7 @@ extern "C" { #define DRM_XE_MADVISE 0x0c #define DRM_XE_VM_QUERY_MEM_RANGE_ATTRS 0x0d #define DRM_XE_EXEC_QUEUE_SET_PROPERTY 0x0e +#define DRM_XE_VM_GET_PROPERTY 0x0f /* Must be kept compact -- no holes */ @@ -125,6 +127,7 @@ extern "C" { #define DRM_IOCTL_XE_MADVISE DRM_IOW(DRM_COMMAND_BASE + DRM_XE_MADVISE, struct drm_xe_madvise) #define DRM_IOCTL_XE_VM_QUERY_MEM_RANGE_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_VM_QUERY_MEM_RANGE_ATTRS, struct drm_xe_vm_query_mem_range_attr) #define DRM_IOCTL_XE_EXEC_QUEUE_SET_PROPERTY DRM_IOW(DRM_COMMAND_BASE + DRM_XE_EXEC_QUEUE_SET_PROPERTY, struct drm_xe_exec_queue_set_property) +#define DRM_IOCTL_XE_VM_GET_PROPERTY DRM_IOWR(DRM_COMMAND_BASE + DRM_XE_VM_GET_PROPERTY, struct drm_xe_vm_get_property) /** * DOC: Xe IOCTL Extensions @@ -1263,6 +1266,89 @@ struct drm_xe_vm_bind { __u64 reserved[2]; }; +/** struct xe_vm_fault - Describes faults for %DRM_XE_VM_GET_PROPERTY_FAULTS */ +struct xe_vm_fault { + /** @address: Canonical address of the fault */ + __u64 address; + /** @address_precision: Precision of faulted address */ + __u32 address_precision; + /** @access_type: Type of address access that resulted in fault */ +#define FAULT_ACCESS_TYPE_READ 0 +#define FAULT_ACCESS_TYPE_WRITE 1 +#define FAULT_ACCESS_TYPE_ATOMIC 2 + __u8 access_type; + /** @fault_type: Type of fault reported */ +#define FAULT_TYPE_NOT_PRESENT 0 +#define FAULT_TYPE_WRITE_ACCESS 1 +#define FAULT_TYPE_ATOMIC_ACCESS 2 + __u8 fault_type; + /** @fault_level: fault level of the fault */ +#define FAULT_LEVEL_PTE 0 +#define FAULT_LEVEL_PDE 1 +#define FAULT_LEVEL_PDP 2 +#define FAULT_LEVEL_PML4 3 +#define FAULT_LEVEL_PML5 4 + __u8 fault_level; + /** @pad: MBZ */ + __u8 pad; + /** @reserved: MBZ */ + __u64 reserved[4]; +}; + +/** + * struct drm_xe_vm_get_property - Input of &DRM_IOCTL_XE_VM_GET_PROPERTY + * + * The user provides a VM and a property to query among DRM_XE_VM_GET_PROPERTY_*, + * and sets the values in the vm_id and property members, respectively. This + * determines both the VM to get the property of, as well as the property to + * report. + * + * If size is set to 0, the driver fills it with the required size for the + * requested property. The user is expected here to allocate memory for the + * property structure and to provide a pointer to the allocated memory using the + * data member. For some properties, this may be zero, in which case, the + * value of the property will be saved to the value member and size will remain + * zero on return. + * + * If size is not zero, then the IOCTL will attempt to copy the requested + * property into the data member. + * + * The IOCTL will return -ENOENT if the VM could not be identified from the + * provided VM ID, or -EINVAL if the IOCTL fails for any other reason, such as + * providing an invalid size for the given property or if the property data + * could not be copied to the memory allocated to the data member. + * + * The property member can be: + * - %DRM_XE_VM_GET_PROPERTY_FAULTS + */ +struct drm_xe_vm_get_property { + /** @extensions: Pointer to the first extension struct, if any */ + __u64 extensions; + + /** @vm_id: The ID of the VM to query the properties of */ + __u32 vm_id; + +#define DRM_XE_VM_GET_PROPERTY_FAULTS 0 + /** @property: property to get */ + __u32 property; + + /** @size: Size to allocate for @data */ + __u32 size; + + /** @pad: MBZ */ + __u32 pad; + + union { + /** @data: Pointer to user-defined array of flexible size and type */ + __u64 data; + /** @value: Return value for scalar queries */ + __u64 value; + }; + + /** @reserved: MBZ */ + __u64 reserved[3]; +}; + /** * struct drm_xe_exec_queue_create - Input of &DRM_IOCTL_XE_EXEC_QUEUE_CREATE * -- cgit From f99a05cc0b85fadd109fb3250fceccc5eaad3830 Mon Sep 17 00:00:00 2001 From: Himal Prasad Ghimiray Date: Thu, 26 Mar 2026 18:38:27 +0530 Subject: drm/xe/uapi: Add UAPI support for purgeable buffer objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the DRM_XE_MADVISE ioctl to support purgeable buffer object management by adding DRM_XE_VMA_ATTR_PURGEABLE_STATE attribute type. This allows userspace applications to provide memory usage hints to the kernel for better memory management under pressure: - WILLNEED: Buffer is needed and should not be purged. If the BO was previously purged, retained field returns 0 indicating backing store was lost (once purged, always purged semantics matching i915). - DONTNEED: Buffer is not currently needed and may be purged by the kernel under memory pressure to free resources. Only applies to non-shared BOs. To prevent undefined behavior, the following operations are blocked while a BO is in DONTNEED state: - New mmap() operations return -EBUSY - VM_BIND operations return -EBUSY - New dma-buf exports return -EBUSY - CPU page faults return SIGBUS - GPU page faults fail with -EACCES This ensures applications cannot use a BO while marked as DONTNEED, preventing erratic behavior when the kernel purges the backing store. The implementation includes a 'retained' output field (matching i915's drm_i915_gem_madvise.retained) that indicates whether the BO's backing store still exists (1) or has been purged (0). Added DRM_XE_QUERY_CONFIG_FLAG_HAS_PURGING_SUPPORT flag to allow userspace to detect kernel support for purgeable buffer objects before attempting to use the feature. Cc: Matthew Brost Acked-by: José Roberto de Souza Reviewed-by: Thomas Hellström Signed-off-by: Himal Prasad Ghimiray Signed-off-by: Arvind Yadav Signed-off-by: Matthew Brost Link: https://patch.msgid.link/20260326130843.3545241-2-arvind.yadav@intel.com --- include/uapi/drm/xe_drm.h | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) (limited to 'include/uapi') diff --git a/include/uapi/drm/xe_drm.h b/include/uapi/drm/xe_drm.h index 6c99514a85e1..ae2fda23ce7c 100644 --- a/include/uapi/drm/xe_drm.h +++ b/include/uapi/drm/xe_drm.h @@ -432,6 +432,7 @@ struct drm_xe_query_config { #define DRM_XE_QUERY_CONFIG_FLAG_HAS_CPU_ADDR_MIRROR (1 << 2) #define DRM_XE_QUERY_CONFIG_FLAG_HAS_NO_COMPRESSION_HINT (1 << 3) #define DRM_XE_QUERY_CONFIG_FLAG_HAS_DISABLE_STATE_CACHE_PERF_FIX (1 << 4) + #define DRM_XE_QUERY_CONFIG_FLAG_HAS_PURGING_SUPPORT (1 << 5) #define DRM_XE_QUERY_CONFIG_MIN_ALIGNMENT 2 #define DRM_XE_QUERY_CONFIG_VA_BITS 3 #define DRM_XE_QUERY_CONFIG_MAX_EXEC_QUEUE_PRIORITY 4 @@ -2171,6 +2172,7 @@ struct drm_xe_query_eu_stall { * - DRM_XE_MEM_RANGE_ATTR_PREFERRED_LOC: Set preferred memory location. * - DRM_XE_MEM_RANGE_ATTR_ATOMIC: Set atomic access policy. * - DRM_XE_MEM_RANGE_ATTR_PAT: Set page attribute table index. + * - DRM_XE_VMA_ATTR_PURGEABLE_STATE: Set purgeable state for BOs. * * Example: * @@ -2203,6 +2205,7 @@ struct drm_xe_madvise { #define DRM_XE_MEM_RANGE_ATTR_PREFERRED_LOC 0 #define DRM_XE_MEM_RANGE_ATTR_ATOMIC 1 #define DRM_XE_MEM_RANGE_ATTR_PAT 2 +#define DRM_XE_VMA_ATTR_PURGEABLE_STATE 3 /** @type: type of attribute */ __u32 type; @@ -2293,6 +2296,72 @@ struct drm_xe_madvise { /** @pat_index.reserved: Reserved */ __u64 reserved; } pat_index; + + /** + * @purge_state_val: Purgeable state configuration + * + * Used when @type == DRM_XE_VMA_ATTR_PURGEABLE_STATE. + * + * Configures the purgeable state of buffer objects in the specified + * virtual address range. This allows applications to hint to the kernel + * about bo's usage patterns for better memory management. + * + * By default all VMAs are in WILLNEED state. + * + * Supported values for @purge_state_val.val: + * - DRM_XE_VMA_PURGEABLE_STATE_WILLNEED (0): Marks BO as needed. + * If the BO was previously purged, the kernel sets the __u32 at + * @retained_ptr to 0 (backing store lost) so the application knows + * it must recreate the BO. + * + * - DRM_XE_VMA_PURGEABLE_STATE_DONTNEED (1): Marks BO as not currently + * needed. Kernel may purge it under memory pressure to reclaim memory. + * Only applies to non-shared BOs. The kernel sets the __u32 at + * @retained_ptr to 1 if the backing store still exists (not yet purged), + * or 0 if it was already purged. + * + * Important: Once marked as DONTNEED, touching the BO's memory + * is undefined behavior. It may succeed temporarily (before the + * kernel purges the backing store) but will suddenly fail once + * the BO transitions to PURGED state. + * + * To transition back: use WILLNEED and check @retained_ptr — + * if 0, backing store was lost and the BO must be recreated. + * + * The following operations are blocked in DONTNEED state to + * prevent the BO from being re-mapped after madvise: + * - New mmap() calls: Fail with -EBUSY + * - VM_BIND operations: Fail with -EBUSY + * - New dma-buf exports: Fail with -EBUSY + * - CPU page faults (existing mmap): Fail with SIGBUS + * - GPU page faults (fault-mode VMs): Fail with -EACCES + */ + struct { +#define DRM_XE_VMA_PURGEABLE_STATE_WILLNEED 0 +#define DRM_XE_VMA_PURGEABLE_STATE_DONTNEED 1 + /** @purge_state_val.val: value for DRM_XE_VMA_ATTR_PURGEABLE_STATE */ + __u32 val; + + /** @purge_state_val.pad: MBZ */ + __u32 pad; + /** + * @purge_state_val.retained_ptr: Pointer to a __u32 output + * field for backing store status. + * + * Userspace must initialize the __u32 value at this address + * to 0 before the ioctl. Kernel writes a __u32 after the + * operation: + * - 1 if backing store exists (not purged) + * - 0 if backing store was purged + * + * If userspace fails to initialize to 0, ioctl returns -EINVAL. + * This ensures a safe default (0 = assume purged) if kernel + * cannot write the result. + * + * Similar to i915's drm_i915_gem_madvise.retained field. + */ + __u64 retained_ptr; + } purge_state_val; }; /** @reserved: Reserved */ -- cgit From 64ac64bb62064dbfbb66964331f5a2af6adeb03b Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 27 Mar 2026 05:44:06 +0530 Subject: drm/msm/adreno: Expose a PARAM to check AQE support AQE (Applicaton Qrisc Engine) is required to support VK ray-pipeline. Two conditions should be met to use this HW: 1. AQE firmware should be loaded and programmed 2. Preemption support Expose a new MSM_PARAM to allow userspace to query its support. Signed-off-by: Akhil P Oommen Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/714685/ Message-ID: <20260327-a8xx-gpu-batch2-v2-17-2b53c38d2101@oss.qualcomm.com> Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 13 +++++++++++++ drivers/gpu/drm/msm/adreno/adreno_gpu.c | 4 ++++ drivers/gpu/drm/msm/adreno/adreno_gpu.h | 1 + include/uapi/drm/msm_drm.h | 1 + 4 files changed, 19 insertions(+) (limited to 'include/uapi') diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c index ae592022bebc..615509c8917e 100644 --- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c @@ -2604,6 +2604,17 @@ static int a6xx_set_supported_hw(struct device *dev, struct a6xx_gpu *a6xx_gpu, return 0; } +static bool a6xx_aqe_is_enabled(struct adreno_gpu *adreno_gpu) +{ + struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); + + /* + * AQE uses preemption context record as scratch pad, so check if + * preemption is enabled + */ + return (adreno_gpu->base.nr_rings > 1) && !!a6xx_gpu->aqe_bo; +} + static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev) { struct msm_drm_private *priv = dev->dev_private; @@ -2803,6 +2814,7 @@ const struct adreno_gpu_funcs a7xx_gpu_funcs = { .bus_halt = a6xx_bus_clear_pending_transactions, .mmu_fault_handler = a6xx_fault_handler, .gx_is_on = a7xx_gmu_gx_is_on, + .aqe_is_enabled = a6xx_aqe_is_enabled, }; const struct adreno_gpu_funcs a8xx_gpu_funcs = { @@ -2831,4 +2843,5 @@ const struct adreno_gpu_funcs a8xx_gpu_funcs = { .bus_halt = a8xx_bus_clear_pending_transactions, .mmu_fault_handler = a8xx_fault_handler, .gx_is_on = a8xx_gmu_gx_is_on, + .aqe_is_enabled = a6xx_aqe_is_enabled, }; diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 0dbeb332f8d1..85d135a9d336 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -441,6 +441,10 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx, case MSM_PARAM_HAS_PRR: *value = adreno_smmu_has_prr(gpu); return 0; + case MSM_PARAM_AQE: + *value = !!(adreno_gpu->funcs->aqe_is_enabled && + adreno_gpu->funcs->aqe_is_enabled(adreno_gpu)); + return 0; default: return UERR(EINVAL, drm, "%s: invalid param: %u", gpu->name, param); } diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 834f6fd2a89e..ec643b84646b 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -80,6 +80,7 @@ struct adreno_gpu_funcs { void (*bus_halt)(struct adreno_gpu *adreno_gpu, bool gx_off); int (*mmu_fault_handler)(void *arg, unsigned long iova, int flags, void *data); bool (*gx_is_on)(struct adreno_gpu *adreno_gpu); + bool (*aqe_is_enabled)(struct adreno_gpu *adreno_gpu); }; struct adreno_reglist { diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 5c67294edc95..b99098792371 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -117,6 +117,7 @@ struct drm_msm_timespec { * ioctl will throw -EPIPE. */ #define MSM_PARAM_EN_VM_BIND 0x16 /* WO, once */ +#define MSM_PARAM_AQE 0x17 /* RO */ /* For backwards compat. The original support for preemption was based on * a single ring per priority level so # of priority levels equals the # -- cgit