sched/fair: add boosted task utilization
[firefly-linux-kernel-4.4.55.git] / kernel / sched / tune.c
1 #include <linux/cgroup.h>
2 #include <linux/err.h>
3 #include <linux/percpu.h>
4 #include <linux/printk.h>
5 #include <linux/rcupdate.h>
6 #include <linux/slab.h>
7
8 #include "sched.h"
9
10 unsigned int sysctl_sched_cfs_boost __read_mostly;
11
12 #ifdef CONFIG_CGROUP_SCHEDTUNE
13
14 /*
15  * EAS scheduler tunables for task groups.
16  */
17
18 /* SchdTune tunables for a group of tasks */
19 struct schedtune {
20         /* SchedTune CGroup subsystem */
21         struct cgroup_subsys_state css;
22
23         /* Boost group allocated ID */
24         int idx;
25
26         /* Boost value for tasks on that SchedTune CGroup */
27         int boost;
28
29 };
30
31 static inline struct schedtune *css_st(struct cgroup_subsys_state *css)
32 {
33         return css ? container_of(css, struct schedtune, css) : NULL;
34 }
35
36 static inline struct schedtune *task_schedtune(struct task_struct *tsk)
37 {
38         return css_st(task_css(tsk, schedtune_cgrp_id));
39 }
40
41 static inline struct schedtune *parent_st(struct schedtune *st)
42 {
43         return css_st(st->css.parent);
44 }
45
46 /*
47  * SchedTune root control group
48  * The root control group is used to defined a system-wide boosting tuning,
49  * which is applied to all tasks in the system.
50  * Task specific boost tuning could be specified by creating and
51  * configuring a child control group under the root one.
52  * By default, system-wide boosting is disabled, i.e. no boosting is applied
53  * to tasks which are not into a child control group.
54  */
55 static struct schedtune
56 root_schedtune = {
57         .boost  = 0,
58 };
59
60 /*
61  * Maximum number of boost groups to support
62  * When per-task boosting is used we still allow only limited number of
63  * boost groups for two main reasons:
64  * 1. on a real system we usually have only few classes of workloads which
65  *    make sense to boost with different values (e.g. background vs foreground
66  *    tasks, interactive vs low-priority tasks)
67  * 2. a limited number allows for a simpler and more memory/time efficient
68  *    implementation especially for the computation of the per-CPU boost
69  *    value
70  */
71 #define BOOSTGROUPS_COUNT 4
72
73 /* Array of configured boostgroups */
74 static struct schedtune *allocated_group[BOOSTGROUPS_COUNT] = {
75         &root_schedtune,
76         NULL,
77 };
78
79 /* SchedTune boost groups
80  * Keep track of all the boost groups which impact on CPU, for example when a
81  * CPU has two RUNNABLE tasks belonging to two different boost groups and thus
82  * likely with different boost values.
83  * Since on each system we expect only a limited number of boost groups, here
84  * we use a simple array to keep track of the metrics required to compute the
85  * maximum per-CPU boosting value.
86  */
87 struct boost_groups {
88         /* Maximum boost value for all RUNNABLE tasks on a CPU */
89         unsigned boost_max;
90         struct {
91                 /* The boost for tasks on that boost group */
92                 unsigned boost;
93                 /* Count of RUNNABLE tasks on that boost group */
94                 unsigned tasks;
95         } group[BOOSTGROUPS_COUNT];
96 };
97
98 /* Boost groups affecting each CPU in the system */
99 DEFINE_PER_CPU(struct boost_groups, cpu_boost_groups);
100
101 static void
102 schedtune_cpu_update(int cpu)
103 {
104         struct boost_groups *bg;
105         unsigned boost_max;
106         int idx;
107
108         bg = &per_cpu(cpu_boost_groups, cpu);
109
110         /* The root boost group is always active */
111         boost_max = bg->group[0].boost;
112         for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx) {
113                 /*
114                  * A boost group affects a CPU only if it has
115                  * RUNNABLE tasks on that CPU
116                  */
117                 if (bg->group[idx].tasks == 0)
118                         continue;
119                 boost_max = max(boost_max, bg->group[idx].boost);
120         }
121
122         bg->boost_max = boost_max;
123 }
124
125 static int
126 schedtune_boostgroup_update(int idx, int boost)
127 {
128         struct boost_groups *bg;
129         int cur_boost_max;
130         int old_boost;
131         int cpu;
132
133         /* Update per CPU boost groups */
134         for_each_possible_cpu(cpu) {
135                 bg = &per_cpu(cpu_boost_groups, cpu);
136
137                 /*
138                  * Keep track of current boost values to compute the per CPU
139                  * maximum only when it has been affected by the new value of
140                  * the updated boost group
141                  */
142                 cur_boost_max = bg->boost_max;
143                 old_boost = bg->group[idx].boost;
144
145                 /* Update the boost value of this boost group */
146                 bg->group[idx].boost = boost;
147
148                 /* Check if this update increase current max */
149                 if (boost > cur_boost_max && bg->group[idx].tasks) {
150                         bg->boost_max = boost;
151                         continue;
152                 }
153
154                 /* Check if this update has decreased current max */
155                 if (cur_boost_max == old_boost && old_boost > boost)
156                         schedtune_cpu_update(cpu);
157         }
158
159         return 0;
160 }
161
162 static inline void
163 schedtune_tasks_update(struct task_struct *p, int cpu, int idx, int task_count)
164 {
165         struct boost_groups *bg;
166         int tasks;
167
168         bg = &per_cpu(cpu_boost_groups, cpu);
169
170         /* Update boosted tasks count while avoiding to make it negative */
171         if (task_count < 0 && bg->group[idx].tasks <= -task_count)
172                 bg->group[idx].tasks = 0;
173         else
174                 bg->group[idx].tasks += task_count;
175
176         /* Boost group activation or deactivation on that RQ */
177         tasks = bg->group[idx].tasks;
178         if (tasks == 1 || tasks == 0)
179                 schedtune_cpu_update(cpu);
180 }
181
182 /*
183  * NOTE: This function must be called while holding the lock on the CPU RQ
184  */
185 void schedtune_enqueue_task(struct task_struct *p, int cpu)
186 {
187         struct schedtune *st;
188         int idx;
189
190         /*
191          * When a task is marked PF_EXITING by do_exit() it's going to be
192          * dequeued and enqueued multiple times in the exit path.
193          * Thus we avoid any further update, since we do not want to change
194          * CPU boosting while the task is exiting.
195          */
196         if (p->flags & PF_EXITING)
197                 return;
198
199         /* Get task boost group */
200         rcu_read_lock();
201         st = task_schedtune(p);
202         idx = st->idx;
203         rcu_read_unlock();
204
205         schedtune_tasks_update(p, cpu, idx, 1);
206 }
207
208 /*
209  * NOTE: This function must be called while holding the lock on the CPU RQ
210  */
211 void schedtune_dequeue_task(struct task_struct *p, int cpu)
212 {
213         struct schedtune *st;
214         int idx;
215
216         /*
217          * When a task is marked PF_EXITING by do_exit() it's going to be
218          * dequeued and enqueued multiple times in the exit path.
219          * Thus we avoid any further update, since we do not want to change
220          * CPU boosting while the task is exiting.
221          * The last dequeue will be done by cgroup exit() callback.
222          */
223         if (p->flags & PF_EXITING)
224                 return;
225
226         /* Get task boost group */
227         rcu_read_lock();
228         st = task_schedtune(p);
229         idx = st->idx;
230         rcu_read_unlock();
231
232         schedtune_tasks_update(p, cpu, idx, -1);
233 }
234
235 int schedtune_cpu_boost(int cpu)
236 {
237         struct boost_groups *bg;
238
239         bg = &per_cpu(cpu_boost_groups, cpu);
240         return bg->boost_max;
241 }
242
243 int schedtune_task_boost(struct task_struct *p)
244 {
245         struct schedtune *st;
246         int task_boost;
247
248         /* Get task boost value */
249         rcu_read_lock();
250         st = task_schedtune(p);
251         task_boost = st->boost;
252         rcu_read_unlock();
253
254         return task_boost;
255 }
256
257 static u64
258 boost_read(struct cgroup_subsys_state *css, struct cftype *cft)
259 {
260         struct schedtune *st = css_st(css);
261
262         return st->boost;
263 }
264
265 static int
266 boost_write(struct cgroup_subsys_state *css, struct cftype *cft,
267             u64 boost)
268 {
269         struct schedtune *st = css_st(css);
270
271         if (boost < 0 || boost > 100)
272                 return -EINVAL;
273
274         st->boost = boost;
275         if (css == &root_schedtune.css)
276                 sysctl_sched_cfs_boost = boost;
277
278         /* Update CPU boost */
279         schedtune_boostgroup_update(st->idx, st->boost);
280
281         return 0;
282 }
283
284 static struct cftype files[] = {
285         {
286                 .name = "boost",
287                 .read_u64 = boost_read,
288                 .write_u64 = boost_write,
289         },
290         { }     /* terminate */
291 };
292
293 static int
294 schedtune_boostgroup_init(struct schedtune *st)
295 {
296         struct boost_groups *bg;
297         int cpu;
298
299         /* Keep track of allocated boost groups */
300         allocated_group[st->idx] = st;
301
302         /* Initialize the per CPU boost groups */
303         for_each_possible_cpu(cpu) {
304                 bg = &per_cpu(cpu_boost_groups, cpu);
305                 bg->group[st->idx].boost = 0;
306                 bg->group[st->idx].tasks = 0;
307         }
308
309         return 0;
310 }
311
312 static int
313 schedtune_init(void)
314 {
315         struct boost_groups *bg;
316         int cpu;
317
318         /* Initialize the per CPU boost groups */
319         for_each_possible_cpu(cpu) {
320                 bg = &per_cpu(cpu_boost_groups, cpu);
321                 memset(bg, 0, sizeof(struct boost_groups));
322         }
323
324         pr_info("  schedtune configured to support %d boost groups\n",
325                 BOOSTGROUPS_COUNT);
326         return 0;
327 }
328
329 static struct cgroup_subsys_state *
330 schedtune_css_alloc(struct cgroup_subsys_state *parent_css)
331 {
332         struct schedtune *st;
333         int idx;
334
335         if (!parent_css) {
336                 schedtune_init();
337                 return &root_schedtune.css;
338         }
339
340         /* Allow only single level hierachies */
341         if (parent_css != &root_schedtune.css) {
342                 pr_err("Nested SchedTune boosting groups not allowed\n");
343                 return ERR_PTR(-ENOMEM);
344         }
345
346         /* Allow only a limited number of boosting groups */
347         for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx)
348                 if (!allocated_group[idx])
349                         break;
350         if (idx == BOOSTGROUPS_COUNT) {
351                 pr_err("Trying to create more than %d SchedTune boosting groups\n",
352                        BOOSTGROUPS_COUNT);
353                 return ERR_PTR(-ENOSPC);
354         }
355
356         st = kzalloc(sizeof(*st), GFP_KERNEL);
357         if (!st)
358                 goto out;
359
360         /* Initialize per CPUs boost group support */
361         st->idx = idx;
362         if (schedtune_boostgroup_init(st))
363                 goto release;
364
365         return &st->css;
366
367 release:
368         kfree(st);
369 out:
370         return ERR_PTR(-ENOMEM);
371 }
372
373 static void
374 schedtune_boostgroup_release(struct schedtune *st)
375 {
376         /* Reset this boost group */
377         schedtune_boostgroup_update(st->idx, 0);
378
379         /* Keep track of allocated boost groups */
380         allocated_group[st->idx] = NULL;
381 }
382
383 static void
384 schedtune_css_free(struct cgroup_subsys_state *css)
385 {
386         struct schedtune *st = css_st(css);
387
388         schedtune_boostgroup_release(st);
389         kfree(st);
390 }
391
392 struct cgroup_subsys schedtune_cgrp_subsys = {
393         .css_alloc      = schedtune_css_alloc,
394         .css_free       = schedtune_css_free,
395         .legacy_cftypes = files,
396         .early_init     = 1,
397 };
398
399 #endif /* CONFIG_CGROUP_SCHEDTUNE */
400
401 int
402 sysctl_sched_cfs_boost_handler(struct ctl_table *table, int write,
403                                void __user *buffer, size_t *lenp,
404                                loff_t *ppos)
405 {
406         int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
407
408         if (ret || !write)
409                 return ret;
410
411         return 0;
412 }