Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/panfrost: show device-wide list of DRM GEM objects over DebugFS

This change is essentially a Panfrost port of commit a3707f53eb3f
("drm/panthor: show device-wide list of DRM GEM objects over DebugFS").

The DebugFS file is almost the same as in Panthor, minus the GEM object
usage flags, since Panfrost has no kernel-only BO's.

Two additional GEM state flags which are displayed but aren't relevant
to Panthor are 'Purged' and 'Purgeable', since Panfrost implements an
explicit shrinker and a madvise ioctl to flag objects as reclaimable.

Signed-off-by: Adrián Larumbe <adrian.larumbe@collabora.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Signed-off-by: Steven Price <steven.price@arm.com>
Link: https://lore.kernel.org/r/20250520174634.353267-5-adrian.larumbe@collabora.com

authored by

Adrián Larumbe and committed by
Steven Price
e48ade5e 2f684bbb

+236
+5
drivers/gpu/drm/panfrost/panfrost_device.c
··· 209 209 210 210 spin_lock_init(&pfdev->cycle_counter.lock); 211 211 212 + #ifdef CONFIG_DEBUG_FS 213 + mutex_init(&pfdev->debugfs.gems_lock); 214 + INIT_LIST_HEAD(&pfdev->debugfs.gems_list); 215 + #endif 216 + 212 217 err = panfrost_pm_domain_init(pfdev); 213 218 if (err) 214 219 return err;
+15
drivers/gpu/drm/panfrost/panfrost_device.h
··· 111 111 u8 gpu_quirks; 112 112 }; 113 113 114 + /** 115 + * struct panfrost_device_debugfs - Device-wide DebugFS tracking structures 116 + */ 117 + struct panfrost_device_debugfs { 118 + /** @gems_list: Device-wide list of GEM objects owned by at least one file. */ 119 + struct list_head gems_list; 120 + 121 + /** @gems_lock: Serializes access to the device-wide list of GEM objects. */ 122 + struct mutex gems_lock; 123 + }; 124 + 114 125 struct panfrost_device { 115 126 struct device *dev; 116 127 struct drm_device *ddev; ··· 175 164 atomic_t use_count; 176 165 spinlock_t lock; 177 166 } cycle_counter; 167 + 168 + #ifdef CONFIG_DEBUG_FS 169 + struct panfrost_device_debugfs debugfs; 170 + #endif 178 171 }; 179 172 180 173 struct panfrost_mmu {
+35
drivers/gpu/drm/panfrost/panfrost_drv.c
··· 13 13 #include <linux/platform_device.h> 14 14 #include <linux/pm_runtime.h> 15 15 #include <drm/panfrost_drm.h> 16 + #include <drm/drm_debugfs.h> 16 17 #include <drm/drm_drv.h> 17 18 #include <drm/drm_ioctl.h> 18 19 #include <drm/drm_syncobj.h> ··· 660 659 .show_fdinfo = drm_show_fdinfo, 661 660 }; 662 661 662 + #ifdef CONFIG_DEBUG_FS 663 + static int panthor_gems_show(struct seq_file *m, void *data) 664 + { 665 + struct drm_info_node *node = m->private; 666 + struct drm_device *dev = node->minor->dev; 667 + struct panfrost_device *pfdev = dev->dev_private; 668 + 669 + panfrost_gem_debugfs_print_bos(pfdev, m); 670 + 671 + return 0; 672 + } 673 + 674 + static struct drm_info_list panthor_debugfs_list[] = { 675 + {"gems", panthor_gems_show, 0, NULL}, 676 + }; 677 + 678 + static int panthor_gems_debugfs_init(struct drm_minor *minor) 679 + { 680 + drm_debugfs_create_files(panthor_debugfs_list, 681 + ARRAY_SIZE(panthor_debugfs_list), 682 + minor->debugfs_root, minor); 683 + 684 + return 0; 685 + } 686 + 687 + static void panfrost_debugfs_init(struct drm_minor *minor) 688 + { 689 + panthor_gems_debugfs_init(minor); 690 + } 691 + #endif 692 + 663 693 /* 664 694 * Panfrost driver version: 665 695 * - 1.0 - initial interface ··· 715 683 716 684 .gem_create_object = panfrost_gem_create_object, 717 685 .gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table, 686 + #ifdef CONFIG_DEBUG_FS 687 + .debugfs_init = panfrost_debugfs_init, 688 + #endif 718 689 }; 719 690 720 691 static int panfrost_probe(struct platform_device *pdev)
+134
drivers/gpu/drm/panfrost/panfrost_gem.c
··· 12 12 #include "panfrost_gem.h" 13 13 #include "panfrost_mmu.h" 14 14 15 + #ifdef CONFIG_DEBUG_FS 16 + static void panfrost_gem_debugfs_bo_add(struct panfrost_device *pfdev, 17 + struct panfrost_gem_object *bo) 18 + { 19 + bo->debugfs.creator.tgid = current->group_leader->pid; 20 + get_task_comm(bo->debugfs.creator.process_name, current->group_leader); 21 + 22 + mutex_lock(&pfdev->debugfs.gems_lock); 23 + list_add_tail(&bo->debugfs.node, &pfdev->debugfs.gems_list); 24 + mutex_unlock(&pfdev->debugfs.gems_lock); 25 + } 26 + 27 + static void panfrost_gem_debugfs_bo_rm(struct panfrost_gem_object *bo) 28 + { 29 + struct panfrost_device *pfdev = bo->base.base.dev->dev_private; 30 + 31 + if (list_empty(&bo->debugfs.node)) 32 + return; 33 + 34 + mutex_lock(&pfdev->debugfs.gems_lock); 35 + list_del_init(&bo->debugfs.node); 36 + mutex_unlock(&pfdev->debugfs.gems_lock); 37 + } 38 + #else 39 + static void panfrost_gem_debugfs_bo_add(struct panfrost_device *pfdev, 40 + struct panfrost_gem_object *bo) 41 + {} 42 + static void panfrost_gem_debugfs_bo_rm(struct panfrost_gem_object *bo) {} 43 + #endif 44 + 15 45 /* Called DRM core on the last userspace/kernel unreference of the 16 46 * BO. 17 47 */ ··· 67 37 WARN_ON_ONCE(!list_empty(&bo->mappings.list)); 68 38 69 39 kfree_const(bo->label.str); 40 + panfrost_gem_debugfs_bo_rm(bo); 70 41 mutex_destroy(&bo->label.lock); 71 42 72 43 if (bo->sgts) { ··· 297 266 obj->base.map_wc = !pfdev->coherent; 298 267 mutex_init(&obj->label.lock); 299 268 269 + panfrost_gem_debugfs_bo_add(pfdev, obj); 270 + 300 271 return &obj->base.base; 301 272 } 302 273 ··· 387 354 388 355 panfrost_gem_set_label(obj, str); 389 356 } 357 + 358 + #ifdef CONFIG_DEBUG_FS 359 + struct gem_size_totals { 360 + size_t size; 361 + size_t resident; 362 + size_t reclaimable; 363 + }; 364 + 365 + struct flag_def { 366 + u32 flag; 367 + const char *name; 368 + }; 369 + 370 + static void panfrost_gem_debugfs_print_flag_names(struct seq_file *m) 371 + { 372 + int len; 373 + int i; 374 + 375 + static const struct flag_def gem_state_flags_names[] = { 376 + {PANFROST_DEBUGFS_GEM_STATE_FLAG_IMPORTED, "imported"}, 377 + {PANFROST_DEBUGFS_GEM_STATE_FLAG_EXPORTED, "exported"}, 378 + {PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGED, "purged"}, 379 + {PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGEABLE, "purgeable"}, 380 + }; 381 + 382 + seq_puts(m, "GEM state flags: "); 383 + for (i = 0, len = ARRAY_SIZE(gem_state_flags_names); i < len; i++) { 384 + seq_printf(m, "%s (0x%x)%s", gem_state_flags_names[i].name, 385 + gem_state_flags_names[i].flag, (i < len - 1) ? ", " : "\n\n"); 386 + } 387 + } 388 + 389 + static void panfrost_gem_debugfs_bo_print(struct panfrost_gem_object *bo, 390 + struct seq_file *m, 391 + struct gem_size_totals *totals) 392 + { 393 + unsigned int refcount = kref_read(&bo->base.base.refcount); 394 + char creator_info[32] = {}; 395 + size_t resident_size; 396 + u32 gem_state_flags = 0; 397 + 398 + /* Skip BOs being destroyed. */ 399 + if (!refcount) 400 + return; 401 + 402 + resident_size = bo->base.pages ? bo->base.base.size : 0; 403 + 404 + snprintf(creator_info, sizeof(creator_info), 405 + "%s/%d", bo->debugfs.creator.process_name, bo->debugfs.creator.tgid); 406 + seq_printf(m, "%-32s%-16d%-16d%-16zd%-16zd0x%-16lx", 407 + creator_info, 408 + bo->base.base.name, 409 + refcount, 410 + bo->base.base.size, 411 + resident_size, 412 + drm_vma_node_start(&bo->base.base.vma_node)); 413 + 414 + if (bo->base.base.import_attach) 415 + gem_state_flags |= PANFROST_DEBUGFS_GEM_STATE_FLAG_IMPORTED; 416 + if (bo->base.base.dma_buf) 417 + gem_state_flags |= PANFROST_DEBUGFS_GEM_STATE_FLAG_EXPORTED; 418 + 419 + if (bo->base.madv < 0) 420 + gem_state_flags |= PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGED; 421 + else if (bo->base.madv > 0) 422 + gem_state_flags |= PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGEABLE; 423 + 424 + seq_printf(m, "0x%-10x", gem_state_flags); 425 + 426 + scoped_guard(mutex, &bo->label.lock) { 427 + seq_printf(m, "%s\n", bo->label.str ? : ""); 428 + } 429 + 430 + totals->size += bo->base.base.size; 431 + totals->resident += resident_size; 432 + if (bo->base.madv > 0) 433 + totals->reclaimable += resident_size; 434 + } 435 + 436 + void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev, 437 + struct seq_file *m) 438 + { 439 + struct gem_size_totals totals = {0}; 440 + struct panfrost_gem_object *bo; 441 + 442 + panfrost_gem_debugfs_print_flag_names(m); 443 + 444 + seq_puts(m, "created-by global-name refcount size resident-size file-offset state label\n"); 445 + seq_puts(m, "-----------------------------------------------------------------------------------------------------------------------------------\n"); 446 + 447 + scoped_guard(mutex, &pfdev->debugfs.gems_lock) { 448 + list_for_each_entry(bo, &pfdev->debugfs.gems_list, debugfs.node) { 449 + panfrost_gem_debugfs_bo_print(bo, m, &totals); 450 + } 451 + } 452 + 453 + seq_puts(m, "===================================================================================================================================\n"); 454 + seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n", 455 + totals.size, totals.resident, totals.reclaimable); 456 + } 457 + #endif
+47
drivers/gpu/drm/panfrost/panfrost_gem.h
··· 8 8 #include <drm/drm_mm.h> 9 9 10 10 struct panfrost_mmu; 11 + struct panfrost_device; 11 12 12 13 #define PANFROST_BO_LABEL_MAXLEN 4096 14 + 15 + enum panfrost_debugfs_gem_state_flags { 16 + /** @PANFROST_DEBUGFS_GEM_STATE_FLAG_IMPORTED: GEM BO is PRIME imported. */ 17 + PANFROST_DEBUGFS_GEM_STATE_FLAG_IMPORTED = BIT(0), 18 + 19 + /** @PANFROST_DEBUGFS_GEM_STATE_FLAG_EXPORTED: GEM BO is PRIME exported. */ 20 + PANFROST_DEBUGFS_GEM_STATE_FLAG_EXPORTED = BIT(1), 21 + 22 + /** @PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGED: GEM BO was reclaimed by the shrinker. */ 23 + PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGED = BIT(2), 24 + 25 + /** 26 + * @PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGEABLE: GEM BO pages were marked as no longer 27 + * needed by UM and can be reclaimed by the shrinker. 28 + */ 29 + PANFROST_DEBUGFS_GEM_STATE_FLAG_PURGEABLE = BIT(3), 30 + }; 31 + 32 + /** 33 + * struct panfrost_gem_debugfs - GEM object's DebugFS list information 34 + */ 35 + struct panfrost_gem_debugfs { 36 + /** 37 + * @node: Node used to insert the object in the device-wide list of 38 + * GEM objects, to display information about it through a DebugFS file. 39 + */ 40 + struct list_head node; 41 + 42 + /** @creator: Information about the UM process which created the GEM. */ 43 + struct { 44 + /** @creator.process_name: Group leader name in owning thread's process */ 45 + char process_name[TASK_COMM_LEN]; 46 + 47 + /** @creator.tgid: PID of the thread's group leader within its process */ 48 + pid_t tgid; 49 + } creator; 50 + }; 13 51 14 52 struct panfrost_gem_object { 15 53 struct drm_gem_shmem_object base; ··· 97 59 98 60 bool noexec :1; 99 61 bool is_heap :1; 62 + 63 + #ifdef CONFIG_DEBUG_FS 64 + struct panfrost_gem_debugfs debugfs; 65 + #endif 100 66 }; 101 67 102 68 struct panfrost_gem_mapping { ··· 149 107 150 108 void panfrost_gem_set_label(struct drm_gem_object *obj, const char *label); 151 109 void panfrost_gem_internal_set_label(struct drm_gem_object *obj, const char *label); 110 + 111 + #ifdef CONFIG_DEBUG_FS 112 + void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev, 113 + struct seq_file *m); 114 + #endif 152 115 153 116 #endif /* __PANFROST_GEM_H__ */