1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * SPDX-License-Identifier: MIT
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright © 2019 Intel Corporation
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <linux/slab.h>
8*4882a593Smuzhiyun #include <linux/workqueue.h>
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include "i915_active.h"
11*4882a593Smuzhiyun #include "gem/i915_gem_context.h"
12*4882a593Smuzhiyun #include "gem/i915_gem_object.h"
13*4882a593Smuzhiyun #include "i915_globals.h"
14*4882a593Smuzhiyun #include "i915_request.h"
15*4882a593Smuzhiyun #include "i915_scheduler.h"
16*4882a593Smuzhiyun #include "i915_vma.h"
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun static LIST_HEAD(globals);
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun static atomic_t active;
21*4882a593Smuzhiyun static atomic_t epoch;
22*4882a593Smuzhiyun static struct park_work {
23*4882a593Smuzhiyun struct delayed_work work;
24*4882a593Smuzhiyun struct rcu_head rcu;
25*4882a593Smuzhiyun unsigned long flags;
26*4882a593Smuzhiyun #define PENDING 0
27*4882a593Smuzhiyun int epoch;
28*4882a593Smuzhiyun } park;
29*4882a593Smuzhiyun
i915_globals_shrink(void)30*4882a593Smuzhiyun static void i915_globals_shrink(void)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun struct i915_global *global;
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun /*
35*4882a593Smuzhiyun * kmem_cache_shrink() discards empty slabs and reorders partially
36*4882a593Smuzhiyun * filled slabs to prioritise allocating from the mostly full slabs,
37*4882a593Smuzhiyun * with the aim of reducing fragmentation.
38*4882a593Smuzhiyun */
39*4882a593Smuzhiyun list_for_each_entry(global, &globals, link)
40*4882a593Smuzhiyun global->shrink();
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun
__i915_globals_grace(struct rcu_head * rcu)43*4882a593Smuzhiyun static void __i915_globals_grace(struct rcu_head *rcu)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun /* Ratelimit parking as shrinking is quite slow */
46*4882a593Smuzhiyun schedule_delayed_work(&park.work, round_jiffies_up_relative(2 * HZ));
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
__i915_globals_queue_rcu(void)49*4882a593Smuzhiyun static void __i915_globals_queue_rcu(void)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun park.epoch = atomic_inc_return(&epoch);
52*4882a593Smuzhiyun if (!atomic_read(&active)) {
53*4882a593Smuzhiyun init_rcu_head(&park.rcu);
54*4882a593Smuzhiyun call_rcu(&park.rcu, __i915_globals_grace);
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun
__i915_globals_park(struct work_struct * work)58*4882a593Smuzhiyun static void __i915_globals_park(struct work_struct *work)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun destroy_rcu_head(&park.rcu);
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun /* Confirm nothing woke up in the last grace period */
63*4882a593Smuzhiyun if (park.epoch != atomic_read(&epoch)) {
64*4882a593Smuzhiyun __i915_globals_queue_rcu();
65*4882a593Smuzhiyun return;
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun clear_bit(PENDING, &park.flags);
69*4882a593Smuzhiyun i915_globals_shrink();
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun
i915_global_register(struct i915_global * global)72*4882a593Smuzhiyun void __init i915_global_register(struct i915_global *global)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun GEM_BUG_ON(!global->shrink);
75*4882a593Smuzhiyun GEM_BUG_ON(!global->exit);
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun list_add_tail(&global->link, &globals);
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun
__i915_globals_cleanup(void)80*4882a593Smuzhiyun static void __i915_globals_cleanup(void)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun struct i915_global *global, *next;
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun list_for_each_entry_safe_reverse(global, next, &globals, link)
85*4882a593Smuzhiyun global->exit();
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun static __initconst int (* const initfn[])(void) = {
89*4882a593Smuzhiyun i915_global_active_init,
90*4882a593Smuzhiyun i915_global_buddy_init,
91*4882a593Smuzhiyun i915_global_context_init,
92*4882a593Smuzhiyun i915_global_gem_context_init,
93*4882a593Smuzhiyun i915_global_objects_init,
94*4882a593Smuzhiyun i915_global_request_init,
95*4882a593Smuzhiyun i915_global_scheduler_init,
96*4882a593Smuzhiyun i915_global_vma_init,
97*4882a593Smuzhiyun };
98*4882a593Smuzhiyun
i915_globals_init(void)99*4882a593Smuzhiyun int __init i915_globals_init(void)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun int i;
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(initfn); i++) {
104*4882a593Smuzhiyun int err;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun err = initfn[i]();
107*4882a593Smuzhiyun if (err) {
108*4882a593Smuzhiyun __i915_globals_cleanup();
109*4882a593Smuzhiyun return err;
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun INIT_DELAYED_WORK(&park.work, __i915_globals_park);
114*4882a593Smuzhiyun return 0;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
i915_globals_park(void)117*4882a593Smuzhiyun void i915_globals_park(void)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun /*
120*4882a593Smuzhiyun * Defer shrinking the global slab caches (and other work) until
121*4882a593Smuzhiyun * after a RCU grace period has completed with no activity. This
122*4882a593Smuzhiyun * is to try and reduce the latency impact on the consumers caused
123*4882a593Smuzhiyun * by us shrinking the caches the same time as they are trying to
124*4882a593Smuzhiyun * allocate, with the assumption being that if we idle long enough
125*4882a593Smuzhiyun * for an RCU grace period to elapse since the last use, it is likely
126*4882a593Smuzhiyun * to be longer until we need the caches again.
127*4882a593Smuzhiyun */
128*4882a593Smuzhiyun if (!atomic_dec_and_test(&active))
129*4882a593Smuzhiyun return;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun /* Queue cleanup after the next RCU grace period has freed slabs */
132*4882a593Smuzhiyun if (!test_and_set_bit(PENDING, &park.flags))
133*4882a593Smuzhiyun __i915_globals_queue_rcu();
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
i915_globals_unpark(void)136*4882a593Smuzhiyun void i915_globals_unpark(void)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun atomic_inc(&epoch);
139*4882a593Smuzhiyun atomic_inc(&active);
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
__i915_globals_flush(void)142*4882a593Smuzhiyun static void __exit __i915_globals_flush(void)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun atomic_inc(&active); /* skip shrinking */
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun rcu_barrier(); /* wait for the work to be queued */
147*4882a593Smuzhiyun flush_delayed_work(&park.work);
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun atomic_dec(&active);
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
i915_globals_exit(void)152*4882a593Smuzhiyun void __exit i915_globals_exit(void)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun GEM_BUG_ON(atomic_read(&active));
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun __i915_globals_flush();
157*4882a593Smuzhiyun __i915_globals_cleanup();
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun /* And ensure that our DESTROY_BY_RCU slabs are truly destroyed */
160*4882a593Smuzhiyun rcu_barrier();
161*4882a593Smuzhiyun }
162