1*9e5fbd4fSriastradh /* $NetBSD: i915_globals.c,v 1.3 2021/12/19 11:49:11 riastradh Exp $ */
24e390cabSriastradh
34e390cabSriastradh /*
44e390cabSriastradh * SPDX-License-Identifier: MIT
54e390cabSriastradh *
64e390cabSriastradh * Copyright © 2019 Intel Corporation
74e390cabSriastradh */
84e390cabSriastradh
94e390cabSriastradh #include <sys/cdefs.h>
10*9e5fbd4fSriastradh __KERNEL_RCSID(0, "$NetBSD: i915_globals.c,v 1.3 2021/12/19 11:49:11 riastradh Exp $");
114e390cabSriastradh
124e390cabSriastradh #include <linux/slab.h>
134e390cabSriastradh #include <linux/workqueue.h>
144e390cabSriastradh
154e390cabSriastradh #include "i915_active.h"
164e390cabSriastradh #include "gem/i915_gem_context.h"
174e390cabSriastradh #include "gem/i915_gem_object.h"
184e390cabSriastradh #include "i915_globals.h"
194e390cabSriastradh #include "i915_request.h"
204e390cabSriastradh #include "i915_scheduler.h"
214e390cabSriastradh #include "i915_vma.h"
224e390cabSriastradh
23*9e5fbd4fSriastradh #include <linux/nbsd-namespace.h>
24*9e5fbd4fSriastradh
254e390cabSriastradh static LIST_HEAD(globals);
264e390cabSriastradh
274e390cabSriastradh static atomic_t active;
284e390cabSriastradh static atomic_t epoch;
294e390cabSriastradh static struct park_work {
304e390cabSriastradh struct delayed_work work;
314e390cabSriastradh struct rcu_head rcu;
324e390cabSriastradh unsigned long flags;
334e390cabSriastradh #define PENDING 0
344e390cabSriastradh int epoch;
354e390cabSriastradh } park;
364e390cabSriastradh
i915_globals_shrink(void)374e390cabSriastradh static void i915_globals_shrink(void)
384e390cabSriastradh {
394e390cabSriastradh struct i915_global *global;
404e390cabSriastradh
414e390cabSriastradh /*
424e390cabSriastradh * kmem_cache_shrink() discards empty slabs and reorders partially
434e390cabSriastradh * filled slabs to prioritise allocating from the mostly full slabs,
444e390cabSriastradh * with the aim of reducing fragmentation.
454e390cabSriastradh */
464e390cabSriastradh list_for_each_entry(global, &globals, link)
474e390cabSriastradh global->shrink();
484e390cabSriastradh }
494e390cabSriastradh
__i915_globals_grace(struct rcu_head * rcu)504e390cabSriastradh static void __i915_globals_grace(struct rcu_head *rcu)
514e390cabSriastradh {
524e390cabSriastradh /* Ratelimit parking as shrinking is quite slow */
534e390cabSriastradh schedule_delayed_work(&park.work, round_jiffies_up_relative(2 * HZ));
544e390cabSriastradh }
554e390cabSriastradh
__i915_globals_queue_rcu(void)564e390cabSriastradh static void __i915_globals_queue_rcu(void)
574e390cabSriastradh {
584e390cabSriastradh park.epoch = atomic_inc_return(&epoch);
594e390cabSriastradh if (!atomic_read(&active)) {
604e390cabSriastradh init_rcu_head(&park.rcu);
614e390cabSriastradh call_rcu(&park.rcu, __i915_globals_grace);
624e390cabSriastradh }
634e390cabSriastradh }
644e390cabSriastradh
__i915_globals_park(struct work_struct * work)654e390cabSriastradh static void __i915_globals_park(struct work_struct *work)
664e390cabSriastradh {
674e390cabSriastradh destroy_rcu_head(&park.rcu);
684e390cabSriastradh
694e390cabSriastradh /* Confirm nothing woke up in the last grace period */
704e390cabSriastradh if (park.epoch != atomic_read(&epoch)) {
714e390cabSriastradh __i915_globals_queue_rcu();
724e390cabSriastradh return;
734e390cabSriastradh }
744e390cabSriastradh
754e390cabSriastradh clear_bit(PENDING, &park.flags);
764e390cabSriastradh i915_globals_shrink();
774e390cabSriastradh }
784e390cabSriastradh
i915_global_register(struct i915_global * global)794e390cabSriastradh void __init i915_global_register(struct i915_global *global)
804e390cabSriastradh {
814e390cabSriastradh GEM_BUG_ON(!global->shrink);
824e390cabSriastradh GEM_BUG_ON(!global->exit);
834e390cabSriastradh
844e390cabSriastradh list_add_tail(&global->link, &globals);
854e390cabSriastradh }
864e390cabSriastradh
__i915_globals_cleanup(void)874e390cabSriastradh static void __i915_globals_cleanup(void)
884e390cabSriastradh {
894e390cabSriastradh struct i915_global *global, *next;
904e390cabSriastradh
914e390cabSriastradh list_for_each_entry_safe_reverse(global, next, &globals, link)
924e390cabSriastradh global->exit();
934e390cabSriastradh }
944e390cabSriastradh
954e390cabSriastradh static __initconst int (* const initfn[])(void) = {
964e390cabSriastradh i915_global_active_init,
974e390cabSriastradh i915_global_buddy_init,
984e390cabSriastradh i915_global_context_init,
994e390cabSriastradh i915_global_gem_context_init,
1004e390cabSriastradh i915_global_objects_init,
1014e390cabSriastradh i915_global_request_init,
1024e390cabSriastradh i915_global_scheduler_init,
1034e390cabSriastradh i915_global_vma_init,
1044e390cabSriastradh };
1054e390cabSriastradh
i915_globals_init(void)1064e390cabSriastradh int __init i915_globals_init(void)
1074e390cabSriastradh {
1084e390cabSriastradh int i;
1094e390cabSriastradh
1104e390cabSriastradh for (i = 0; i < ARRAY_SIZE(initfn); i++) {
1114e390cabSriastradh int err;
1124e390cabSriastradh
1134e390cabSriastradh err = initfn[i]();
1144e390cabSriastradh if (err) {
1154e390cabSriastradh __i915_globals_cleanup();
1164e390cabSriastradh return err;
1174e390cabSriastradh }
1184e390cabSriastradh }
1194e390cabSriastradh
1204e390cabSriastradh INIT_DELAYED_WORK(&park.work, __i915_globals_park);
1214e390cabSriastradh return 0;
1224e390cabSriastradh }
1234e390cabSriastradh
i915_globals_park(void)1244e390cabSriastradh void i915_globals_park(void)
1254e390cabSriastradh {
1264e390cabSriastradh /*
1274e390cabSriastradh * Defer shrinking the global slab caches (and other work) until
1284e390cabSriastradh * after a RCU grace period has completed with no activity. This
1294e390cabSriastradh * is to try and reduce the latency impact on the consumers caused
1304e390cabSriastradh * by us shrinking the caches the same time as they are trying to
1314e390cabSriastradh * allocate, with the assumption being that if we idle long enough
1324e390cabSriastradh * for an RCU grace period to elapse since the last use, it is likely
1334e390cabSriastradh * to be longer until we need the caches again.
1344e390cabSriastradh */
1354e390cabSriastradh if (!atomic_dec_and_test(&active))
1364e390cabSriastradh return;
1374e390cabSriastradh
1384e390cabSriastradh /* Queue cleanup after the next RCU grace period has freed slabs */
1394e390cabSriastradh if (!test_and_set_bit(PENDING, &park.flags))
1404e390cabSriastradh __i915_globals_queue_rcu();
1414e390cabSriastradh }
1424e390cabSriastradh
i915_globals_unpark(void)1434e390cabSriastradh void i915_globals_unpark(void)
1444e390cabSriastradh {
1454e390cabSriastradh atomic_inc(&epoch);
1464e390cabSriastradh atomic_inc(&active);
1474e390cabSriastradh }
1484e390cabSriastradh
__i915_globals_flush(void)1494e390cabSriastradh static void __exit __i915_globals_flush(void)
1504e390cabSriastradh {
1514e390cabSriastradh atomic_inc(&active); /* skip shrinking */
1524e390cabSriastradh
1534e390cabSriastradh rcu_barrier(); /* wait for the work to be queued */
1544e390cabSriastradh flush_delayed_work(&park.work);
1554e390cabSriastradh
1564e390cabSriastradh atomic_dec(&active);
1574e390cabSriastradh }
1584e390cabSriastradh
i915_globals_exit(void)1594e390cabSriastradh void __exit i915_globals_exit(void)
1604e390cabSriastradh {
1614e390cabSriastradh GEM_BUG_ON(atomic_read(&active));
1624e390cabSriastradh
1634e390cabSriastradh __i915_globals_flush();
1644e390cabSriastradh __i915_globals_cleanup();
1654e390cabSriastradh
1664e390cabSriastradh /* And ensure that our DESTROY_BY_RCU slabs are truly destroyed */
1674e390cabSriastradh rcu_barrier();
1684e390cabSriastradh }
169