blob: 8b9ca8610a3629d511acac2b81176c90bcc4807a [file] [log] [blame]
Patrick Bellasiae710302015-06-23 09:17:54 +01001#include <linux/cgroup.h>
2#include <linux/err.h>
Patrick Bellasi2f369bb2016-01-12 18:12:13 +00003#include <linux/kernel.h>
Patrick Bellasiae710302015-06-23 09:17:54 +01004#include <linux/percpu.h>
5#include <linux/printk.h>
Patrick Bellasi2f369bb2016-01-12 18:12:13 +00006#include <linux/reciprocal_div.h>
Patrick Bellasiedd28d32015-07-07 15:33:20 +01007#include <linux/rcupdate.h>
Patrick Bellasiae710302015-06-23 09:17:54 +01008#include <linux/slab.h>
9
Patrick Bellasi050dcb82015-06-22 13:49:07 +010010#include <trace/events/sched.h>
11
Patrick Bellasi69fa4c72015-06-22 18:11:44 +010012#include "sched.h"
13
14unsigned int sysctl_sched_cfs_boost __read_mostly;
15
Patrick Bellasi2f369bb2016-01-12 18:12:13 +000016/*
17 * System energy normalization constants
18 */
19static struct target_nrg {
20 unsigned long min_power;
21 unsigned long max_power;
22 struct reciprocal_value rdiv;
23} schedtune_target_nrg;
24
25/* Performance Boost region (B) threshold params */
26static int perf_boost_idx;
27
28/* Performance Constraint region (C) threshold params */
29static int perf_constrain_idx;
30
31/**
32 * Performance-Energy (P-E) Space thresholds constants
33 */
34struct threshold_params {
35 int nrg_gain;
36 int cap_gain;
37};
38
39/*
40 * System specific P-E space thresholds constants
41 */
42static struct threshold_params
43threshold_gains[] = {
44 { 0, 4 }, /* >= 0% */
45 { 0, 4 }, /* >= 10% */
46 { 1, 4 }, /* >= 20% */
47 { 2, 4 }, /* >= 30% */
48 { 3, 4 }, /* >= 40% */
49 { 4, 3 }, /* >= 50% */
50 { 4, 2 }, /* >= 60% */
51 { 4, 1 }, /* >= 70% */
52 { 4, 0 }, /* >= 80% */
53 { 4, 0 } /* >= 90% */
54};
55
56static int
57__schedtune_accept_deltas(int nrg_delta, int cap_delta,
58 int perf_boost_idx, int perf_constrain_idx)
59{
60 int payoff = -INT_MAX;
61
62 /* Performance Boost (B) region */
63 if (nrg_delta > 0 && cap_delta > 0) {
64 /*
65 * Evaluate "Performance Boost" vs "Energy Increase"
66 * payoff criteria:
67 * cap_delta / nrg_delta < cap_gain / nrg_gain
68 * which is:
69 * nrg_delta * cap_gain > cap_delta * nrg_gain
70 */
71 payoff = nrg_delta * threshold_gains[perf_boost_idx].cap_gain;
72 payoff -= cap_delta * threshold_gains[perf_boost_idx].nrg_gain;
73 return payoff;
74 }
75
76 /* Performance Constraint (C) region */
77 if (nrg_delta < 0 && cap_delta < 0) {
78 /*
79 * Evaluate "Performance Boost" vs "Energy Increase"
80 * payoff criteria:
81 * cap_delta / nrg_delta > cap_gain / nrg_gain
82 * which is:
83 * cap_delta * nrg_gain > nrg_delta * cap_gain
84 */
85 payoff = cap_delta * threshold_gains[perf_constrain_idx].nrg_gain;
86 payoff -= nrg_delta * threshold_gains[perf_constrain_idx].cap_gain;
87 return payoff;
88 }
89
90 /* Default: reject schedule candidate */
91 return payoff;
92}
93
Patrick Bellasiae710302015-06-23 09:17:54 +010094#ifdef CONFIG_CGROUP_SCHEDTUNE
95
96/*
97 * EAS scheduler tunables for task groups.
98 */
99
100/* SchdTune tunables for a group of tasks */
101struct schedtune {
102 /* SchedTune CGroup subsystem */
103 struct cgroup_subsys_state css;
104
105 /* Boost group allocated ID */
106 int idx;
107
108 /* Boost value for tasks on that SchedTune CGroup */
109 int boost;
110
Patrick Bellasi2f369bb2016-01-12 18:12:13 +0000111 /* Performance Boost (B) region threshold params */
112 int perf_boost_idx;
113
114 /* Performance Constraint (C) region threshold params */
115 int perf_constrain_idx;
Patrick Bellasiae710302015-06-23 09:17:54 +0100116};
117
118static inline struct schedtune *css_st(struct cgroup_subsys_state *css)
119{
120 return css ? container_of(css, struct schedtune, css) : NULL;
121}
122
123static inline struct schedtune *task_schedtune(struct task_struct *tsk)
124{
125 return css_st(task_css(tsk, schedtune_cgrp_id));
126}
127
128static inline struct schedtune *parent_st(struct schedtune *st)
129{
130 return css_st(st->css.parent);
131}
132
133/*
134 * SchedTune root control group
135 * The root control group is used to defined a system-wide boosting tuning,
136 * which is applied to all tasks in the system.
137 * Task specific boost tuning could be specified by creating and
138 * configuring a child control group under the root one.
139 * By default, system-wide boosting is disabled, i.e. no boosting is applied
140 * to tasks which are not into a child control group.
141 */
142static struct schedtune
143root_schedtune = {
144 .boost = 0,
Patrick Bellasi2f369bb2016-01-12 18:12:13 +0000145 .perf_boost_idx = 0,
146 .perf_constrain_idx = 0,
Patrick Bellasiae710302015-06-23 09:17:54 +0100147};
148
Patrick Bellasi2f369bb2016-01-12 18:12:13 +0000149int
150schedtune_accept_deltas(int nrg_delta, int cap_delta,
151 struct task_struct *task)
152{
153 struct schedtune *ct;
154 int perf_boost_idx;
155 int perf_constrain_idx;
156
157 /* Optimal (O) region */
158 if (nrg_delta < 0 && cap_delta > 0)
159 return INT_MAX;
160
161 /* Suboptimal (S) region */
162 if (nrg_delta > 0 && cap_delta < 0)
163 return -INT_MAX;
164
165 /* Get task specific perf Boost/Constraints indexes */
166 rcu_read_lock();
167 ct = task_schedtune(task);
168 perf_boost_idx = ct->perf_boost_idx;
169 perf_constrain_idx = ct->perf_constrain_idx;
170 rcu_read_unlock();
171
172 return __schedtune_accept_deltas(nrg_delta, cap_delta,
173 perf_boost_idx, perf_constrain_idx);
174}
175
Patrick Bellasiae710302015-06-23 09:17:54 +0100176/*
177 * Maximum number of boost groups to support
178 * When per-task boosting is used we still allow only limited number of
179 * boost groups for two main reasons:
180 * 1. on a real system we usually have only few classes of workloads which
181 * make sense to boost with different values (e.g. background vs foreground
182 * tasks, interactive vs low-priority tasks)
183 * 2. a limited number allows for a simpler and more memory/time efficient
184 * implementation especially for the computation of the per-CPU boost
185 * value
186 */
187#define BOOSTGROUPS_COUNT 4
188
189/* Array of configured boostgroups */
190static struct schedtune *allocated_group[BOOSTGROUPS_COUNT] = {
191 &root_schedtune,
192 NULL,
193};
194
195/* SchedTune boost groups
196 * Keep track of all the boost groups which impact on CPU, for example when a
197 * CPU has two RUNNABLE tasks belonging to two different boost groups and thus
198 * likely with different boost values.
199 * Since on each system we expect only a limited number of boost groups, here
200 * we use a simple array to keep track of the metrics required to compute the
201 * maximum per-CPU boosting value.
202 */
203struct boost_groups {
204 /* Maximum boost value for all RUNNABLE tasks on a CPU */
205 unsigned boost_max;
206 struct {
207 /* The boost for tasks on that boost group */
208 unsigned boost;
209 /* Count of RUNNABLE tasks on that boost group */
210 unsigned tasks;
211 } group[BOOSTGROUPS_COUNT];
212};
213
214/* Boost groups affecting each CPU in the system */
215DEFINE_PER_CPU(struct boost_groups, cpu_boost_groups);
216
Patrick Bellasi9a871ed2016-01-14 12:31:35 +0000217static void
218schedtune_cpu_update(int cpu)
219{
220 struct boost_groups *bg;
221 unsigned boost_max;
222 int idx;
223
224 bg = &per_cpu(cpu_boost_groups, cpu);
225
226 /* The root boost group is always active */
227 boost_max = bg->group[0].boost;
228 for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx) {
229 /*
230 * A boost group affects a CPU only if it has
231 * RUNNABLE tasks on that CPU
232 */
233 if (bg->group[idx].tasks == 0)
234 continue;
235 boost_max = max(boost_max, bg->group[idx].boost);
236 }
237
238 bg->boost_max = boost_max;
239}
240
241static int
242schedtune_boostgroup_update(int idx, int boost)
243{
244 struct boost_groups *bg;
245 int cur_boost_max;
246 int old_boost;
247 int cpu;
248
249 /* Update per CPU boost groups */
250 for_each_possible_cpu(cpu) {
251 bg = &per_cpu(cpu_boost_groups, cpu);
252
253 /*
254 * Keep track of current boost values to compute the per CPU
255 * maximum only when it has been affected by the new value of
256 * the updated boost group
257 */
258 cur_boost_max = bg->boost_max;
259 old_boost = bg->group[idx].boost;
260
261 /* Update the boost value of this boost group */
262 bg->group[idx].boost = boost;
263
264 /* Check if this update increase current max */
265 if (boost > cur_boost_max && bg->group[idx].tasks) {
266 bg->boost_max = boost;
267 continue;
268 }
269
270 /* Check if this update has decreased current max */
271 if (cur_boost_max == old_boost && old_boost > boost)
272 schedtune_cpu_update(cpu);
273 }
274
275 return 0;
276}
277
Patrick Bellasiedd28d32015-07-07 15:33:20 +0100278static inline void
279schedtune_tasks_update(struct task_struct *p, int cpu, int idx, int task_count)
280{
281 struct boost_groups *bg;
282 int tasks;
283
284 bg = &per_cpu(cpu_boost_groups, cpu);
285
286 /* Update boosted tasks count while avoiding to make it negative */
287 if (task_count < 0 && bg->group[idx].tasks <= -task_count)
288 bg->group[idx].tasks = 0;
289 else
290 bg->group[idx].tasks += task_count;
291
292 /* Boost group activation or deactivation on that RQ */
293 tasks = bg->group[idx].tasks;
294 if (tasks == 1 || tasks == 0)
295 schedtune_cpu_update(cpu);
296}
297
298/*
299 * NOTE: This function must be called while holding the lock on the CPU RQ
300 */
301void schedtune_enqueue_task(struct task_struct *p, int cpu)
302{
303 struct schedtune *st;
304 int idx;
305
306 /*
307 * When a task is marked PF_EXITING by do_exit() it's going to be
308 * dequeued and enqueued multiple times in the exit path.
309 * Thus we avoid any further update, since we do not want to change
310 * CPU boosting while the task is exiting.
311 */
312 if (p->flags & PF_EXITING)
313 return;
314
315 /* Get task boost group */
316 rcu_read_lock();
317 st = task_schedtune(p);
318 idx = st->idx;
319 rcu_read_unlock();
320
321 schedtune_tasks_update(p, cpu, idx, 1);
322}
323
324/*
325 * NOTE: This function must be called while holding the lock on the CPU RQ
326 */
327void schedtune_dequeue_task(struct task_struct *p, int cpu)
328{
329 struct schedtune *st;
330 int idx;
331
332 /*
333 * When a task is marked PF_EXITING by do_exit() it's going to be
334 * dequeued and enqueued multiple times in the exit path.
335 * Thus we avoid any further update, since we do not want to change
336 * CPU boosting while the task is exiting.
337 * The last dequeue will be done by cgroup exit() callback.
338 */
339 if (p->flags & PF_EXITING)
340 return;
341
342 /* Get task boost group */
343 rcu_read_lock();
344 st = task_schedtune(p);
345 idx = st->idx;
346 rcu_read_unlock();
347
348 schedtune_tasks_update(p, cpu, idx, -1);
349}
350
351int schedtune_cpu_boost(int cpu)
352{
353 struct boost_groups *bg;
354
355 bg = &per_cpu(cpu_boost_groups, cpu);
356 return bg->boost_max;
357}
358
Patrick Bellasi9b2b8da2016-01-14 18:31:53 +0000359int schedtune_task_boost(struct task_struct *p)
360{
361 struct schedtune *st;
362 int task_boost;
363
364 /* Get task boost value */
365 rcu_read_lock();
366 st = task_schedtune(p);
367 task_boost = st->boost;
368 rcu_read_unlock();
369
370 return task_boost;
371}
372
Patrick Bellasiae710302015-06-23 09:17:54 +0100373static u64
374boost_read(struct cgroup_subsys_state *css, struct cftype *cft)
375{
376 struct schedtune *st = css_st(css);
377
378 return st->boost;
379}
380
381static int
382boost_write(struct cgroup_subsys_state *css, struct cftype *cft,
383 u64 boost)
384{
385 struct schedtune *st = css_st(css);
386
387 if (boost < 0 || boost > 100)
388 return -EINVAL;
389
390 st->boost = boost;
391 if (css == &root_schedtune.css)
392 sysctl_sched_cfs_boost = boost;
393
Patrick Bellasi9a871ed2016-01-14 12:31:35 +0000394 /* Update CPU boost */
395 schedtune_boostgroup_update(st->idx, st->boost);
396
Patrick Bellasi050dcb82015-06-22 13:49:07 +0100397 trace_sched_tune_config(st->boost);
398
Patrick Bellasiae710302015-06-23 09:17:54 +0100399 return 0;
400}
401
402static struct cftype files[] = {
403 {
404 .name = "boost",
405 .read_u64 = boost_read,
406 .write_u64 = boost_write,
407 },
408 { } /* terminate */
409};
410
411static int
412schedtune_boostgroup_init(struct schedtune *st)
413{
Patrick Bellasi9a871ed2016-01-14 12:31:35 +0000414 struct boost_groups *bg;
415 int cpu;
416
Patrick Bellasiae710302015-06-23 09:17:54 +0100417 /* Keep track of allocated boost groups */
418 allocated_group[st->idx] = st;
419
Patrick Bellasi9a871ed2016-01-14 12:31:35 +0000420 /* Initialize the per CPU boost groups */
421 for_each_possible_cpu(cpu) {
422 bg = &per_cpu(cpu_boost_groups, cpu);
423 bg->group[st->idx].boost = 0;
424 bg->group[st->idx].tasks = 0;
425 }
426
Patrick Bellasiae710302015-06-23 09:17:54 +0100427 return 0;
428}
429
430static int
431schedtune_init(void)
432{
433 struct boost_groups *bg;
434 int cpu;
435
436 /* Initialize the per CPU boost groups */
437 for_each_possible_cpu(cpu) {
438 bg = &per_cpu(cpu_boost_groups, cpu);
439 memset(bg, 0, sizeof(struct boost_groups));
440 }
441
442 pr_info(" schedtune configured to support %d boost groups\n",
443 BOOSTGROUPS_COUNT);
444 return 0;
445}
446
447static struct cgroup_subsys_state *
448schedtune_css_alloc(struct cgroup_subsys_state *parent_css)
449{
450 struct schedtune *st;
451 int idx;
452
453 if (!parent_css) {
454 schedtune_init();
455 return &root_schedtune.css;
456 }
457
458 /* Allow only single level hierachies */
459 if (parent_css != &root_schedtune.css) {
460 pr_err("Nested SchedTune boosting groups not allowed\n");
461 return ERR_PTR(-ENOMEM);
462 }
463
464 /* Allow only a limited number of boosting groups */
465 for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx)
466 if (!allocated_group[idx])
467 break;
468 if (idx == BOOSTGROUPS_COUNT) {
469 pr_err("Trying to create more than %d SchedTune boosting groups\n",
470 BOOSTGROUPS_COUNT);
471 return ERR_PTR(-ENOSPC);
472 }
473
474 st = kzalloc(sizeof(*st), GFP_KERNEL);
475 if (!st)
476 goto out;
477
478 /* Initialize per CPUs boost group support */
479 st->idx = idx;
480 if (schedtune_boostgroup_init(st))
481 goto release;
482
483 return &st->css;
484
485release:
486 kfree(st);
487out:
488 return ERR_PTR(-ENOMEM);
489}
490
491static void
492schedtune_boostgroup_release(struct schedtune *st)
493{
Patrick Bellasi9a871ed2016-01-14 12:31:35 +0000494 /* Reset this boost group */
495 schedtune_boostgroup_update(st->idx, 0);
496
Patrick Bellasiae710302015-06-23 09:17:54 +0100497 /* Keep track of allocated boost groups */
498 allocated_group[st->idx] = NULL;
499}
500
501static void
502schedtune_css_free(struct cgroup_subsys_state *css)
503{
504 struct schedtune *st = css_st(css);
505
506 schedtune_boostgroup_release(st);
507 kfree(st);
508}
509
510struct cgroup_subsys schedtune_cgrp_subsys = {
511 .css_alloc = schedtune_css_alloc,
512 .css_free = schedtune_css_free,
513 .legacy_cftypes = files,
514 .early_init = 1,
515};
516
Patrick Bellasi2f369bb2016-01-12 18:12:13 +0000517#else /* CONFIG_CGROUP_SCHEDTUNE */
518
519int
520schedtune_accept_deltas(int nrg_delta, int cap_delta,
521 struct task_struct *task)
522{
523 /* Optimal (O) region */
524 if (nrg_delta < 0 && cap_delta > 0)
525 return INT_MAX;
526
527 /* Suboptimal (S) region */
528 if (nrg_delta > 0 && cap_delta < 0)
529 return -INT_MAX;
530
531 return __schedtune_accept_deltas(nrg_delta, cap_delta,
532 perf_boost_idx, perf_constrain_idx);
533}
534
Patrick Bellasiae710302015-06-23 09:17:54 +0100535#endif /* CONFIG_CGROUP_SCHEDTUNE */
536
Patrick Bellasi69fa4c72015-06-22 18:11:44 +0100537int
538sysctl_sched_cfs_boost_handler(struct ctl_table *table, int write,
539 void __user *buffer, size_t *lenp,
540 loff_t *ppos)
541{
542 int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
543
544 if (ret || !write)
545 return ret;
546
Patrick Bellasi2f369bb2016-01-12 18:12:13 +0000547 /* Performance Boost (B) region threshold params */
548 perf_boost_idx = sysctl_sched_cfs_boost;
549 perf_boost_idx /= 10;
550
551 /* Performance Constraint (C) region threshold params */
552 perf_constrain_idx = 100 - sysctl_sched_cfs_boost;
553 perf_constrain_idx /= 10;
554
Patrick Bellasi69fa4c72015-06-22 18:11:44 +0100555 return 0;
556}
Patrick Bellasi2f369bb2016-01-12 18:12:13 +0000557
558/*
559 * System energy normalization
560 * Returns the normalized value, in the range [0..SCHED_LOAD_SCALE],
561 * corresponding to the specified energy variation.
562 */
563int
564schedtune_normalize_energy(int energy_diff)
565{
566 u32 normalized_nrg;
567 int max_delta;
568
569#ifdef CONFIG_SCHED_DEBUG
570 /* Check for boundaries */
571 max_delta = schedtune_target_nrg.max_power;
572 max_delta -= schedtune_target_nrg.min_power;
573 WARN_ON(abs(energy_diff) >= max_delta);
574#endif
575
576 /* Do scaling using positive numbers to increase the range */
577 normalized_nrg = (energy_diff < 0) ? -energy_diff : energy_diff;
578
579 /* Scale by energy magnitude */
580 normalized_nrg <<= SCHED_CAPACITY_SHIFT;
581
582 /* Normalize on max energy for target platform */
583 normalized_nrg = reciprocal_divide(
584 normalized_nrg, schedtune_target_nrg.rdiv);
585
586 return (energy_diff < 0) ? -normalized_nrg : normalized_nrg;
587}
588
589#ifdef CONFIG_SCHED_DEBUG
590static void
591schedtune_test_nrg(unsigned long delta_pwr)
592{
593 unsigned long test_delta_pwr;
594 unsigned long test_norm_pwr;
595 int idx;
596
597 /*
598 * Check normalization constants using some constant system
599 * energy values
600 */
601 pr_info("schedtune: verify normalization constants...\n");
602 for (idx = 0; idx < 6; ++idx) {
603 test_delta_pwr = delta_pwr >> idx;
604
605 /* Normalize on max energy for target platform */
606 test_norm_pwr = reciprocal_divide(
607 test_delta_pwr << SCHED_CAPACITY_SHIFT,
608 schedtune_target_nrg.rdiv);
609
610 pr_info("schedtune: max_pwr/2^%d: %4lu => norm_pwr: %5lu\n",
611 idx, test_delta_pwr, test_norm_pwr);
612 }
613}
614#else
615#define schedtune_test_nrg(delta_pwr)
616#endif
617
618/*
619 * Compute the min/max power consumption of a cluster and all its CPUs
620 */
621static void
622schedtune_add_cluster_nrg(
623 struct sched_domain *sd,
624 struct sched_group *sg,
625 struct target_nrg *ste)
626{
627 struct sched_domain *sd2;
628 struct sched_group *sg2;
629
630 struct cpumask *cluster_cpus;
631 char str[32];
632
633 unsigned long min_pwr;
634 unsigned long max_pwr;
635 int cpu;
636
637 /* Get Cluster energy using EM data for the first CPU */
638 cluster_cpus = sched_group_cpus(sg);
639 snprintf(str, 32, "CLUSTER[%*pbl]",
640 cpumask_pr_args(cluster_cpus));
641
642 min_pwr = sg->sge->idle_states[sg->sge->nr_idle_states - 1].power;
643 max_pwr = sg->sge->cap_states[sg->sge->nr_cap_states - 1].power;
644 pr_info("schedtune: %-17s min_pwr: %5lu max_pwr: %5lu\n",
645 str, min_pwr, max_pwr);
646
647 /*
648 * Keep track of this cluster's energy in the computation of the
649 * overall system energy
650 */
651 ste->min_power += min_pwr;
652 ste->max_power += max_pwr;
653
654 /* Get CPU energy using EM data for each CPU in the group */
655 for_each_cpu(cpu, cluster_cpus) {
656 /* Get a SD view for the specific CPU */
657 for_each_domain(cpu, sd2) {
658 /* Get the CPU group */
659 sg2 = sd2->groups;
660 min_pwr = sg2->sge->idle_states[sg2->sge->nr_idle_states - 1].power;
661 max_pwr = sg2->sge->cap_states[sg2->sge->nr_cap_states - 1].power;
662
663 ste->min_power += min_pwr;
664 ste->max_power += max_pwr;
665
666 snprintf(str, 32, "CPU[%d]", cpu);
667 pr_info("schedtune: %-17s min_pwr: %5lu max_pwr: %5lu\n",
668 str, min_pwr, max_pwr);
669
670 /*
671 * Assume we have EM data only at the CPU and
672 * the upper CLUSTER level
673 */
674 BUG_ON(!cpumask_equal(
675 sched_group_cpus(sg),
676 sched_group_cpus(sd2->parent->groups)
677 ));
678 break;
679 }
680 }
681}
682
683/*
684 * Initialize the constants required to compute normalized energy.
685 * The values of these constants depends on the EM data for the specific
686 * target system and topology.
687 * Thus, this function is expected to be called by the code
688 * that bind the EM to the topology information.
689 */
690static int
691schedtune_init_late(void)
692{
693 struct target_nrg *ste = &schedtune_target_nrg;
694 unsigned long delta_pwr = 0;
695 struct sched_domain *sd;
696 struct sched_group *sg;
697
698 pr_info("schedtune: init normalization constants...\n");
699 ste->max_power = 0;
700 ste->min_power = 0;
701
702 rcu_read_lock();
703
704 /*
705 * When EAS is in use, we always have a pointer to the highest SD
706 * which provides EM data.
707 */
708 sd = rcu_dereference(per_cpu(sd_ea, cpumask_first(cpu_online_mask)));
709 if (!sd) {
710 pr_info("schedtune: no energy model data\n");
711 goto nodata;
712 }
713
714 sg = sd->groups;
715 do {
716 schedtune_add_cluster_nrg(sd, sg, ste);
717 } while (sg = sg->next, sg != sd->groups);
718
719 rcu_read_unlock();
720
721 pr_info("schedtune: %-17s min_pwr: %5lu max_pwr: %5lu\n",
722 "SYSTEM", ste->min_power, ste->max_power);
723
724 /* Compute normalization constants */
725 delta_pwr = ste->max_power - ste->min_power;
726 ste->rdiv = reciprocal_value(delta_pwr);
727 pr_info("schedtune: using normalization constants mul: %u sh1: %u sh2: %u\n",
728 ste->rdiv.m, ste->rdiv.sh1, ste->rdiv.sh2);
729
730 schedtune_test_nrg(delta_pwr);
731 return 0;
732
733nodata:
734 rcu_read_unlock();
735 return -EINVAL;
736}
737late_initcall(schedtune_init_late);