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

drm/xe: Use separate rpm lockdep map for non-d3cold-capable devices

For non-d3cold-capable devices we'd like to be able to wake up the
device from reclaim. In particular, for Lunar Lake we'd like to be
able to blit CCS metadata to system at shrink time; at least from
kswapd where it's reasonable OK to wait for rpm resume and a
preceding rpm suspend.

Therefore use a separate lockdep map for such devices and prime it
reclaim-tainted.

v2:
- Rename lockmap acquire- and release functions. (Rodrigo Vivi).
- Reinstate the old xe_pm_runtime_lockdep_prime() function and
rename it to xe_rpm_might_enter_cb(). (Matthew Auld).
- Introduce a separate xe_pm_runtime_lockdep_prime function
called from module init for known required locking orders.
v3:
- Actually hook up the prime function at module init.
v4:
- Rebase.
v5:
- Don't use reclaim-safe RPM with sriov.

Cc: "Vivi, Rodrigo" <rodrigo.vivi@intel.com>
Cc: "Auld, Matthew" <matthew.auld@intel.com>
Signed-off-by: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240826143450.92511-1-thomas.hellstrom@linux.intel.com

+80 -14
+9
drivers/gpu/drm/xe/xe_module.c
··· 13 13 #include "xe_drv.h" 14 14 #include "xe_hw_fence.h" 15 15 #include "xe_pci.h" 16 + #include "xe_pm.h" 16 17 #include "xe_observation.h" 17 18 #include "xe_sched_job.h" 18 19 ··· 77 76 void (*exit)(void); 78 77 }; 79 78 79 + static void xe_dummy_exit(void) 80 + { 81 + } 82 + 80 83 static const struct init_funcs init_funcs[] = { 81 84 { 82 85 .init = xe_check_nomodeset, ··· 100 95 { 101 96 .init = xe_observation_sysctl_register, 102 97 .exit = xe_observation_sysctl_unregister, 98 + }, 99 + { 100 + .init = xe_pm_module_init, 101 + .exit = xe_dummy_exit, 103 102 }, 104 103 }; 105 104
+70 -14
drivers/gpu/drm/xe/xe_pm.c
··· 70 70 */ 71 71 72 72 #ifdef CONFIG_LOCKDEP 73 - static struct lockdep_map xe_pm_runtime_lockdep_map = { 74 - .name = "xe_pm_runtime_lockdep_map" 73 + static struct lockdep_map xe_pm_runtime_d3cold_map = { 74 + .name = "xe_rpm_d3cold_map" 75 + }; 76 + 77 + static struct lockdep_map xe_pm_runtime_nod3cold_map = { 78 + .name = "xe_rpm_nod3cold_map" 75 79 }; 76 80 #endif 81 + 82 + static bool __maybe_unused xe_rpm_reclaim_safe(const struct xe_device *xe) 83 + { 84 + return !xe->d3cold.capable && !xe->info.has_sriov; 85 + } 86 + 87 + static void xe_rpm_lockmap_acquire(const struct xe_device *xe) 88 + { 89 + lock_map_acquire(xe_rpm_reclaim_safe(xe) ? 90 + &xe_pm_runtime_nod3cold_map : 91 + &xe_pm_runtime_d3cold_map); 92 + } 93 + 94 + static void xe_rpm_lockmap_release(const struct xe_device *xe) 95 + { 96 + lock_map_release(xe_rpm_reclaim_safe(xe) ? 97 + &xe_pm_runtime_nod3cold_map : 98 + &xe_pm_runtime_d3cold_map); 99 + } 77 100 78 101 /** 79 102 * xe_pm_suspend - Helper for System suspend, i.e. S0->S3 / S0->S2idle ··· 377 354 * annotation here and in xe_pm_runtime_get() lockdep will see 378 355 * the potential lock inversion and give us a nice splat. 379 356 */ 380 - lock_map_acquire(&xe_pm_runtime_lockdep_map); 357 + xe_rpm_lockmap_acquire(xe); 381 358 382 359 /* 383 360 * Applying lock for entire list op as xe_ttm_bo_destroy and xe_bo_move_notify ··· 410 387 out: 411 388 if (err) 412 389 xe_display_pm_resume(xe, true); 413 - lock_map_release(&xe_pm_runtime_lockdep_map); 390 + xe_rpm_lockmap_release(xe); 414 391 xe_pm_write_callback_task(xe, NULL); 415 392 return err; 416 393 } ··· 431 408 /* Disable access_ongoing asserts and prevent recursive pm calls */ 432 409 xe_pm_write_callback_task(xe, current); 433 410 434 - lock_map_acquire(&xe_pm_runtime_lockdep_map); 411 + xe_rpm_lockmap_acquire(xe); 435 412 436 413 if (xe->d3cold.allowed) { 437 414 err = xe_pcode_ready(xe, true); ··· 463 440 } 464 441 465 442 out: 466 - lock_map_release(&xe_pm_runtime_lockdep_map); 443 + xe_rpm_lockmap_release(xe); 467 444 xe_pm_write_callback_task(xe, NULL); 468 445 return err; 469 446 } ··· 477 454 * stuff that can happen inside the runtime_resume callback by acquiring 478 455 * a dummy lock (it doesn't protect anything and gets compiled out on 479 456 * non-debug builds). Lockdep then only needs to see the 480 - * xe_pm_runtime_lockdep_map -> runtime_resume callback once, and then can 481 - * hopefully validate all the (callers_locks) -> xe_pm_runtime_lockdep_map. 457 + * xe_pm_runtime_xxx_map -> runtime_resume callback once, and then can 458 + * hopefully validate all the (callers_locks) -> xe_pm_runtime_xxx_map. 482 459 * For example if the (callers_locks) are ever grabbed in the 483 460 * runtime_resume callback, lockdep should give us a nice splat. 484 461 */ 485 - static void pm_runtime_lockdep_prime(void) 462 + static void xe_rpm_might_enter_cb(const struct xe_device *xe) 486 463 { 487 - lock_map_acquire(&xe_pm_runtime_lockdep_map); 488 - lock_map_release(&xe_pm_runtime_lockdep_map); 464 + xe_rpm_lockmap_acquire(xe); 465 + xe_rpm_lockmap_release(xe); 466 + } 467 + 468 + /* 469 + * Prime the lockdep maps for known locking orders that need to 470 + * be supported but that may not always occur on all systems. 471 + */ 472 + static void xe_pm_runtime_lockdep_prime(void) 473 + { 474 + struct dma_resv lockdep_resv; 475 + 476 + dma_resv_init(&lockdep_resv); 477 + lock_map_acquire(&xe_pm_runtime_d3cold_map); 478 + /* D3Cold takes the dma_resv locks to evict bos */ 479 + dma_resv_lock(&lockdep_resv, NULL); 480 + dma_resv_unlock(&lockdep_resv); 481 + lock_map_release(&xe_pm_runtime_d3cold_map); 482 + 483 + /* Shrinkers might like to wake up the device under reclaim. */ 484 + fs_reclaim_acquire(GFP_KERNEL); 485 + lock_map_acquire(&xe_pm_runtime_nod3cold_map); 486 + lock_map_release(&xe_pm_runtime_nod3cold_map); 487 + fs_reclaim_release(GFP_KERNEL); 489 488 } 490 489 491 490 /** ··· 522 477 if (xe_pm_read_callback_task(xe) == current) 523 478 return; 524 479 525 - pm_runtime_lockdep_prime(); 480 + xe_rpm_might_enter_cb(xe); 526 481 pm_runtime_resume(xe->drm.dev); 527 482 } 528 483 ··· 554 509 if (WARN_ON(xe_pm_read_callback_task(xe) == current)) 555 510 return -ELOOP; 556 511 557 - pm_runtime_lockdep_prime(); 512 + xe_rpm_might_enter_cb(xe); 558 513 return pm_runtime_get_sync(xe->drm.dev); 559 514 } 560 515 ··· 622 577 return true; 623 578 } 624 579 625 - pm_runtime_lockdep_prime(); 580 + xe_rpm_might_enter_cb(xe); 626 581 return pm_runtime_resume_and_get(xe->drm.dev) >= 0; 627 582 } 628 583 ··· 713 668 714 669 drm_dbg(&xe->drm, 715 670 "d3cold: allowed=%s\n", str_yes_no(xe->d3cold.allowed)); 671 + } 672 + 673 + /** 674 + * xe_pm_module_init() - Perform xe_pm specific module initialization. 675 + * 676 + * Return: 0 on success. Currently doesn't fail. 677 + */ 678 + int __init xe_pm_module_init(void) 679 + { 680 + xe_pm_runtime_lockdep_prime(); 681 + return 0; 716 682 }
+1
drivers/gpu/drm/xe/xe_pm.h
··· 32 32 int xe_pm_set_vram_threshold(struct xe_device *xe, u32 threshold); 33 33 void xe_pm_d3cold_allowed_toggle(struct xe_device *xe); 34 34 struct task_struct *xe_pm_read_callback_task(struct xe_device *xe); 35 + int xe_pm_module_init(void); 35 36 36 37 #endif