18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * SPDX-License-Identifier: MIT 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright © 2019 Intel Corporation 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "i915_active.h" 118c2ecf20Sopenharmony_ci#include "gem/i915_gem_context.h" 128c2ecf20Sopenharmony_ci#include "gem/i915_gem_object.h" 138c2ecf20Sopenharmony_ci#include "i915_globals.h" 148c2ecf20Sopenharmony_ci#include "i915_request.h" 158c2ecf20Sopenharmony_ci#include "i915_scheduler.h" 168c2ecf20Sopenharmony_ci#include "i915_vma.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic LIST_HEAD(globals); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic atomic_t active; 218c2ecf20Sopenharmony_cistatic atomic_t epoch; 228c2ecf20Sopenharmony_cistatic struct park_work { 238c2ecf20Sopenharmony_ci struct delayed_work work; 248c2ecf20Sopenharmony_ci struct rcu_head rcu; 258c2ecf20Sopenharmony_ci unsigned long flags; 268c2ecf20Sopenharmony_ci#define PENDING 0 278c2ecf20Sopenharmony_ci int epoch; 288c2ecf20Sopenharmony_ci} park; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic void i915_globals_shrink(void) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci struct i915_global *global; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci /* 358c2ecf20Sopenharmony_ci * kmem_cache_shrink() discards empty slabs and reorders partially 368c2ecf20Sopenharmony_ci * filled slabs to prioritise allocating from the mostly full slabs, 378c2ecf20Sopenharmony_ci * with the aim of reducing fragmentation. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci list_for_each_entry(global, &globals, link) 408c2ecf20Sopenharmony_ci global->shrink(); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void __i915_globals_grace(struct rcu_head *rcu) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci /* Ratelimit parking as shrinking is quite slow */ 468c2ecf20Sopenharmony_ci schedule_delayed_work(&park.work, round_jiffies_up_relative(2 * HZ)); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void __i915_globals_queue_rcu(void) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci park.epoch = atomic_inc_return(&epoch); 528c2ecf20Sopenharmony_ci if (!atomic_read(&active)) { 538c2ecf20Sopenharmony_ci init_rcu_head(&park.rcu); 548c2ecf20Sopenharmony_ci call_rcu(&park.rcu, __i915_globals_grace); 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic void __i915_globals_park(struct work_struct *work) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci destroy_rcu_head(&park.rcu); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* Confirm nothing woke up in the last grace period */ 638c2ecf20Sopenharmony_ci if (park.epoch != atomic_read(&epoch)) { 648c2ecf20Sopenharmony_ci __i915_globals_queue_rcu(); 658c2ecf20Sopenharmony_ci return; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci clear_bit(PENDING, &park.flags); 698c2ecf20Sopenharmony_ci i915_globals_shrink(); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_civoid __init i915_global_register(struct i915_global *global) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci GEM_BUG_ON(!global->shrink); 758c2ecf20Sopenharmony_ci GEM_BUG_ON(!global->exit); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci list_add_tail(&global->link, &globals); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void __i915_globals_cleanup(void) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct i915_global *global, *next; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci list_for_each_entry_safe_reverse(global, next, &globals, link) 858c2ecf20Sopenharmony_ci global->exit(); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic __initconst int (* const initfn[])(void) = { 898c2ecf20Sopenharmony_ci i915_global_active_init, 908c2ecf20Sopenharmony_ci i915_global_buddy_init, 918c2ecf20Sopenharmony_ci i915_global_context_init, 928c2ecf20Sopenharmony_ci i915_global_gem_context_init, 938c2ecf20Sopenharmony_ci i915_global_objects_init, 948c2ecf20Sopenharmony_ci i915_global_request_init, 958c2ecf20Sopenharmony_ci i915_global_scheduler_init, 968c2ecf20Sopenharmony_ci i915_global_vma_init, 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ciint __init i915_globals_init(void) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci int i; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(initfn); i++) { 1048c2ecf20Sopenharmony_ci int err; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci err = initfn[i](); 1078c2ecf20Sopenharmony_ci if (err) { 1088c2ecf20Sopenharmony_ci __i915_globals_cleanup(); 1098c2ecf20Sopenharmony_ci return err; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&park.work, __i915_globals_park); 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_civoid i915_globals_park(void) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci /* 1208c2ecf20Sopenharmony_ci * Defer shrinking the global slab caches (and other work) until 1218c2ecf20Sopenharmony_ci * after a RCU grace period has completed with no activity. This 1228c2ecf20Sopenharmony_ci * is to try and reduce the latency impact on the consumers caused 1238c2ecf20Sopenharmony_ci * by us shrinking the caches the same time as they are trying to 1248c2ecf20Sopenharmony_ci * allocate, with the assumption being that if we idle long enough 1258c2ecf20Sopenharmony_ci * for an RCU grace period to elapse since the last use, it is likely 1268c2ecf20Sopenharmony_ci * to be longer until we need the caches again. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci if (!atomic_dec_and_test(&active)) 1298c2ecf20Sopenharmony_ci return; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Queue cleanup after the next RCU grace period has freed slabs */ 1328c2ecf20Sopenharmony_ci if (!test_and_set_bit(PENDING, &park.flags)) 1338c2ecf20Sopenharmony_ci __i915_globals_queue_rcu(); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_civoid i915_globals_unpark(void) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci atomic_inc(&epoch); 1398c2ecf20Sopenharmony_ci atomic_inc(&active); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void __exit __i915_globals_flush(void) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci atomic_inc(&active); /* skip shrinking */ 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci rcu_barrier(); /* wait for the work to be queued */ 1478c2ecf20Sopenharmony_ci flush_delayed_work(&park.work); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci atomic_dec(&active); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_civoid __exit i915_globals_exit(void) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci GEM_BUG_ON(atomic_read(&active)); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci __i915_globals_flush(); 1578c2ecf20Sopenharmony_ci __i915_globals_cleanup(); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* And ensure that our DESTROY_BY_RCU slabs are truly destroyed */ 1608c2ecf20Sopenharmony_ci rcu_barrier(); 1618c2ecf20Sopenharmony_ci} 162