blob: f623868937253c9454074708f666e140d09f5733 [file] [log] [blame]
Patrick Bellasiae710302015-06-23 09:17:54 +01001#include <linux/cgroup.h>
2#include <linux/err.h>
3#include <linux/percpu.h>
4#include <linux/printk.h>
5#include <linux/slab.h>
6
Patrick Bellasi69fa4c72015-06-22 18:11:44 +01007#include "sched.h"
8
9unsigned int sysctl_sched_cfs_boost __read_mostly;
10
Patrick Bellasiae710302015-06-23 09:17:54 +010011#ifdef CONFIG_CGROUP_SCHEDTUNE
12
13/*
14 * EAS scheduler tunables for task groups.
15 */
16
17/* SchdTune tunables for a group of tasks */
18struct schedtune {
19 /* SchedTune CGroup subsystem */
20 struct cgroup_subsys_state css;
21
22 /* Boost group allocated ID */
23 int idx;
24
25 /* Boost value for tasks on that SchedTune CGroup */
26 int boost;
27
28};
29
30static inline struct schedtune *css_st(struct cgroup_subsys_state *css)
31{
32 return css ? container_of(css, struct schedtune, css) : NULL;
33}
34
35static inline struct schedtune *task_schedtune(struct task_struct *tsk)
36{
37 return css_st(task_css(tsk, schedtune_cgrp_id));
38}
39
40static inline struct schedtune *parent_st(struct schedtune *st)
41{
42 return css_st(st->css.parent);
43}
44
45/*
46 * SchedTune root control group
47 * The root control group is used to defined a system-wide boosting tuning,
48 * which is applied to all tasks in the system.
49 * Task specific boost tuning could be specified by creating and
50 * configuring a child control group under the root one.
51 * By default, system-wide boosting is disabled, i.e. no boosting is applied
52 * to tasks which are not into a child control group.
53 */
54static struct schedtune
55root_schedtune = {
56 .boost = 0,
57};
58
59/*
60 * Maximum number of boost groups to support
61 * When per-task boosting is used we still allow only limited number of
62 * boost groups for two main reasons:
63 * 1. on a real system we usually have only few classes of workloads which
64 * make sense to boost with different values (e.g. background vs foreground
65 * tasks, interactive vs low-priority tasks)
66 * 2. a limited number allows for a simpler and more memory/time efficient
67 * implementation especially for the computation of the per-CPU boost
68 * value
69 */
70#define BOOSTGROUPS_COUNT 4
71
72/* Array of configured boostgroups */
73static struct schedtune *allocated_group[BOOSTGROUPS_COUNT] = {
74 &root_schedtune,
75 NULL,
76};
77
78/* SchedTune boost groups
79 * Keep track of all the boost groups which impact on CPU, for example when a
80 * CPU has two RUNNABLE tasks belonging to two different boost groups and thus
81 * likely with different boost values.
82 * Since on each system we expect only a limited number of boost groups, here
83 * we use a simple array to keep track of the metrics required to compute the
84 * maximum per-CPU boosting value.
85 */
86struct boost_groups {
87 /* Maximum boost value for all RUNNABLE tasks on a CPU */
88 unsigned boost_max;
89 struct {
90 /* The boost for tasks on that boost group */
91 unsigned boost;
92 /* Count of RUNNABLE tasks on that boost group */
93 unsigned tasks;
94 } group[BOOSTGROUPS_COUNT];
95};
96
97/* Boost groups affecting each CPU in the system */
98DEFINE_PER_CPU(struct boost_groups, cpu_boost_groups);
99
Patrick Bellasi9a871ed2016-01-14 12:31:35 +0000100static void
101schedtune_cpu_update(int cpu)
102{
103 struct boost_groups *bg;
104 unsigned boost_max;
105 int idx;
106
107 bg = &per_cpu(cpu_boost_groups, cpu);
108
109 /* The root boost group is always active */
110 boost_max = bg->group[0].boost;
111 for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx) {
112 /*
113 * A boost group affects a CPU only if it has
114 * RUNNABLE tasks on that CPU
115 */
116 if (bg->group[idx].tasks == 0)
117 continue;
118 boost_max = max(boost_max, bg->group[idx].boost);
119 }
120
121 bg->boost_max = boost_max;
122}
123
124static int
125schedtune_boostgroup_update(int idx, int boost)
126{
127 struct boost_groups *bg;
128 int cur_boost_max;
129 int old_boost;
130 int cpu;
131
132 /* Update per CPU boost groups */
133 for_each_possible_cpu(cpu) {
134 bg = &per_cpu(cpu_boost_groups, cpu);
135
136 /*
137 * Keep track of current boost values to compute the per CPU
138 * maximum only when it has been affected by the new value of
139 * the updated boost group
140 */
141 cur_boost_max = bg->boost_max;
142 old_boost = bg->group[idx].boost;
143
144 /* Update the boost value of this boost group */
145 bg->group[idx].boost = boost;
146
147 /* Check if this update increase current max */
148 if (boost > cur_boost_max && bg->group[idx].tasks) {
149 bg->boost_max = boost;
150 continue;
151 }
152
153 /* Check if this update has decreased current max */
154 if (cur_boost_max == old_boost && old_boost > boost)
155 schedtune_cpu_update(cpu);
156 }
157
158 return 0;
159}
160
Patrick Bellasiae710302015-06-23 09:17:54 +0100161static u64
162boost_read(struct cgroup_subsys_state *css, struct cftype *cft)
163{
164 struct schedtune *st = css_st(css);
165
166 return st->boost;
167}
168
169static int
170boost_write(struct cgroup_subsys_state *css, struct cftype *cft,
171 u64 boost)
172{
173 struct schedtune *st = css_st(css);
174
175 if (boost < 0 || boost > 100)
176 return -EINVAL;
177
178 st->boost = boost;
179 if (css == &root_schedtune.css)
180 sysctl_sched_cfs_boost = boost;
181
Patrick Bellasi9a871ed2016-01-14 12:31:35 +0000182 /* Update CPU boost */
183 schedtune_boostgroup_update(st->idx, st->boost);
184
Patrick Bellasiae710302015-06-23 09:17:54 +0100185 return 0;
186}
187
188static struct cftype files[] = {
189 {
190 .name = "boost",
191 .read_u64 = boost_read,
192 .write_u64 = boost_write,
193 },
194 { } /* terminate */
195};
196
197static int
198schedtune_boostgroup_init(struct schedtune *st)
199{
Patrick Bellasi9a871ed2016-01-14 12:31:35 +0000200 struct boost_groups *bg;
201 int cpu;
202
Patrick Bellasiae710302015-06-23 09:17:54 +0100203 /* Keep track of allocated boost groups */
204 allocated_group[st->idx] = st;
205
Patrick Bellasi9a871ed2016-01-14 12:31:35 +0000206 /* Initialize the per CPU boost groups */
207 for_each_possible_cpu(cpu) {
208 bg = &per_cpu(cpu_boost_groups, cpu);
209 bg->group[st->idx].boost = 0;
210 bg->group[st->idx].tasks = 0;
211 }
212
Patrick Bellasiae710302015-06-23 09:17:54 +0100213 return 0;
214}
215
216static int
217schedtune_init(void)
218{
219 struct boost_groups *bg;
220 int cpu;
221
222 /* Initialize the per CPU boost groups */
223 for_each_possible_cpu(cpu) {
224 bg = &per_cpu(cpu_boost_groups, cpu);
225 memset(bg, 0, sizeof(struct boost_groups));
226 }
227
228 pr_info(" schedtune configured to support %d boost groups\n",
229 BOOSTGROUPS_COUNT);
230 return 0;
231}
232
233static struct cgroup_subsys_state *
234schedtune_css_alloc(struct cgroup_subsys_state *parent_css)
235{
236 struct schedtune *st;
237 int idx;
238
239 if (!parent_css) {
240 schedtune_init();
241 return &root_schedtune.css;
242 }
243
244 /* Allow only single level hierachies */
245 if (parent_css != &root_schedtune.css) {
246 pr_err("Nested SchedTune boosting groups not allowed\n");
247 return ERR_PTR(-ENOMEM);
248 }
249
250 /* Allow only a limited number of boosting groups */
251 for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx)
252 if (!allocated_group[idx])
253 break;
254 if (idx == BOOSTGROUPS_COUNT) {
255 pr_err("Trying to create more than %d SchedTune boosting groups\n",
256 BOOSTGROUPS_COUNT);
257 return ERR_PTR(-ENOSPC);
258 }
259
260 st = kzalloc(sizeof(*st), GFP_KERNEL);
261 if (!st)
262 goto out;
263
264 /* Initialize per CPUs boost group support */
265 st->idx = idx;
266 if (schedtune_boostgroup_init(st))
267 goto release;
268
269 return &st->css;
270
271release:
272 kfree(st);
273out:
274 return ERR_PTR(-ENOMEM);
275}
276
277static void
278schedtune_boostgroup_release(struct schedtune *st)
279{
Patrick Bellasi9a871ed2016-01-14 12:31:35 +0000280 /* Reset this boost group */
281 schedtune_boostgroup_update(st->idx, 0);
282
Patrick Bellasiae710302015-06-23 09:17:54 +0100283 /* Keep track of allocated boost groups */
284 allocated_group[st->idx] = NULL;
285}
286
287static void
288schedtune_css_free(struct cgroup_subsys_state *css)
289{
290 struct schedtune *st = css_st(css);
291
292 schedtune_boostgroup_release(st);
293 kfree(st);
294}
295
296struct cgroup_subsys schedtune_cgrp_subsys = {
297 .css_alloc = schedtune_css_alloc,
298 .css_free = schedtune_css_free,
299 .legacy_cftypes = files,
300 .early_init = 1,
301};
302
303#endif /* CONFIG_CGROUP_SCHEDTUNE */
304
Patrick Bellasi69fa4c72015-06-22 18:11:44 +0100305int
306sysctl_sched_cfs_boost_handler(struct ctl_table *table, int write,
307 void __user *buffer, size_t *lenp,
308 loff_t *ppos)
309{
310 int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
311
312 if (ret || !write)
313 return ret;
314
315 return 0;
316}