1 // SPDX-License-Identifier: GPL-2.0 or MIT 2 /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */ 3 /* Copyright 2023 Collabora ltd. */ 4 /* Copyright 2025 Amazon.com, Inc. or its affiliates */ 5 6 #include <linux/cleanup.h> 7 #include <linux/dma-buf.h> 8 #include <linux/dma-mapping.h> 9 #include <linux/err.h> 10 #include <linux/slab.h> 11 12 #include <drm/drm_print.h> 13 #include <drm/panthor_drm.h> 14 15 #include "panthor_device.h" 16 #include "panthor_drv.h" 17 #include "panthor_fw.h" 18 #include "panthor_gem.h" 19 #include "panthor_mmu.h" 20 21 void panthor_gem_init(struct panthor_device *ptdev) 22 { 23 int err; 24 25 if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && 26 !panthor_transparent_hugepage) 27 return; 28 29 err = drm_gem_huge_mnt_create(&ptdev->base, "within_size"); 30 if (drm_gem_get_huge_mnt(&ptdev->base)) 31 drm_info(&ptdev->base, "Using Transparent Hugepage\n"); 32 else if (err) 33 drm_warn(&ptdev->base, "Can't use Transparent Hugepage (%d)\n", 34 err); 35 } 36 37 #ifdef CONFIG_DEBUG_FS 38 static void panthor_gem_debugfs_bo_init(struct panthor_gem_object *bo) 39 { 40 INIT_LIST_HEAD(&bo->debugfs.node); 41 } 42 43 static void panthor_gem_debugfs_bo_add(struct panthor_gem_object *bo) 44 { 45 struct panthor_device *ptdev = container_of(bo->base.base.dev, 46 struct panthor_device, base); 47 48 bo->debugfs.creator.tgid = current->tgid; 49 get_task_comm(bo->debugfs.creator.process_name, current->group_leader); 50 51 mutex_lock(&ptdev->gems.lock); 52 list_add_tail(&bo->debugfs.node, &ptdev->gems.node); 53 mutex_unlock(&ptdev->gems.lock); 54 } 55 56 static void panthor_gem_debugfs_bo_rm(struct panthor_gem_object *bo) 57 { 58 struct panthor_device *ptdev = container_of(bo->base.base.dev, 59 struct panthor_device, base); 60 61 if (list_empty(&bo->debugfs.node)) 62 return; 63 64 mutex_lock(&ptdev->gems.lock); 65 list_del_init(&bo->debugfs.node); 66 mutex_unlock(&ptdev->gems.lock); 67 } 68 69 static void panthor_gem_debugfs_set_usage_flags(struct panthor_gem_object *bo, u32 usage_flags) 70 { 71 bo->debugfs.flags = usage_flags; 72 panthor_gem_debugfs_bo_add(bo); 73 } 74 #else 75 static void panthor_gem_debugfs_bo_rm(struct panthor_gem_object *bo) {} 76 static void panthor_gem_debugfs_set_usage_flags(struct panthor_gem_object *bo, u32 usage_flags) {} 77 static void panthor_gem_debugfs_bo_init(struct panthor_gem_object *bo) {} 78 #endif 79 80 static bool 81 should_map_wc(struct panthor_gem_object *bo, struct panthor_vm *exclusive_vm) 82 { 83 struct panthor_device *ptdev = container_of(bo->base.base.dev, struct panthor_device, base); 84 85 /* We can't do uncached mappings if the device is coherent, 86 * because the zeroing done by the shmem layer at page allocation 87 * time happens on a cached mapping which isn't CPU-flushed (at least 88 * not on Arm64 where the flush is deferred to PTE setup time, and 89 * only done conditionally based on the mapping permissions). We can't 90 * rely on dma_map_sgtable()/dma_sync_sgtable_for_xxx() either to flush 91 * those, because they are NOPed if dma_dev_coherent() returns true. 92 * 93 * FIXME: Note that this problem is going to pop up again when we 94 * decide to support mapping buffers with the NO_MMAP flag as 95 * non-shareable (AKA buffers accessed only by the GPU), because we 96 * need the same CPU flush to happen after page allocation, otherwise 97 * there's a risk of data leak or late corruption caused by a dirty 98 * cacheline being evicted. At this point we'll need a way to force 99 * CPU cache maintenance regardless of whether the device is coherent 100 * or not. 101 */ 102 if (ptdev->coherent) 103 return false; 104 105 /* Cached mappings are explicitly requested, so no write-combine. */ 106 if (bo->flags & DRM_PANTHOR_BO_WB_MMAP) 107 return false; 108 109 /* The default is write-combine. */ 110 return true; 111 } 112 113 static void panthor_gem_free_object(struct drm_gem_object *obj) 114 { 115 struct panthor_gem_object *bo = to_panthor_bo(obj); 116 struct drm_gem_object *vm_root_gem = bo->exclusive_vm_root_gem; 117 118 panthor_gem_debugfs_bo_rm(bo); 119 120 /* 121 * Label might have been allocated with kstrdup_const(), 122 * we need to take that into account when freeing the memory 123 */ 124 kfree_const(bo->label.str); 125 126 mutex_destroy(&bo->label.lock); 127 128 drm_gem_free_mmap_offset(&bo->base.base); 129 drm_gem_shmem_free(&bo->base); 130 drm_gem_object_put(vm_root_gem); 131 } 132 133 /** 134 * panthor_kernel_bo_destroy() - Destroy a kernel buffer object 135 * @bo: Kernel buffer object to destroy. If NULL or an ERR_PTR(), the destruction 136 * is skipped. 137 */ 138 void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo) 139 { 140 struct panthor_vm *vm; 141 142 if (IS_ERR_OR_NULL(bo)) 143 return; 144 145 vm = bo->vm; 146 panthor_kernel_bo_vunmap(bo); 147 148 drm_WARN_ON(bo->obj->dev, 149 to_panthor_bo(bo->obj)->exclusive_vm_root_gem != panthor_vm_root_gem(vm)); 150 panthor_vm_unmap_range(vm, bo->va_node.start, bo->va_node.size); 151 panthor_vm_free_va(vm, &bo->va_node); 152 drm_gem_object_put(bo->obj); 153 panthor_vm_put(vm); 154 kfree(bo); 155 } 156 157 /** 158 * panthor_kernel_bo_create() - Create and map a GEM object to a VM 159 * @ptdev: Device. 160 * @vm: VM to map the GEM to. If NULL, the kernel object is not GPU mapped. 161 * @size: Size of the buffer object. 162 * @bo_flags: Combination of drm_panthor_bo_flags flags. 163 * @vm_map_flags: Combination of drm_panthor_vm_bind_op_flags (only those 164 * that are related to map operations). 165 * @gpu_va: GPU address assigned when mapping to the VM. 166 * If gpu_va == PANTHOR_VM_KERNEL_AUTO_VA, the virtual address will be 167 * automatically allocated. 168 * @name: Descriptive label of the BO's contents 169 * 170 * Return: A valid pointer in case of success, an ERR_PTR() otherwise. 171 */ 172 struct panthor_kernel_bo * 173 panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm, 174 size_t size, u32 bo_flags, u32 vm_map_flags, 175 u64 gpu_va, const char *name) 176 { 177 struct drm_gem_shmem_object *obj; 178 struct panthor_kernel_bo *kbo; 179 struct panthor_gem_object *bo; 180 u32 debug_flags = PANTHOR_DEBUGFS_GEM_USAGE_FLAG_KERNEL; 181 int ret; 182 183 if (drm_WARN_ON(&ptdev->base, !vm)) 184 return ERR_PTR(-EINVAL); 185 186 kbo = kzalloc(sizeof(*kbo), GFP_KERNEL); 187 if (!kbo) 188 return ERR_PTR(-ENOMEM); 189 190 obj = drm_gem_shmem_create(&ptdev->base, size); 191 if (IS_ERR(obj)) { 192 ret = PTR_ERR(obj); 193 goto err_free_bo; 194 } 195 196 bo = to_panthor_bo(&obj->base); 197 kbo->obj = &obj->base; 198 bo->flags = bo_flags; 199 bo->base.map_wc = should_map_wc(bo, vm); 200 bo->exclusive_vm_root_gem = panthor_vm_root_gem(vm); 201 drm_gem_object_get(bo->exclusive_vm_root_gem); 202 bo->base.base.resv = bo->exclusive_vm_root_gem->resv; 203 204 if (vm == panthor_fw_vm(ptdev)) 205 debug_flags |= PANTHOR_DEBUGFS_GEM_USAGE_FLAG_FW_MAPPED; 206 207 panthor_gem_kernel_bo_set_label(kbo, name); 208 panthor_gem_debugfs_set_usage_flags(to_panthor_bo(kbo->obj), debug_flags); 209 210 /* The system and GPU MMU page size might differ, which becomes a 211 * problem for FW sections that need to be mapped at explicit address 212 * since our PAGE_SIZE alignment might cover a VA range that's 213 * expected to be used for another section. 214 * Make sure we never map more than we need. 215 */ 216 size = ALIGN(size, panthor_vm_page_size(vm)); 217 ret = panthor_vm_alloc_va(vm, gpu_va, size, &kbo->va_node); 218 if (ret) 219 goto err_put_obj; 220 221 ret = panthor_vm_map_bo_range(vm, bo, 0, size, kbo->va_node.start, vm_map_flags); 222 if (ret) 223 goto err_free_va; 224 225 kbo->vm = panthor_vm_get(vm); 226 return kbo; 227 228 err_free_va: 229 panthor_vm_free_va(vm, &kbo->va_node); 230 231 err_put_obj: 232 drm_gem_object_put(&obj->base); 233 234 err_free_bo: 235 kfree(kbo); 236 return ERR_PTR(ret); 237 } 238 239 static struct sg_table * 240 panthor_gem_prime_map_dma_buf(struct dma_buf_attachment *attach, 241 enum dma_data_direction dir) 242 { 243 struct sg_table *sgt = drm_gem_map_dma_buf(attach, dir); 244 245 if (!IS_ERR(sgt)) 246 attach->priv = sgt; 247 248 return sgt; 249 } 250 251 static void 252 panthor_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach, 253 struct sg_table *sgt, 254 enum dma_data_direction dir) 255 { 256 attach->priv = NULL; 257 drm_gem_unmap_dma_buf(attach, sgt, dir); 258 } 259 260 static int 261 panthor_gem_prime_begin_cpu_access(struct dma_buf *dma_buf, 262 enum dma_data_direction dir) 263 { 264 struct drm_gem_object *obj = dma_buf->priv; 265 struct drm_device *dev = obj->dev; 266 struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); 267 struct dma_buf_attachment *attach; 268 269 dma_resv_lock(obj->resv, NULL); 270 if (shmem->sgt) 271 dma_sync_sgtable_for_cpu(dev->dev, shmem->sgt, dir); 272 273 if (shmem->vaddr) 274 invalidate_kernel_vmap_range(shmem->vaddr, shmem->base.size); 275 276 list_for_each_entry(attach, &dma_buf->attachments, node) { 277 struct sg_table *sgt = attach->priv; 278 279 if (sgt) 280 dma_sync_sgtable_for_cpu(attach->dev, sgt, dir); 281 } 282 dma_resv_unlock(obj->resv); 283 284 return 0; 285 } 286 287 static int 288 panthor_gem_prime_end_cpu_access(struct dma_buf *dma_buf, 289 enum dma_data_direction dir) 290 { 291 struct drm_gem_object *obj = dma_buf->priv; 292 struct drm_device *dev = obj->dev; 293 struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); 294 struct dma_buf_attachment *attach; 295 296 dma_resv_lock(obj->resv, NULL); 297 list_for_each_entry(attach, &dma_buf->attachments, node) { 298 struct sg_table *sgt = attach->priv; 299 300 if (sgt) 301 dma_sync_sgtable_for_device(attach->dev, sgt, dir); 302 } 303 304 if (shmem->vaddr) 305 flush_kernel_vmap_range(shmem->vaddr, shmem->base.size); 306 307 if (shmem->sgt) 308 dma_sync_sgtable_for_device(dev->dev, shmem->sgt, dir); 309 310 dma_resv_unlock(obj->resv); 311 return 0; 312 } 313 314 static const struct dma_buf_ops panthor_dma_buf_ops = { 315 .attach = drm_gem_map_attach, 316 .detach = drm_gem_map_detach, 317 .map_dma_buf = panthor_gem_prime_map_dma_buf, 318 .unmap_dma_buf = panthor_gem_prime_unmap_dma_buf, 319 .release = drm_gem_dmabuf_release, 320 .mmap = drm_gem_dmabuf_mmap, 321 .vmap = drm_gem_dmabuf_vmap, 322 .vunmap = drm_gem_dmabuf_vunmap, 323 .begin_cpu_access = panthor_gem_prime_begin_cpu_access, 324 .end_cpu_access = panthor_gem_prime_end_cpu_access, 325 }; 326 327 static struct dma_buf * 328 panthor_gem_prime_export(struct drm_gem_object *obj, int flags) 329 { 330 struct drm_device *dev = obj->dev; 331 struct dma_buf_export_info exp_info = { 332 .exp_name = KBUILD_MODNAME, 333 .owner = THIS_MODULE, 334 .ops = &panthor_dma_buf_ops, 335 .size = obj->size, 336 .flags = flags, 337 .priv = obj, 338 .resv = obj->resv, 339 }; 340 341 /* We can't export GEMs that have an exclusive VM. */ 342 if (to_panthor_bo(obj)->exclusive_vm_root_gem) 343 return ERR_PTR(-EINVAL); 344 345 return drm_gem_dmabuf_export(dev, &exp_info); 346 } 347 348 struct drm_gem_object * 349 panthor_gem_prime_import(struct drm_device *dev, 350 struct dma_buf *dma_buf) 351 { 352 struct drm_gem_object *obj = dma_buf->priv; 353 354 if (dma_buf->ops == &panthor_dma_buf_ops && obj->dev == dev) { 355 /* Importing dmabuf exported from our own gem increases 356 * refcount on gem itself instead of f_count of dmabuf. 357 */ 358 drm_gem_object_get(obj); 359 return obj; 360 } 361 362 return drm_gem_prime_import(dev, dma_buf); 363 } 364 365 static enum drm_gem_object_status panthor_gem_status(struct drm_gem_object *obj) 366 { 367 struct panthor_gem_object *bo = to_panthor_bo(obj); 368 enum drm_gem_object_status res = 0; 369 370 if (drm_gem_is_imported(&bo->base.base) || bo->base.pages) 371 res |= DRM_GEM_OBJECT_RESIDENT; 372 373 return res; 374 } 375 376 static const struct drm_gem_object_funcs panthor_gem_funcs = { 377 .free = panthor_gem_free_object, 378 .print_info = drm_gem_shmem_object_print_info, 379 .pin = drm_gem_shmem_object_pin, 380 .unpin = drm_gem_shmem_object_unpin, 381 .get_sg_table = drm_gem_shmem_object_get_sg_table, 382 .vmap = drm_gem_shmem_object_vmap, 383 .vunmap = drm_gem_shmem_object_vunmap, 384 .mmap = drm_gem_shmem_object_mmap, 385 .status = panthor_gem_status, 386 .export = panthor_gem_prime_export, 387 .vm_ops = &drm_gem_shmem_vm_ops, 388 }; 389 390 /** 391 * panthor_gem_create_object - Implementation of driver->gem_create_object. 392 * @ddev: DRM device 393 * @size: Size in bytes of the memory the object will reference 394 * 395 * This lets the GEM helpers allocate object structs for us, and keep 396 * our BO stats correct. 397 */ 398 struct drm_gem_object *panthor_gem_create_object(struct drm_device *ddev, size_t size) 399 { 400 struct panthor_gem_object *obj; 401 402 obj = kzalloc(sizeof(*obj), GFP_KERNEL); 403 if (!obj) 404 return ERR_PTR(-ENOMEM); 405 406 obj->base.base.funcs = &panthor_gem_funcs; 407 mutex_init(&obj->label.lock); 408 409 panthor_gem_debugfs_bo_init(obj); 410 411 return &obj->base.base; 412 } 413 414 /** 415 * panthor_gem_create_with_handle() - Create a GEM object and attach it to a handle. 416 * @file: DRM file. 417 * @ddev: DRM device. 418 * @exclusive_vm: Exclusive VM. Not NULL if the GEM object can't be shared. 419 * @size: Size of the GEM object to allocate. 420 * @flags: Combination of drm_panthor_bo_flags flags. 421 * @handle: Pointer holding the handle pointing to the new GEM object. 422 * 423 * Return: Zero on success 424 */ 425 int 426 panthor_gem_create_with_handle(struct drm_file *file, 427 struct drm_device *ddev, 428 struct panthor_vm *exclusive_vm, 429 u64 *size, u32 flags, u32 *handle) 430 { 431 int ret; 432 struct drm_gem_shmem_object *shmem; 433 struct panthor_gem_object *bo; 434 435 shmem = drm_gem_shmem_create(ddev, *size); 436 if (IS_ERR(shmem)) 437 return PTR_ERR(shmem); 438 439 bo = to_panthor_bo(&shmem->base); 440 bo->flags = flags; 441 bo->base.map_wc = should_map_wc(bo, exclusive_vm); 442 443 if (exclusive_vm) { 444 bo->exclusive_vm_root_gem = panthor_vm_root_gem(exclusive_vm); 445 drm_gem_object_get(bo->exclusive_vm_root_gem); 446 bo->base.base.resv = bo->exclusive_vm_root_gem->resv; 447 } 448 449 panthor_gem_debugfs_set_usage_flags(bo, 0); 450 451 /* If this is a write-combine mapping, we query the sgt to force a CPU 452 * cache flush (dma_map_sgtable() is called when the sgt is created). 453 * This ensures the zero-ing is visible to any uncached mapping created 454 * by vmap/mmap. 455 * FIXME: Ideally this should be done when pages are allocated, not at 456 * BO creation time. 457 */ 458 if (shmem->map_wc) { 459 struct sg_table *sgt; 460 461 sgt = drm_gem_shmem_get_pages_sgt(shmem); 462 if (IS_ERR(sgt)) { 463 ret = PTR_ERR(sgt); 464 goto out_put_gem; 465 } 466 } 467 468 /* 469 * Allocate an id of idr table where the obj is registered 470 * and handle has the id what user can see. 471 */ 472 ret = drm_gem_handle_create(file, &shmem->base, handle); 473 if (!ret) 474 *size = bo->base.base.size; 475 476 out_put_gem: 477 /* drop reference from allocate - handle holds it now. */ 478 drm_gem_object_put(&shmem->base); 479 480 return ret; 481 } 482 483 void 484 panthor_gem_bo_set_label(struct drm_gem_object *obj, const char *label) 485 { 486 struct panthor_gem_object *bo = to_panthor_bo(obj); 487 const char *old_label; 488 489 scoped_guard(mutex, &bo->label.lock) { 490 old_label = bo->label.str; 491 bo->label.str = label; 492 } 493 494 kfree_const(old_label); 495 } 496 497 void 498 panthor_gem_kernel_bo_set_label(struct panthor_kernel_bo *bo, const char *label) 499 { 500 const char *str; 501 502 /* We should never attempt labelling a UM-exposed GEM object */ 503 if (drm_WARN_ON(bo->obj->dev, bo->obj->handle_count > 0)) 504 return; 505 506 if (!label) 507 return; 508 509 str = kstrdup_const(label, GFP_KERNEL); 510 if (!str) { 511 /* Failing to allocate memory for a label isn't a fatal condition */ 512 drm_warn(bo->obj->dev, "Not enough memory to allocate BO label"); 513 return; 514 } 515 516 panthor_gem_bo_set_label(bo->obj, str); 517 } 518 519 int 520 panthor_gem_sync(struct drm_gem_object *obj, u32 type, 521 u64 offset, u64 size) 522 { 523 struct panthor_gem_object *bo = to_panthor_bo(obj); 524 struct drm_gem_shmem_object *shmem = &bo->base; 525 const struct drm_device *dev = shmem->base.dev; 526 struct sg_table *sgt; 527 struct scatterlist *sgl; 528 unsigned int count; 529 530 /* Make sure the range is in bounds. */ 531 if (offset + size < offset || offset + size > shmem->base.size) 532 return -EINVAL; 533 534 /* Disallow CPU-cache maintenance on imported buffers. */ 535 if (drm_gem_is_imported(&shmem->base)) 536 return -EINVAL; 537 538 switch (type) { 539 case DRM_PANTHOR_BO_SYNC_CPU_CACHE_FLUSH: 540 case DRM_PANTHOR_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE: 541 break; 542 543 default: 544 return -EINVAL; 545 } 546 547 /* Don't bother if it's WC-mapped */ 548 if (shmem->map_wc) 549 return 0; 550 551 /* Nothing to do if the size is zero. */ 552 if (size == 0) 553 return 0; 554 555 sgt = drm_gem_shmem_get_pages_sgt(shmem); 556 if (IS_ERR(sgt)) 557 return PTR_ERR(sgt); 558 559 for_each_sgtable_dma_sg(sgt, sgl, count) { 560 if (size == 0) 561 break; 562 563 dma_addr_t paddr = sg_dma_address(sgl); 564 size_t len = sg_dma_len(sgl); 565 566 if (len <= offset) { 567 offset -= len; 568 continue; 569 } 570 571 paddr += offset; 572 len -= offset; 573 len = min_t(size_t, len, size); 574 size -= len; 575 offset = 0; 576 577 /* It's unclear whether dma_sync_xxx() is the right API to do CPU 578 * cache maintenance given an IOMMU can register their own 579 * implementation doing more than just CPU cache flushes/invalidation, 580 * and what we really care about here is CPU caches only, but that's 581 * the best we have that is both arch-agnostic and does at least the 582 * CPU cache maintenance on a <page,offset,size> tuple. 583 * 584 * Also, I wish we could do a single 585 * 586 * dma_sync_single_for_device(BIDIR) 587 * 588 * and get a flush+invalidate, but that's not how it's implemented 589 * in practice (at least on arm64), so we have to make it 590 * 591 * dma_sync_single_for_device(TO_DEVICE) 592 * dma_sync_single_for_cpu(FROM_DEVICE) 593 * 594 * for the flush+invalidate case. 595 */ 596 dma_sync_single_for_device(dev->dev, paddr, len, DMA_TO_DEVICE); 597 if (type == DRM_PANTHOR_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE) 598 dma_sync_single_for_cpu(dev->dev, paddr, len, DMA_FROM_DEVICE); 599 } 600 601 return 0; 602 } 603 604 #ifdef CONFIG_DEBUG_FS 605 struct gem_size_totals { 606 size_t size; 607 size_t resident; 608 size_t reclaimable; 609 }; 610 611 static void panthor_gem_debugfs_print_flag_names(struct seq_file *m) 612 { 613 int len; 614 int i; 615 616 static const char * const gem_state_flags_names[] = { 617 [PANTHOR_DEBUGFS_GEM_STATE_IMPORTED_BIT] = "imported", 618 [PANTHOR_DEBUGFS_GEM_STATE_EXPORTED_BIT] = "exported", 619 }; 620 621 static const char * const gem_usage_flags_names[] = { 622 [PANTHOR_DEBUGFS_GEM_USAGE_KERNEL_BIT] = "kernel", 623 [PANTHOR_DEBUGFS_GEM_USAGE_FW_MAPPED_BIT] = "fw-mapped", 624 }; 625 626 seq_puts(m, "GEM state flags: "); 627 for (i = 0, len = ARRAY_SIZE(gem_state_flags_names); i < len; i++) { 628 if (!gem_state_flags_names[i]) 629 continue; 630 seq_printf(m, "%s (0x%x)%s", gem_state_flags_names[i], 631 (u32)BIT(i), (i < len - 1) ? ", " : "\n"); 632 } 633 634 seq_puts(m, "GEM usage flags: "); 635 for (i = 0, len = ARRAY_SIZE(gem_usage_flags_names); i < len; i++) { 636 if (!gem_usage_flags_names[i]) 637 continue; 638 seq_printf(m, "%s (0x%x)%s", gem_usage_flags_names[i], 639 (u32)BIT(i), (i < len - 1) ? ", " : "\n\n"); 640 } 641 } 642 643 static void panthor_gem_debugfs_bo_print(struct panthor_gem_object *bo, 644 struct seq_file *m, 645 struct gem_size_totals *totals) 646 { 647 unsigned int refcount = kref_read(&bo->base.base.refcount); 648 char creator_info[32] = {}; 649 size_t resident_size; 650 u32 gem_usage_flags = bo->debugfs.flags; 651 u32 gem_state_flags = 0; 652 653 /* Skip BOs being destroyed. */ 654 if (!refcount) 655 return; 656 657 resident_size = bo->base.pages ? bo->base.base.size : 0; 658 659 snprintf(creator_info, sizeof(creator_info), 660 "%s/%d", bo->debugfs.creator.process_name, bo->debugfs.creator.tgid); 661 seq_printf(m, "%-32s%-16d%-16d%-16zd%-16zd0x%-16lx", 662 creator_info, 663 bo->base.base.name, 664 refcount, 665 bo->base.base.size, 666 resident_size, 667 drm_vma_node_start(&bo->base.base.vma_node)); 668 669 if (bo->base.base.import_attach) 670 gem_state_flags |= PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED; 671 if (bo->base.base.dma_buf) 672 gem_state_flags |= PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED; 673 674 seq_printf(m, "0x%-8x 0x%-10x", gem_state_flags, gem_usage_flags); 675 676 scoped_guard(mutex, &bo->label.lock) { 677 seq_printf(m, "%s\n", bo->label.str ? : ""); 678 } 679 680 totals->size += bo->base.base.size; 681 totals->resident += resident_size; 682 if (bo->base.madv > 0) 683 totals->reclaimable += resident_size; 684 } 685 686 void panthor_gem_debugfs_print_bos(struct panthor_device *ptdev, 687 struct seq_file *m) 688 { 689 struct gem_size_totals totals = {0}; 690 struct panthor_gem_object *bo; 691 692 panthor_gem_debugfs_print_flag_names(m); 693 694 seq_puts(m, "created-by global-name refcount size resident-size file-offset state usage label\n"); 695 seq_puts(m, "----------------------------------------------------------------------------------------------------------------------------------------------\n"); 696 697 scoped_guard(mutex, &ptdev->gems.lock) { 698 list_for_each_entry(bo, &ptdev->gems.node, debugfs.node) { 699 panthor_gem_debugfs_bo_print(bo, m, &totals); 700 } 701 } 702 703 seq_puts(m, "==============================================================================================================================================\n"); 704 seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n", 705 totals.size, totals.resident, totals.reclaimable); 706 } 707 #endif 708