* [PATCH v9 1/4] drm/panthor: Introduce BO labeling
2025-04-18 2:27 [PATCH v9 0/4] Panthor BO tagging and GEMS debug display Adrián Larumbe
@ 2025-04-18 2:27 ` Adrián Larumbe
2025-04-18 2:27 ` [PATCH v9 2/4] drm/panthor: Add driver IOCTL for setting BO labels Adrián Larumbe
` (2 subsequent siblings)
3 siblings, 0 replies; 11+ messages in thread
From: Adrián Larumbe @ 2025-04-18 2:27 UTC (permalink / raw)
To: linux-kernel
Cc: dri-devel, Boris Brezillon, kernel, Adrián Larumbe,
Liviu Dudau, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Add a new character string Panthor BO field, and a function that allows
setting it from within the driver.
Driver takes care of freeing the string when it's replaced or no longer
needed at object destruction time, but allocating it is the responsibility
of callers.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Reviewed-by: Steven Price <steven.price@arm.com>
---
drivers/gpu/drm/panthor/panthor_gem.c | 46 +++++++++++++++++++++++++++
drivers/gpu/drm/panthor/panthor_gem.h | 17 ++++++++++
2 files changed, 63 insertions(+)
diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c
index 8244a4e6c2a2..8dd7fa63f1ff 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.c
+++ b/drivers/gpu/drm/panthor/panthor_gem.c
@@ -2,6 +2,7 @@
/* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
/* Copyright 2023 Collabora ltd. */
+#include <linux/cleanup.h>
#include <linux/dma-buf.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
@@ -18,6 +19,14 @@ static void panthor_gem_free_object(struct drm_gem_object *obj)
struct panthor_gem_object *bo = to_panthor_bo(obj);
struct drm_gem_object *vm_root_gem = bo->exclusive_vm_root_gem;
+ /*
+ * Label might have been allocated with kstrdup_const(),
+ * we need to take that into account when freeing the memory
+ */
+ kfree_const(bo->label.str);
+
+ mutex_destroy(&bo->label.lock);
+
drm_gem_free_mmap_offset(&bo->base.base);
mutex_destroy(&bo->gpuva_list_lock);
drm_gem_shmem_free(&bo->base);
@@ -196,6 +205,7 @@ struct drm_gem_object *panthor_gem_create_object(struct drm_device *ddev, size_t
obj->base.map_wc = !ptdev->coherent;
mutex_init(&obj->gpuva_list_lock);
drm_gem_gpuva_set_lock(&obj->base.base, &obj->gpuva_list_lock);
+ mutex_init(&obj->label.lock);
return &obj->base.base;
}
@@ -247,3 +257,39 @@ panthor_gem_create_with_handle(struct drm_file *file,
return ret;
}
+
+void
+panthor_gem_bo_set_label(struct drm_gem_object *obj, const char *label)
+{
+ struct panthor_gem_object *bo = to_panthor_bo(obj);
+ const char *old_label;
+
+ scoped_guard(mutex, &bo->label.lock) {
+ old_label = bo->label.str;
+ bo->label.str = label;
+ }
+
+ kfree_const(old_label);
+}
+
+void
+panthor_gem_kernel_bo_set_label(struct panthor_kernel_bo *bo, const char *label)
+{
+ const char *str;
+
+ /* We should never attempt labelling a UM-exposed GEM object */
+ if (drm_WARN_ON(bo->obj->dev, bo->obj->handle_count > 0))
+ return;
+
+ if (!label)
+ return;
+
+ str = kstrdup_const(label, GFP_KERNEL);
+ if (!str) {
+ /* Failing to allocate memory for a label isn't a fatal condition */
+ drm_warn(bo->obj->dev, "Not enough memory to allocate BO label");
+ return;
+ }
+
+ panthor_gem_bo_set_label(bo->obj, str);
+}
diff --git a/drivers/gpu/drm/panthor/panthor_gem.h b/drivers/gpu/drm/panthor/panthor_gem.h
index 1a363bb814f4..af0d77338860 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.h
+++ b/drivers/gpu/drm/panthor/panthor_gem.h
@@ -46,6 +46,20 @@ struct panthor_gem_object {
/** @flags: Combination of drm_panthor_bo_flags flags. */
u32 flags;
+
+ /**
+ * @label: BO tagging fields. The label can be assigned within the
+ * driver itself or through a specific IOCTL.
+ */
+ struct {
+ /**
+ * @label.str: Pointer to NULL-terminated string,
+ */
+ const char *str;
+
+ /** @lock.str: Protects access to the @label.str field. */
+ struct mutex lock;
+ } label;
};
/**
@@ -91,6 +105,9 @@ panthor_gem_create_with_handle(struct drm_file *file,
struct panthor_vm *exclusive_vm,
u64 *size, u32 flags, uint32_t *handle);
+void panthor_gem_bo_set_label(struct drm_gem_object *obj, const char *label);
+void panthor_gem_kernel_bo_set_label(struct panthor_kernel_bo *bo, const char *label);
+
static inline u64
panthor_kernel_bo_gpuva(struct panthor_kernel_bo *bo)
{
--
2.48.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v9 2/4] drm/panthor: Add driver IOCTL for setting BO labels
2025-04-18 2:27 [PATCH v9 0/4] Panthor BO tagging and GEMS debug display Adrián Larumbe
2025-04-18 2:27 ` [PATCH v9 1/4] drm/panthor: Introduce BO labeling Adrián Larumbe
@ 2025-04-18 2:27 ` Adrián Larumbe
2025-04-18 2:27 ` [PATCH v9 3/4] drm/panthor: Label all kernel BO's Adrián Larumbe
2025-04-18 2:27 ` [PATCH v9 4/4] drm/panthor: show device-wide list of DRM GEM objects over DebugFS Adrián Larumbe
3 siblings, 0 replies; 11+ messages in thread
From: Adrián Larumbe @ 2025-04-18 2:27 UTC (permalink / raw)
To: linux-kernel
Cc: dri-devel, Boris Brezillon, kernel, Adrián Larumbe,
Liviu Dudau, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Allow UM to label a BO for which it possesses a DRM handle.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
---
drivers/gpu/drm/panthor/panthor_drv.c | 45 ++++++++++++++++++++++++++-
drivers/gpu/drm/panthor/panthor_gem.h | 2 ++
include/uapi/drm/panthor_drm.h | 23 ++++++++++++++
3 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index 06fe46e32073..8a193886719e 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -1331,6 +1331,47 @@ static int panthor_ioctl_vm_get_state(struct drm_device *ddev, void *data,
return 0;
}
+static int panthor_ioctl_bo_set_label(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct drm_panthor_bo_set_label *args = data;
+ struct drm_gem_object *obj;
+ const char *label = NULL;
+ int ret = 0;
+
+ if (args->pad)
+ return -EINVAL;
+
+ obj = drm_gem_object_lookup(file, args->handle);
+ if (!obj)
+ return -ENOENT;
+
+ if (args->label) {
+ label = strndup_user((const char __user *)(uintptr_t)args->label,
+ PANTHOR_BO_LABEL_MAXLEN);
+ if (IS_ERR(label)) {
+ ret = PTR_ERR(label);
+ if (ret == -EINVAL)
+ ret = -E2BIG;
+ goto err_put_obj;
+ }
+ }
+
+ /*
+ * We treat passing a label of length 0 and passing a NULL label
+ * differently, because even though they might seem conceptually
+ * similar, future uses of the BO label might expect a different
+ * behaviour in each case.
+ */
+ panthor_gem_bo_set_label(obj, label);
+
+err_put_obj:
+ drm_gem_object_put(obj);
+
+ return ret;
+}
+
+
static int
panthor_open(struct drm_device *ddev, struct drm_file *file)
{
@@ -1400,6 +1441,7 @@ static const struct drm_ioctl_desc panthor_drm_driver_ioctls[] = {
PANTHOR_IOCTL(TILER_HEAP_CREATE, tiler_heap_create, DRM_RENDER_ALLOW),
PANTHOR_IOCTL(TILER_HEAP_DESTROY, tiler_heap_destroy, DRM_RENDER_ALLOW),
PANTHOR_IOCTL(GROUP_SUBMIT, group_submit, DRM_RENDER_ALLOW),
+ PANTHOR_IOCTL(BO_SET_LABEL, bo_set_label, DRM_RENDER_ALLOW),
};
static int panthor_mmap(struct file *filp, struct vm_area_struct *vma)
@@ -1509,6 +1551,7 @@ static void panthor_debugfs_init(struct drm_minor *minor)
* - 1.2 - adds DEV_QUERY_GROUP_PRIORITIES_INFO query
* - adds PANTHOR_GROUP_PRIORITY_REALTIME priority
* - 1.3 - adds DRM_PANTHOR_GROUP_STATE_INNOCENT flag
+ * - 1.4 - adds DRM_IOCTL_PANTHOR_BO_SET_LABEL ioctl
*/
static const struct drm_driver panthor_drm_driver = {
.driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ |
@@ -1522,7 +1565,7 @@ static const struct drm_driver panthor_drm_driver = {
.name = "panthor",
.desc = "Panthor DRM driver",
.major = 1,
- .minor = 3,
+ .minor = 4,
.gem_create_object = panthor_gem_create_object,
.gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table,
diff --git a/drivers/gpu/drm/panthor/panthor_gem.h b/drivers/gpu/drm/panthor/panthor_gem.h
index af0d77338860..983cc8ca264e 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.h
+++ b/drivers/gpu/drm/panthor/panthor_gem.h
@@ -13,6 +13,8 @@
struct panthor_vm;
+#define PANTHOR_BO_LABEL_MAXLEN 4096
+
/**
* struct panthor_gem_object - Driver specific GEM object.
*/
diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
index 97e2c4510e69..ad9a70afea6c 100644
--- a/include/uapi/drm/panthor_drm.h
+++ b/include/uapi/drm/panthor_drm.h
@@ -127,6 +127,9 @@ enum drm_panthor_ioctl_id {
/** @DRM_PANTHOR_TILER_HEAP_DESTROY: Destroy a tiler heap. */
DRM_PANTHOR_TILER_HEAP_DESTROY,
+
+ /** @DRM_PANTHOR_BO_SET_LABEL: Label a BO. */
+ DRM_PANTHOR_BO_SET_LABEL,
};
/**
@@ -977,6 +980,24 @@ struct drm_panthor_tiler_heap_destroy {
__u32 pad;
};
+/**
+ * struct drm_panthor_bo_set_label - Arguments passed to DRM_IOCTL_PANTHOR_BO_SET_LABEL
+ */
+struct drm_panthor_bo_set_label {
+ /** @handle: Handle of the buffer object to label. */
+ __u32 handle;
+
+ /** @pad: MBZ. */
+ __u32 pad;
+
+ /**
+ * @label: User pointer to a NUL-terminated string
+ *
+ * Length cannot be greater than 4096
+ */
+ __u64 label;
+};
+
/**
* DRM_IOCTL_PANTHOR() - Build a Panthor IOCTL number
* @__access: Access type. Must be R, W or RW.
@@ -1019,6 +1040,8 @@ enum {
DRM_IOCTL_PANTHOR(WR, TILER_HEAP_CREATE, tiler_heap_create),
DRM_IOCTL_PANTHOR_TILER_HEAP_DESTROY =
DRM_IOCTL_PANTHOR(WR, TILER_HEAP_DESTROY, tiler_heap_destroy),
+ DRM_IOCTL_PANTHOR_BO_SET_LABEL =
+ DRM_IOCTL_PANTHOR(WR, BO_SET_LABEL, bo_set_label),
};
#if defined(__cplusplus)
--
2.48.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v9 3/4] drm/panthor: Label all kernel BO's
2025-04-18 2:27 [PATCH v9 0/4] Panthor BO tagging and GEMS debug display Adrián Larumbe
2025-04-18 2:27 ` [PATCH v9 1/4] drm/panthor: Introduce BO labeling Adrián Larumbe
2025-04-18 2:27 ` [PATCH v9 2/4] drm/panthor: Add driver IOCTL for setting BO labels Adrián Larumbe
@ 2025-04-18 2:27 ` Adrián Larumbe
2025-04-18 2:27 ` [PATCH v9 4/4] drm/panthor: show device-wide list of DRM GEM objects over DebugFS Adrián Larumbe
3 siblings, 0 replies; 11+ messages in thread
From: Adrián Larumbe @ 2025-04-18 2:27 UTC (permalink / raw)
To: linux-kernel
Cc: dri-devel, Boris Brezillon, kernel, Adrián Larumbe,
Liviu Dudau, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter
Kernel BO's aren't exposed to UM, so labelling them is the responsibility
of the driver itself. This kind of tagging will prove useful in further
commits when want to expose these objects through DebugFS.
Expand panthor_kernel_bo_create() interface to take a NUL-terminated
string. No bounds checking is done because all label strings are given
as statically-allocated literals, but if a more complex kernel BO naming
scheme with explicit memory allocation and formatting was desired in the
future, this would have to change.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Reviewed-by: Steven Price <steven.price@arm.com>
---
drivers/gpu/drm/panthor/panthor_fw.c | 8 +++++---
drivers/gpu/drm/panthor/panthor_gem.c | 5 ++++-
drivers/gpu/drm/panthor/panthor_gem.h | 2 +-
drivers/gpu/drm/panthor/panthor_heap.c | 6 ++++--
drivers/gpu/drm/panthor/panthor_sched.c | 9 ++++++---
5 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index 0f52766a3120..a7fdc4d8020d 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -449,7 +449,8 @@ panthor_fw_alloc_queue_iface_mem(struct panthor_device *ptdev,
DRM_PANTHOR_BO_NO_MMAP,
DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC |
DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED,
- PANTHOR_VM_KERNEL_AUTO_VA);
+ PANTHOR_VM_KERNEL_AUTO_VA,
+ "Queue FW interface");
if (IS_ERR(mem))
return mem;
@@ -481,7 +482,8 @@ panthor_fw_alloc_suspend_buf_mem(struct panthor_device *ptdev, size_t size)
return panthor_kernel_bo_create(ptdev, panthor_fw_vm(ptdev), size,
DRM_PANTHOR_BO_NO_MMAP,
DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC,
- PANTHOR_VM_KERNEL_AUTO_VA);
+ PANTHOR_VM_KERNEL_AUTO_VA,
+ "FW suspend buffer");
}
static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
@@ -601,7 +603,7 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
section->mem = panthor_kernel_bo_create(ptdev, panthor_fw_vm(ptdev),
section_size,
DRM_PANTHOR_BO_NO_MMAP,
- vm_map_flags, va);
+ vm_map_flags, va, "FW section");
if (IS_ERR(section->mem))
return PTR_ERR(section->mem);
diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c
index 8dd7fa63f1ff..3f4ab5a2f2ae 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.c
+++ b/drivers/gpu/drm/panthor/panthor_gem.c
@@ -76,13 +76,14 @@ void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo)
* @gpu_va: GPU address assigned when mapping to the VM.
* If gpu_va == PANTHOR_VM_KERNEL_AUTO_VA, the virtual address will be
* automatically allocated.
+ * @name: Descriptive label of the BO's contents
*
* Return: A valid pointer in case of success, an ERR_PTR() otherwise.
*/
struct panthor_kernel_bo *
panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
size_t size, u32 bo_flags, u32 vm_map_flags,
- u64 gpu_va)
+ u64 gpu_va, const char *name)
{
struct drm_gem_shmem_object *obj;
struct panthor_kernel_bo *kbo;
@@ -106,6 +107,8 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
kbo->obj = &obj->base;
bo->flags = bo_flags;
+ panthor_gem_kernel_bo_set_label(kbo, name);
+
/* The system and GPU MMU page size might differ, which becomes a
* problem for FW sections that need to be mapped at explicit address
* since our PAGE_SIZE alignment might cover a VA range that's
diff --git a/drivers/gpu/drm/panthor/panthor_gem.h b/drivers/gpu/drm/panthor/panthor_gem.h
index 983cc8ca264e..3c09af568e47 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.h
+++ b/drivers/gpu/drm/panthor/panthor_gem.h
@@ -153,7 +153,7 @@ panthor_kernel_bo_vunmap(struct panthor_kernel_bo *bo)
struct panthor_kernel_bo *
panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
size_t size, u32 bo_flags, u32 vm_map_flags,
- u64 gpu_va);
+ u64 gpu_va, const char *name);
void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo);
diff --git a/drivers/gpu/drm/panthor/panthor_heap.c b/drivers/gpu/drm/panthor/panthor_heap.c
index 3bdf61c14264..d236e9ceade4 100644
--- a/drivers/gpu/drm/panthor/panthor_heap.c
+++ b/drivers/gpu/drm/panthor/panthor_heap.c
@@ -151,7 +151,8 @@ static int panthor_alloc_heap_chunk(struct panthor_heap_pool *pool,
chunk->bo = panthor_kernel_bo_create(pool->ptdev, pool->vm, heap->chunk_size,
DRM_PANTHOR_BO_NO_MMAP,
DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC,
- PANTHOR_VM_KERNEL_AUTO_VA);
+ PANTHOR_VM_KERNEL_AUTO_VA,
+ "Tiler heap chunk");
if (IS_ERR(chunk->bo)) {
ret = PTR_ERR(chunk->bo);
goto err_free_chunk;
@@ -555,7 +556,8 @@ panthor_heap_pool_create(struct panthor_device *ptdev, struct panthor_vm *vm)
pool->gpu_contexts = panthor_kernel_bo_create(ptdev, vm, bosize,
DRM_PANTHOR_BO_NO_MMAP,
DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC,
- PANTHOR_VM_KERNEL_AUTO_VA);
+ PANTHOR_VM_KERNEL_AUTO_VA,
+ "Heap pool");
if (IS_ERR(pool->gpu_contexts)) {
ret = PTR_ERR(pool->gpu_contexts);
goto err_destroy_pool;
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index 446ec780eb4a..43ee57728de5 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -3332,7 +3332,8 @@ group_create_queue(struct panthor_group *group,
DRM_PANTHOR_BO_NO_MMAP,
DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC |
DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED,
- PANTHOR_VM_KERNEL_AUTO_VA);
+ PANTHOR_VM_KERNEL_AUTO_VA,
+ "CS ring buffer");
if (IS_ERR(queue->ringbuf)) {
ret = PTR_ERR(queue->ringbuf);
goto err_free_queue;
@@ -3362,7 +3363,8 @@ group_create_queue(struct panthor_group *group,
DRM_PANTHOR_BO_NO_MMAP,
DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC |
DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED,
- PANTHOR_VM_KERNEL_AUTO_VA);
+ PANTHOR_VM_KERNEL_AUTO_VA,
+ "Group job stats");
if (IS_ERR(queue->profiling.slots)) {
ret = PTR_ERR(queue->profiling.slots);
@@ -3493,7 +3495,8 @@ int panthor_group_create(struct panthor_file *pfile,
DRM_PANTHOR_BO_NO_MMAP,
DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC |
DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED,
- PANTHOR_VM_KERNEL_AUTO_VA);
+ PANTHOR_VM_KERNEL_AUTO_VA,
+ "Group sync objects");
if (IS_ERR(group->syncobjs)) {
ret = PTR_ERR(group->syncobjs);
goto err_put_group;
--
2.48.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v9 4/4] drm/panthor: show device-wide list of DRM GEM objects over DebugFS
2025-04-18 2:27 [PATCH v9 0/4] Panthor BO tagging and GEMS debug display Adrián Larumbe
` (2 preceding siblings ...)
2025-04-18 2:27 ` [PATCH v9 3/4] drm/panthor: Label all kernel BO's Adrián Larumbe
@ 2025-04-18 2:27 ` Adrián Larumbe
2025-04-18 8:04 ` Boris Brezillon
2025-04-18 8:11 ` Boris Brezillon
3 siblings, 2 replies; 11+ messages in thread
From: Adrián Larumbe @ 2025-04-18 2:27 UTC (permalink / raw)
To: linux-kernel
Cc: dri-devel, Boris Brezillon, kernel, Adrián Larumbe,
Liviu Dudau, Steven Price, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Sumit Semwal,
Christian König, linux-media, linaro-mm-sig
Add a device DebugFS file that displays a complete list of all the DRM
GEM objects that are exposed to UM through a DRM handle.
Since leaking object identifiers that might belong to a different NS is
inadmissible, this functionality is only made available in debug builds
with DEBUGFS support enabled.
File format is that of a table, with each entry displaying a variety of
fields with information about each GEM object.
Each GEM object entry in the file displays the following information
fields: Client PID, BO's global name, reference count, BO virtual size,
BO resize size, VM address in its DRM-managed range, BO label and a GEM
state flags.
There's also a usage flags field for the type of BO, which tells us
whether it's a kernel BO and/or mapped onto the FW's address space.
Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
---
drivers/gpu/drm/panthor/panthor_device.c | 5 +
drivers/gpu/drm/panthor/panthor_device.h | 11 ++
drivers/gpu/drm/panthor/panthor_drv.c | 26 ++++
drivers/gpu/drm/panthor/panthor_gem.c | 182 +++++++++++++++++++++++
drivers/gpu/drm/panthor/panthor_gem.h | 59 ++++++++
5 files changed, 283 insertions(+)
diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
index a9da1d1eeb70..b776e1a2e4f3 100644
--- a/drivers/gpu/drm/panthor/panthor_device.c
+++ b/drivers/gpu/drm/panthor/panthor_device.c
@@ -184,6 +184,11 @@ int panthor_device_init(struct panthor_device *ptdev)
if (ret)
return ret;
+#ifdef CONFIG_DEBUG_FS
+ drmm_mutex_init(&ptdev->base, &ptdev->gems.lock);
+ INIT_LIST_HEAD(&ptdev->gems.node);
+#endif
+
atomic_set(&ptdev->pm.state, PANTHOR_DEVICE_PM_STATE_SUSPENDED);
p = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!p)
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index da6574021664..86206a961b38 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -205,6 +205,17 @@ struct panthor_device {
/** @fast_rate: Maximum device clock frequency. Set by DVFS */
unsigned long fast_rate;
+
+#ifdef CONFIG_DEBUG_FS
+ /** @gems: Device-wide list of GEM objects owned by at least one file. */
+ struct {
+ /** @gems.lock: Protects the device-wide list of GEM objects. */
+ struct mutex lock;
+
+ /** @node: Used to keep track of all the device's DRM objects */
+ struct list_head node;
+ } gems;
+#endif
};
struct panthor_gpu_usage {
diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index 8a193886719e..c00f45576385 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -1538,9 +1538,35 @@ static const struct file_operations panthor_drm_driver_fops = {
};
#ifdef CONFIG_DEBUG_FS
+static int panthor_gems_show(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct panthor_device *ptdev = container_of(dev, struct panthor_device, base);
+
+ panthor_gem_debugfs_print_bos(ptdev, m);
+
+ return 0;
+}
+
+
+static struct drm_info_list panthor_debugfs_list[] = {
+ {"gems", panthor_gems_show, 0, NULL},
+};
+
+static int panthor_gems_debugfs_init(struct drm_minor *minor)
+{
+ drm_debugfs_create_files(panthor_debugfs_list,
+ ARRAY_SIZE(panthor_debugfs_list),
+ minor->debugfs_root, minor);
+
+ return 0;
+}
+
static void panthor_debugfs_init(struct drm_minor *minor)
{
panthor_mmu_debugfs_init(minor);
+ panthor_gems_debugfs_init(minor);
}
#endif
diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c
index 3f4ab5a2f2ae..1e3409c05891 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.c
+++ b/drivers/gpu/drm/panthor/panthor_gem.c
@@ -11,14 +11,51 @@
#include <drm/panthor_drm.h>
#include "panthor_device.h"
+#include "panthor_fw.h"
#include "panthor_gem.h"
#include "panthor_mmu.h"
+#ifdef CONFIG_DEBUG_FS
+static void panthor_gem_debugfs_bo_add(struct panthor_device *ptdev,
+ struct panthor_gem_object *bo)
+{
+ INIT_LIST_HEAD(&bo->debugfs.node);
+
+ bo->debugfs.creator.tgid = current->group_leader->pid;
+ get_task_comm(bo->debugfs.creator.process_name, current->group_leader);
+
+ mutex_lock(&ptdev->gems.lock);
+ list_add_tail(&bo->debugfs.node, &ptdev->gems.node);
+ mutex_unlock(&ptdev->gems.lock);
+}
+
+static void panthor_gem_debugfs_bo_rm(struct panthor_gem_object *bo)
+{
+ struct panthor_device *ptdev = container_of(bo->base.base.dev,
+ struct panthor_device, base);
+
+ if (list_empty(&bo->debugfs.node))
+ return;
+
+ mutex_lock(&ptdev->gems.lock);
+ list_del_init(&bo->debugfs.node);
+ mutex_unlock(&ptdev->gems.lock);
+}
+
+#else
+static void panthor_gem_debugfs_bo_add(struct panthor_device *ptdev,
+ struct panthor_gem_object *bo)
+{}
+static void panthor_gem_debugfs_bo_rm(struct panthor_gem_object *bo) {}
+#endif
+
static void panthor_gem_free_object(struct drm_gem_object *obj)
{
struct panthor_gem_object *bo = to_panthor_bo(obj);
struct drm_gem_object *vm_root_gem = bo->exclusive_vm_root_gem;
+ panthor_gem_debugfs_bo_rm(bo);
+
/*
* Label might have been allocated with kstrdup_const(),
* we need to take that into account when freeing the memory
@@ -88,6 +125,7 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
struct drm_gem_shmem_object *obj;
struct panthor_kernel_bo *kbo;
struct panthor_gem_object *bo;
+ u32 debug_flags = PANTHOR_DEBUGFS_GEM_USAGE_FLAG_KERNEL;
int ret;
if (drm_WARN_ON(&ptdev->base, !vm))
@@ -107,7 +145,11 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
kbo->obj = &obj->base;
bo->flags = bo_flags;
+ if (vm == panthor_fw_vm(ptdev))
+ debug_flags |= PANTHOR_DEBUGFS_GEM_USAGE_FLAG_FW_MAPPED;
+
panthor_gem_kernel_bo_set_label(kbo, name);
+ panthor_gem_debugfs_set_usage_flags(to_panthor_bo(kbo->obj), debug_flags);
/* The system and GPU MMU page size might differ, which becomes a
* problem for FW sections that need to be mapped at explicit address
@@ -210,6 +252,8 @@ struct drm_gem_object *panthor_gem_create_object(struct drm_device *ddev, size_t
drm_gem_gpuva_set_lock(&obj->base.base, &obj->gpuva_list_lock);
mutex_init(&obj->label.lock);
+ panthor_gem_debugfs_bo_add(ptdev, obj);
+
return &obj->base.base;
}
@@ -258,6 +302,12 @@ panthor_gem_create_with_handle(struct drm_file *file,
/* drop reference from allocate - handle holds it now. */
drm_gem_object_put(&shmem->base);
+ /*
+ * No explicit flags are needed in the call below, since the
+ * function internally sets the INITIALIZED bit for us.
+ */
+ panthor_gem_debugfs_set_usage_flags(bo, 0);
+
return ret;
}
@@ -296,3 +346,135 @@ panthor_gem_kernel_bo_set_label(struct panthor_kernel_bo *bo, const char *label)
panthor_gem_bo_set_label(bo->obj, str);
}
+
+#ifdef CONFIG_DEBUG_FS
+static void
+panthor_gem_debugfs_format_flags(char flags_str[], int flags_len,
+ const char * const names[], u32 name_count,
+ u32 flags)
+{
+ bool first = true;
+ int offset = 0;
+
+#define ACC_FLAGS(...) \
+ ({ \
+ offset += snprintf(flags_str + offset, flags_len - offset, ##__VA_ARGS__); \
+ if (offset == flags_len) \
+ return; \
+ })
+
+ ACC_FLAGS("%c", '(');
+
+ if (!flags)
+ ACC_FLAGS("%s", "none");
+
+ while (flags) {
+ u32 bit = fls(flags) - 1;
+ u32 idx = bit + 1;
+
+ if (!first)
+ ACC_FLAGS("%s", ",");
+
+ if (idx < name_count && names[idx])
+ ACC_FLAGS("%s", names[idx]);
+
+ first = false;
+ flags &= ~BIT(bit);
+ }
+
+ ACC_FLAGS("%c", ')');
+
+#undef ACC_FLAGS
+}
+
+struct gem_size_totals {
+ size_t size;
+ size_t resident;
+ size_t reclaimable;
+};
+
+static void panthor_gem_debugfs_bo_print(struct panthor_gem_object *bo,
+ struct seq_file *m,
+ struct gem_size_totals *totals)
+{
+ unsigned int refcount = kref_read(&bo->base.base.refcount);
+ char creator_info[32] = {};
+ size_t resident_size;
+ char gem_state_str[64] = {};
+ char gem_usage_str[64] = {};
+ u32 gem_usage_flags = bo->debugfs.flags & (u32)~PANTHOR_DEBUGFS_GEM_USAGE_FLAG_INITIALIZED;
+ u32 gem_state_flags = 0;
+
+ static const char * const gem_state_flags_names[] = {
+ [PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED] = "imported",
+ [PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED] = "exported",
+ };
+
+ static const char * const gem_usage_flags_names[] = {
+ [PANTHOR_DEBUGFS_GEM_USAGE_FLAG_KERNEL] = "kernel",
+ [PANTHOR_DEBUGFS_GEM_USAGE_FLAG_FW_MAPPED] = "fw-mapped",
+ };
+
+ /* Skip BOs being destroyed. */
+ if (!refcount)
+ return;
+
+ resident_size = bo->base.pages != NULL ? bo->base.base.size : 0;
+
+ snprintf(creator_info, sizeof(creator_info),
+ "%s/%d", bo->debugfs.creator.process_name, bo->debugfs.creator.tgid);
+ seq_printf(m, "%-32s%-16d%-16d%-16zd%-16zd%-16lx",
+ creator_info,
+ bo->base.base.name,
+ refcount,
+ bo->base.base.size,
+ resident_size,
+ drm_vma_node_start(&bo->base.base.vma_node));
+
+ if (bo->base.base.import_attach != NULL)
+ gem_state_flags |= PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED;
+ if (bo->base.base.dma_buf != NULL)
+ gem_state_flags |= PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED;
+
+ panthor_gem_debugfs_format_flags(gem_state_str, sizeof(gem_state_str),
+ gem_state_flags_names, ARRAY_SIZE(gem_state_flags_names),
+ gem_state_flags);
+ panthor_gem_debugfs_format_flags(gem_usage_str, sizeof(gem_usage_str),
+ gem_usage_flags_names, ARRAY_SIZE(gem_usage_flags_names),
+ gem_usage_flags);
+
+ seq_printf(m, "%-64s%-64s", gem_state_str, gem_usage_str);
+
+ scoped_guard(mutex, &bo->label.lock) {
+ seq_printf(m, "%s", bo->label.str ? : "");
+ }
+
+ seq_puts(m, "\n");
+
+ totals->size += bo->base.base.size;
+ totals->resident += resident_size;
+ if (bo->base.madv > 0)
+ totals->reclaimable += resident_size;
+}
+
+void panthor_gem_debugfs_print_bos(struct panthor_device *ptdev,
+ struct seq_file *m)
+{
+ struct gem_size_totals totals = {0};
+ struct panthor_gem_object *bo;
+
+ seq_puts(m, "created-by global-name refcount size resident-size file-offset state usage label\n");
+ seq_puts(m, "-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n");
+
+ scoped_guard(mutex, &ptdev->gems.lock) {
+ list_for_each_entry(bo, &ptdev->gems.node, debugfs.node) {
+ if (bo->debugfs.flags & PANTHOR_DEBUGFS_GEM_USAGE_FLAG_INITIALIZED)
+ panthor_gem_debugfs_bo_print(bo, m, &totals);
+ }
+ }
+
+ seq_puts(m, "=====================================================================================================================================================================================================================================================\n");
+ seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n",
+ totals.size, totals.resident, totals.reclaimable);
+}
+#endif
diff --git a/drivers/gpu/drm/panthor/panthor_gem.h b/drivers/gpu/drm/panthor/panthor_gem.h
index 3c09af568e47..94b244f0540e 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.h
+++ b/drivers/gpu/drm/panthor/panthor_gem.h
@@ -15,6 +15,48 @@ struct panthor_vm;
#define PANTHOR_BO_LABEL_MAXLEN 4096
+enum panthor_debugfs_gem_state_flags {
+ /** @PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED: GEM BO is PRIME imported. */
+ PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED = BIT(0),
+
+ /** @PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED: GEM BO is PRIME exported. */
+ PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED = BIT(1),
+};
+
+enum panthor_debugfs_gem_usage_flags {
+ /** @PANTHOR_DEBUGFS_GEM_USAGE_FLAG_KERNEL: BO is for kernel use only. */
+ PANTHOR_DEBUGFS_GEM_USAGE_FLAG_KERNEL = BIT(0),
+
+ /** @PANTHOR_DEBUGFS_GEM_USAGE_FLAG_FW_MAPPED: BO is mapped on the FW VM. */
+ PANTHOR_DEBUGFS_GEM_USAGE_FLAG_FW_MAPPED = BIT(1),
+
+ /** @PANTHOR_DEBUGFS_GEM_USAGE_FLAG_INITIALIZED: BO is ready for DebugFS display. */
+ PANTHOR_DEBUGFS_GEM_USAGE_FLAG_INITIALIZED = BIT(31),
+};
+
+/**
+ * struct panthor_gem_debugfs - GEM object's DebugFS list information
+ */
+struct panthor_gem_debugfs {
+ /**
+ * @node: Node used to insert the object in the device-wide list of
+ * GEM objects, to display information about it through a DebugFS file.
+ */
+ struct list_head node;
+
+ /** @creator: Information about the UM process which created the GEM. */
+ struct {
+ /** @creator.process_name: Group leader name in owning thread's process */
+ char process_name[TASK_COMM_LEN];
+
+ /** @creator.tgid: PID of the thread's group leader within its process */
+ pid_t tgid;
+ } creator;
+
+ /** @flags: Combination of panthor_debugfs_gem_usage_flags flags */
+ u32 flags;
+};
+
/**
* struct panthor_gem_object - Driver specific GEM object.
*/
@@ -62,6 +104,10 @@ struct panthor_gem_object {
/** @lock.str: Protects access to the @label.str field. */
struct mutex lock;
} label;
+
+#ifdef CONFIG_DEBUG_FS
+ struct panthor_gem_debugfs debugfs;
+#endif
};
/**
@@ -157,4 +203,17 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo);
+#ifdef CONFIG_DEBUG_FS
+void panthor_gem_debugfs_print_bos(struct panthor_device *pfdev,
+ struct seq_file *m);
+static inline void
+panthor_gem_debugfs_set_usage_flags(struct panthor_gem_object *bo, u32 usage_flags)
+{
+ bo->debugfs.flags = usage_flags | PANTHOR_DEBUGFS_GEM_USAGE_FLAG_INITIALIZED;
+}
+
+#else
+void panthor_gem_debugfs_set_usage_flags(struct panthor_gem_object *bo, u32 usage_flags) {};
+#endif
+
#endif /* __PANTHOR_GEM_H__ */
--
2.48.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v9 4/4] drm/panthor: show device-wide list of DRM GEM objects over DebugFS
2025-04-18 2:27 ` [PATCH v9 4/4] drm/panthor: show device-wide list of DRM GEM objects over DebugFS Adrián Larumbe
@ 2025-04-18 8:04 ` Boris Brezillon
2025-04-18 20:15 ` Adrián Larumbe
2025-04-18 8:11 ` Boris Brezillon
1 sibling, 1 reply; 11+ messages in thread
From: Boris Brezillon @ 2025-04-18 8:04 UTC (permalink / raw)
To: Adrián Larumbe
Cc: linux-kernel, dri-devel, kernel, Liviu Dudau, Steven Price,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Sumit Semwal, Christian König, linux-media,
linaro-mm-sig
On Fri, 18 Apr 2025 03:27:07 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> +#ifdef CONFIG_DEBUG_FS
> +static void
> +panthor_gem_debugfs_format_flags(char flags_str[], int flags_len,
> + const char * const names[], u32 name_count,
> + u32 flags)
> +{
> + bool first = true;
> + int offset = 0;
> +
> +#define ACC_FLAGS(...) \
> + ({ \
> + offset += snprintf(flags_str + offset, flags_len - offset, ##__VA_ARGS__); \
> + if (offset == flags_len) \
> + return; \
> + })
I tried applying, but checkpatch complains with:
-:225: WARNING:MACRO_WITH_FLOW_CONTROL: Macros with flow control statements should be avoided
#225: FILE: drivers/gpu/drm/panthor/panthor_gem.c:359:
+#define ACC_FLAGS(...) \
+ ({ \
+ offset += snprintf(flags_str + offset, flags_len - offset, ##__VA_ARGS__); \
+ if (offset == flags_len) \
+ return; \
+ })
and now I'm wondering why we even need to return early? Oh, and the
check looks suspicious to: snprintf() returns the number of chars
that would have been written if the destination was big enough, so
the offset will actually be greater than flags_len if the formatted
string doesn't fit.
There are a few other formatting issues reported by checkpatch
--strict BTW.
Unfortunately this led me to have a second look at these bits
and I have a few more comments, sorry about that :-/.
> +
> + ACC_FLAGS("%c", '(');
Now that flags have their own columns, I'm not sure it makes sense
surround them with parenthesis. That's even weird if we start running
out of space, because there would be an open '(', a few flags,
the last one being truncated, and no closing ')'. The other thing
that's bothering me is the fact we don't know when not all flags
have been displayed because of lack of space.
> +
> + if (!flags)
> + ACC_FLAGS("%s", "none");
> +
> + while (flags) {
> + u32 bit = fls(flags) - 1;
I would probably print flags in bit position order, so ffs()
instead of fls().
> + u32 idx = bit + 1;
Why do you have a "+ 1" here? Feels like an off-by-one error to
me.
> +
> + if (!first)
> + ACC_FLAGS("%s", ",");
> +
> + if (idx < name_count && names[idx])
> + ACC_FLAGS("%s", names[idx]);
> +
> + first = false;
> + flags &= ~BIT(bit);
> + }
> +
> + ACC_FLAGS("%c", ')');
> +
> +#undef ACC_FLAGS
> +}
How about something like that:
static void
panthor_gem_debugfs_format_flags(char *flags_str, u32 flags_str_len,
const char * const flag_names[],
u32 flag_name_count, u32 flags)
{
int offset = 0;
if (!flags) {
snprintf(flags_str, flags_str_len, "%s", "none");
return;
}
while (flags) {
const char *flag_name = "?";
u32 flag = ffs(flags) - 1;
int new_offset = offset;
flags &= ~BIT(flag);
if (flag < flag_name_count && flag_names[flag])
flag_name = flag_names[flag];
/* Print as much as we can. */
new_offset += snprintf(flags_str + offset, flags_str_len - offset,
"%s%s", offset ? "," : "", flag_name);
/* If we have flags remaining, check that we have enough space for
* the "...".
*/
if (flags)
new_offset += strlen(",...");
/* If we overflowed, replace what we've written by "..." and
* bail out.
*/
if (new_offset > flags_str_len) {
snprintf(flags_str + offset, flags_str_len - offset,
"%s...", offset ? "," : "");
return;
}
offset = new_offset;
}
}
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v9 4/4] drm/panthor: show device-wide list of DRM GEM objects over DebugFS
2025-04-18 8:04 ` Boris Brezillon
@ 2025-04-18 20:15 ` Adrián Larumbe
2025-04-22 8:44 ` Boris Brezillon
0 siblings, 1 reply; 11+ messages in thread
From: Adrián Larumbe @ 2025-04-18 20:15 UTC (permalink / raw)
To: Boris Brezillon
Cc: linux-kernel, dri-devel, kernel, Liviu Dudau, Steven Price,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Sumit Semwal, Christian König, linux-media,
linaro-mm-sig
Hi Boris,
On 18.04.2025 10:04, Boris Brezillon wrote:
> On Fri, 18 Apr 2025 03:27:07 +0100
> Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
>
> > +#ifdef CONFIG_DEBUG_FS
> > +static void
> > +panthor_gem_debugfs_format_flags(char flags_str[], int flags_len,
> > + const char * const names[], u32 name_count,
> > + u32 flags)
> > +{
> > + bool first = true;
> > + int offset = 0;
> > +
> > +#define ACC_FLAGS(...) \
> > + ({ \
> > + offset += snprintf(flags_str + offset, flags_len - offset, ##__VA_ARGS__); \
> > + if (offset == flags_len) \
> > + return; \
> > + })
>
> I tried applying, but checkpatch complains with:
>
> -:225: WARNING:MACRO_WITH_FLOW_CONTROL: Macros with flow control statements should be avoided
> #225: FILE: drivers/gpu/drm/panthor/panthor_gem.c:359:
> +#define ACC_FLAGS(...) \
> + ({ \
> + offset += snprintf(flags_str + offset, flags_len - offset, ##__VA_ARGS__); \
> + if (offset == flags_len) \
> + return; \
> + })
>
>
> and now I'm wondering why we even need to return early? Oh, and the
> check looks suspicious to: snprintf() returns the number of chars
> that would have been written if the destination was big enough, so
> the offset will actually be greater than flags_len if the formatted
> string doesn't fit.
I noticed this warning when running checkpatch myself, and while a return isn't strictly
necessary, I thought there was no point in going down the function when no more bytes
could be written into the format string because it's already full.
I did check the code for other locations where flow control is happening inside a macro
and found this:
#define group_queue_work(group, wname) \
do { \
group_get(group); \
if (!queue_work((group)->ptdev->scheduler->wq, &(group)->wname ## _work)) \
group_put(group); \
} while (0)
although I'm not sure whether the do {} while (0) is right there to make the warning go away.
> snprintf() returns the number of chars
> that would have been written if the destination was big enough, so
> the offset will actually be greater than flags_len if the formatted
> string doesn't fit.
Good catch, I don't know why I thought snprintf() would print at most 'flags_len
- offset' bytes, and would return exactly that count at most too, rather than
throwing a hypothetical max value. Then maybe checking whether 'if (offset >=
flags_len)' would be enough ?
> There are a few other formatting issues reported by checkpatch
> --strict BTW.
>
> Unfortunately this led me to have a second look at these bits
> and I have a few more comments, sorry about that :-/.
> +
> + ACC_FLAGS("%c", '(');
> Now that flags have their own columns, I'm not sure it makes sense
> surround them with parenthesis. That's even weird if we start running
> out of space, because there would be an open '(', a few flags,
> the last one being truncated, and no closing ')'. The other thing
> that's bothering me is the fact we don't know when not all flags
> have been displayed because of lack of space.
>
> > +
> > + if (!flags)
> > + ACC_FLAGS("%s", "none");
> > +
> > + while (flags) {
> > + u32 bit = fls(flags) - 1;
>
> I would probably print flags in bit position order, so ffs()
> instead of fls().
>
> > + u32 idx = bit + 1;
>
> Why do you have a "+ 1" here? Feels like an off-by-one error to
> me.
>
> > +
> > + if (!first)
> > + ACC_FLAGS("%s", ",");
> > +
> > + if (idx < name_count && names[idx])
> > + ACC_FLAGS("%s", names[idx]);
> > +
> > + first = false;
> > + flags &= ~BIT(bit);
> > + }
> > +
> > + ACC_FLAGS("%c", ')');
> > +
> > +#undef ACC_FLAGS
> > +}
>
> How about something like that:
>
> static void
> panthor_gem_debugfs_format_flags(char *flags_str, u32 flags_str_len,
> const char * const flag_names[],
> u32 flag_name_count, u32 flags)
> {
> int offset = 0;
>
> if (!flags) {
> snprintf(flags_str, flags_str_len, "%s", "none");
> return;
> }
>
> while (flags) {
> const char *flag_name = "?";
> u32 flag = ffs(flags) - 1;
> int new_offset = offset;
>
> flags &= ~BIT(flag);
>
> if (flag < flag_name_count && flag_names[flag])
> flag_name = flag_names[flag];
>
> /* Print as much as we can. */
> new_offset += snprintf(flags_str + offset, flags_str_len - offset,
> "%s%s", offset ? "," : "", flag_name);
>
> /* If we have flags remaining, check that we have enough space for
> * the "...".
> */
> if (flags)
> new_offset += strlen(",...");
>
> /* If we overflowed, replace what we've written by "..." and
> * bail out.
> */
> if (new_offset > flags_str_len) {
> snprintf(flags_str + offset, flags_str_len - offset,
> "%s...", offset ? "," : "");
> return;
> }
>
> offset = new_offset;
> }
> }
This looks good to me. However, I'm starting to wonder whether it makes sense to
try to come up with a very elaborate flag formatting scheme, because of two
reasons:
- It messes up with the output because we need to provide enough headroom in
case the flag set will increase in the future. This is not a big deal because
the debugfs file is meant to be parsed by UM tools, but ...
- In case we go over the space available to print flags, not printing the
remaining ones feels even less informative than printing let's say a hexadecimal
mask, because parsing tools would rather deal with no missing data than the
absence of human-readable flag names.
On top of that, I think, while these flags could be mostly of interest to parsing
tools, they'd be less so to someone casually peeking into the raw textual
output. I think they'd be mostly interested in the process which triggered
their creation, their size, virtual address in the VM, and above all their name
(potentially a very long one).
With all these things born in mind, I'd say we'd best just print a 32 bit mask
for both flag fields, for which we'd always know the exact span in bytes, and
then print all the available flag names in the debugfs file prelude for parsing
tools to pick up on.
Adrian Larumbe
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v9 4/4] drm/panthor: show device-wide list of DRM GEM objects over DebugFS
2025-04-18 20:15 ` Adrián Larumbe
@ 2025-04-22 8:44 ` Boris Brezillon
0 siblings, 0 replies; 11+ messages in thread
From: Boris Brezillon @ 2025-04-22 8:44 UTC (permalink / raw)
To: Adrián Larumbe
Cc: linux-kernel, dri-devel, kernel, Liviu Dudau, Steven Price,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Sumit Semwal, Christian König, linux-media,
linaro-mm-sig
On Fri, 18 Apr 2025 21:15:32 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> Hi Boris,
>
> On 18.04.2025 10:04, Boris Brezillon wrote:
> > On Fri, 18 Apr 2025 03:27:07 +0100
> > Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> >
> > > +#ifdef CONFIG_DEBUG_FS
> > > +static void
> > > +panthor_gem_debugfs_format_flags(char flags_str[], int flags_len,
> > > + const char * const names[], u32 name_count,
> > > + u32 flags)
> > > +{
> > > + bool first = true;
> > > + int offset = 0;
> > > +
> > > +#define ACC_FLAGS(...) \
> > > + ({ \
> > > + offset += snprintf(flags_str + offset, flags_len - offset, ##__VA_ARGS__); \
> > > + if (offset == flags_len) \
> > > + return; \
> > > + })
> >
> > I tried applying, but checkpatch complains with:
> >
> > -:225: WARNING:MACRO_WITH_FLOW_CONTROL: Macros with flow control statements should be avoided
> > #225: FILE: drivers/gpu/drm/panthor/panthor_gem.c:359:
> > +#define ACC_FLAGS(...) \
> > + ({ \
> > + offset += snprintf(flags_str + offset, flags_len - offset, ##__VA_ARGS__); \
> > + if (offset == flags_len) \
> > + return; \
> > + })
> >
> >
> > and now I'm wondering why we even need to return early? Oh, and the
> > check looks suspicious to: snprintf() returns the number of chars
> > that would have been written if the destination was big enough, so
> > the offset will actually be greater than flags_len if the formatted
> > string doesn't fit.
>
> I noticed this warning when running checkpatch myself, and while a return isn't strictly
> necessary, I thought there was no point in going down the function when no more bytes
> could be written into the format string because it's already full.
>
> I did check the code for other locations where flow control is happening inside a macro
> and found this:
>
> #define group_queue_work(group, wname) \
> do { \
> group_get(group); \
> if (!queue_work((group)->ptdev->scheduler->wq, &(group)->wname ## _work)) \
> group_put(group); \
> } while (0)
>
> although I'm not sure whether the do {} while (0) is right there to make the warning go away.
No, the main difference is that it doesn't return behind the callers'
back which is what checkscript is complaining about, though using do {}
while(0) is usually preferred over ({}) when
you have nothing to return.
>
> > snprintf() returns the number of chars
> > that would have been written if the destination was big enough, so
> > the offset will actually be greater than flags_len if the formatted
> > string doesn't fit.
>
> Good catch, I don't know why I thought snprintf() would print at most 'flags_len
> - offset' bytes, and would return exactly that count at most too, rather than
> throwing a hypothetical max value. Then maybe checking whether 'if (offset >=
> flags_len)' would be enough ?
It should.
>
>
> > There are a few other formatting issues reported by checkpatch
> > --strict BTW.
> >
> > Unfortunately this led me to have a second look at these bits
> > and I have a few more comments, sorry about that :-/.
>
> > +
> > + ACC_FLAGS("%c", '(');
>
> > Now that flags have their own columns, I'm not sure it makes sense
> > surround them with parenthesis. That's even weird if we start running
> > out of space, because there would be an open '(', a few flags,
> > the last one being truncated, and no closing ')'. The other thing
> > that's bothering me is the fact we don't know when not all flags
> > have been displayed because of lack of space.
> >
> > > +
> > > + if (!flags)
> > > + ACC_FLAGS("%s", "none");
> > > +
> > > + while (flags) {
> > > + u32 bit = fls(flags) - 1;
> >
> > I would probably print flags in bit position order, so ffs()
> > instead of fls().
> >
> > > + u32 idx = bit + 1;
> >
> > Why do you have a "+ 1" here? Feels like an off-by-one error to
> > me.
> >
> > > +
> > > + if (!first)
> > > + ACC_FLAGS("%s", ",");
> > > +
> > > + if (idx < name_count && names[idx])
> > > + ACC_FLAGS("%s", names[idx]);
> > > +
> > > + first = false;
> > > + flags &= ~BIT(bit);
> > > + }
> > > +
> > > + ACC_FLAGS("%c", ')');
> > > +
> > > +#undef ACC_FLAGS
> > > +}
> >
> > How about something like that:
> >
> > static void
> > panthor_gem_debugfs_format_flags(char *flags_str, u32 flags_str_len,
> > const char * const flag_names[],
> > u32 flag_name_count, u32 flags)
> > {
> > int offset = 0;
> >
> > if (!flags) {
> > snprintf(flags_str, flags_str_len, "%s", "none");
> > return;
> > }
> >
> > while (flags) {
> > const char *flag_name = "?";
> > u32 flag = ffs(flags) - 1;
> > int new_offset = offset;
> >
> > flags &= ~BIT(flag);
> >
> > if (flag < flag_name_count && flag_names[flag])
> > flag_name = flag_names[flag];
> >
> > /* Print as much as we can. */
> > new_offset += snprintf(flags_str + offset, flags_str_len - offset,
> > "%s%s", offset ? "," : "", flag_name);
> >
> > /* If we have flags remaining, check that we have enough space for
> > * the "...".
> > */
> > if (flags)
> > new_offset += strlen(",...");
> >
> > /* If we overflowed, replace what we've written by "..." and
> > * bail out.
> > */
> > if (new_offset > flags_str_len) {
> > snprintf(flags_str + offset, flags_str_len - offset,
> > "%s...", offset ? "," : "");
> > return;
> > }
> >
> > offset = new_offset;
> > }
> > }
>
> This looks good to me. However, I'm starting to wonder whether it makes sense to
> try to come up with a very elaborate flag formatting scheme, because of two
> reasons:
>
> - It messes up with the output because we need to provide enough headroom in
> case the flag set will increase in the future. This is not a big deal because
> the debugfs file is meant to be parsed by UM tools, but ...
>
> - In case we go over the space available to print flags, not printing the
> remaining ones feels even less informative than printing let's say a hexadecimal
> mask, because parsing tools would rather deal with no missing data than the
> absence of human-readable flag names.
>
> On top of that, I think, while these flags could be mostly of interest to parsing
> tools, they'd be less so to someone casually peeking into the raw textual
> output. I think they'd be mostly interested in the process which triggered
> their creation, their size, virtual address in the VM, and above all their name
> (potentially a very long one).
>
> With all these things born in mind, I'd say we'd best just print a 32 bit mask
> for both flag fields, for which we'd always know the exact span in bytes, and
> then print all the available flag names in the debugfs file prelude for parsing
> tools to pick up on.
Yeah, I think I agree with you. The flag printing is messy as it is,
and if we're going to use a tool to parse the output, we're probably
better off with an hexadecimal value here.
Regards,
Boris
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v9 4/4] drm/panthor: show device-wide list of DRM GEM objects over DebugFS
2025-04-18 2:27 ` [PATCH v9 4/4] drm/panthor: show device-wide list of DRM GEM objects over DebugFS Adrián Larumbe
2025-04-18 8:04 ` Boris Brezillon
@ 2025-04-18 8:11 ` Boris Brezillon
2025-04-18 8:26 ` Boris Brezillon
1 sibling, 1 reply; 11+ messages in thread
From: Boris Brezillon @ 2025-04-18 8:11 UTC (permalink / raw)
To: Adrián Larumbe
Cc: linux-kernel, dri-devel, kernel, Liviu Dudau, Steven Price,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Sumit Semwal, Christian König, linux-media,
linaro-mm-sig
On Fri, 18 Apr 2025 03:27:07 +0100
Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
> + static const char * const gem_state_flags_names[] = {
> + [PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED] = "imported",
> + [PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED] = "exported",
Okay, I think I know where the flag indexing issue comes from:
PANTHOR_DEBUGFS_GEM_STATE_FLAG_xx are flags, not bit positions, so we
can't use them as indices here.
> + };
> +
> + static const char * const gem_usage_flags_names[] = {
> + [PANTHOR_DEBUGFS_GEM_USAGE_FLAG_KERNEL] = "kernel",
> + [PANTHOR_DEBUGFS_GEM_USAGE_FLAG_FW_MAPPED] = "fw-mapped",
Same problem here.
> + };
> +
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v9 4/4] drm/panthor: show device-wide list of DRM GEM objects over DebugFS
2025-04-18 8:11 ` Boris Brezillon
@ 2025-04-18 8:26 ` Boris Brezillon
2025-04-18 20:22 ` Adrián Larumbe
0 siblings, 1 reply; 11+ messages in thread
From: Boris Brezillon @ 2025-04-18 8:26 UTC (permalink / raw)
To: Adrián Larumbe
Cc: linux-kernel, dri-devel, kernel, Liviu Dudau, Steven Price,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Sumit Semwal, Christian König, linux-media,
linaro-mm-sig
On Fri, 18 Apr 2025 10:11:56 +0200
Boris Brezillon <boris.brezillon@collabora.com> wrote:
> On Fri, 18 Apr 2025 03:27:07 +0100
> Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
>
> > + static const char * const gem_state_flags_names[] = {
> > + [PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED] = "imported",
FYI, the compiler seems to be happy with:
[ffs(PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED) - 1] = "imported",
but I'm not sure we want to fix it this way. The other
option would be to define bit pos in the enum and then
define flags according to these bit pos:
enum panthor_debugfs_gem_state_flags {
PANTHOR_DEBUGFS_GEM_STATE_IMPORTED_BIT = 0,
PANTHOR_DEBUGFS_GEM_STATE_EXPORTED_BIT = 1,
/** @PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED: GEM BO is PRIME imported. */
PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED = BIT(PANTHOR_DEBUGFS_GEM_STATE_IMPORTED_BIT),
/** @PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED: GEM BO is PRIME exported. */
PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED = BIT(PANTHOR_DEBUGFS_GEM_STATE_EXPORTED_BIT),
};
> > + [PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED] = "exported",
>
> Okay, I think I know where the flag indexing issue comes from:
> PANTHOR_DEBUGFS_GEM_STATE_FLAG_xx are flags, not bit positions, so we
> can't use them as indices here.
>
> > + };
> > +
> > + static const char * const gem_usage_flags_names[] = {
> > + [PANTHOR_DEBUGFS_GEM_USAGE_FLAG_KERNEL] = "kernel",
> > + [PANTHOR_DEBUGFS_GEM_USAGE_FLAG_FW_MAPPED] = "fw-mapped",
>
> Same problem here.
>
> > + };
> > +
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v9 4/4] drm/panthor: show device-wide list of DRM GEM objects over DebugFS
2025-04-18 8:26 ` Boris Brezillon
@ 2025-04-18 20:22 ` Adrián Larumbe
0 siblings, 0 replies; 11+ messages in thread
From: Adrián Larumbe @ 2025-04-18 20:22 UTC (permalink / raw)
To: Boris Brezillon
Cc: linux-kernel, dri-devel, kernel, Liviu Dudau, Steven Price,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Sumit Semwal, Christian König, linux-media,
linaro-mm-sig
On 18.04.2025 10:26, Boris Brezillon wrote:
On Fri, 18 Apr 2025 10:11:56 +0200
Boris Brezillon <boris.brezillon@collabora.com> wrote:
> On Fri, 18 Apr 2025 03:27:07 +0100
> Adrián Larumbe <adrian.larumbe@collabora.com> wrote:
>
> > + static const char * const gem_state_flags_names[] = {
> > + [PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED] = "imported",
> FYI, the compiler seems to be happy with:
>
> [ffs(PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED) - 1] = "imported",
>
> but I'm not sure we want to fix it this way. The other
> option would be to define bit pos in the enum and then
> define flags according to these bit pos:
>
> enum panthor_debugfs_gem_state_flags {
> PANTHOR_DEBUGFS_GEM_STATE_IMPORTED_BIT = 0,
> PANTHOR_DEBUGFS_GEM_STATE_EXPORTED_BIT = 1,
>
> /** @PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED: GEM BO is PRIME imported. */
> PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED = BIT(PANTHOR_DEBUGFS_GEM_STATE_IMPORTED_BIT),
>
> /** @PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED: GEM BO is PRIME exported. */
> PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED = BIT(PANTHOR_DEBUGFS_GEM_STATE_EXPORTED_BIT),
> };
I'm happy with this solution too. To be frank I thought of something like this
when I realised flag names array indices an bit values were askew by one, but
since the names array is constant and mostly meant to be read in the future by
programmers who want to add new flag meanings to the same table, I didn't think
of having a hole in index 0 as a problem.
> > + [PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED] = "exported",
>
> Okay, I think I know where the flag indexing issue comes from:
> PANTHOR_DEBUGFS_GEM_STATE_FLAG_xx are flags, not bit positions, so we
> can't use them as indices here.
>
> > + };
> > +
> > + static const char * const gem_usage_flags_names[] = {
> > + [PANTHOR_DEBUGFS_GEM_USAGE_FLAG_KERNEL] = "kernel",
> > + [PANTHOR_DEBUGFS_GEM_USAGE_FLAG_FW_MAPPED] = "fw-mapped",
>
> Same problem here.
>
> > + };
> > +
Adrian Larumbe
^ permalink raw reply [flat|nested] 11+ messages in thread