summaryrefslogtreecommitdiff
path: root/kernel/sched/boost.c
diff options
context:
space:
mode:
authorSyed Rameez Mustafa <rameezmustafa@codeaurora.org>2016-08-31 16:54:12 -0700
committerSyed Rameez Mustafa <rameezmustafa@codeaurora.org>2016-11-16 17:57:56 -0800
commit30fc7742350f45a6b3667880de0a72c68509ccc5 (patch)
tree840761c4bedb6168d261de7f30c4a64ac8740bb2 /kernel/sched/boost.c
parentfd5b530593795c9aa145c7f9aa3eb817d3841af3 (diff)
sched/hmp: Enhance co-location and scheduler boost features
The recent introduction of the schedtune cgroup controller has provided the scheduler with added flexibility in terms of some of it's placement features. In particular each cgroup under the schedtune controller can now specify: 1) Whether it needs co-location along with other cgroups 2) Whether it is eligible for scheduler boost (sched_boost_enabled) 3) Whether the kernel can override the boost eligibility when necessary (sched_boost_no_override) The scheduler now creates a reserved co-location group at boot. This group is used to co-locate all tasks that form part of any one of the cgroups that have co-location enabled. This reserved group can neither be destroyed nor reused for other purposes. Furthermore, cgroups are only allowed to indicate their co-location preference once at boot. Further updates are disallowed. Since we are now creating co-location groups for an extended period of time, there are a few other factors to consider when determining the preferred cluster for the group. We first exclude any tasks in the group that have not been observed to be running for a significant amount of time. Secondly we introduce the notion of group up and down migrate tunables to allow different migration policies than individual tasks. Lastly we break co-location if a single task in a group exceeds up-migrate but the total load of the group does not exceed group up-migrate. In terms of sched_boost, the scheduler now supports multiple types of boost. These are: 1) FULL_THROTTLE : Force up-migrate tasks belonging any cgroup that has the sched_boost_enabled flag turned on. Little CPUs will only be used when big CPUs can no longer accommodate tasks. Also up-migrate all RT tasks. 2) CONSERVATIVE : Override the sched_boost_enabled flag for all cgroups except those that have the sched_boost_no_override flag set. Force up-migrate all tasks belonging to only those cgroups that still remain eligible for boost. RT tasks do not get force up migrated. 3) RESTRAINED : Start frequency aggregation for co-located tasks. This type of boost does not force up-migrate any task. Finally the boost API removes ref-counting. This means that there can only be a single entity using boost at any given time. If multiple entities are managing boost, they are required to be well behaved so that they don't interfere with one another. Even for a single client, it is not possible to switch directly from one boost type to another. Boost must be first turned off before switching over to a new type. Change-Id: I8d224a70cbef162f27078b62b73acaa22670861d Signed-off-by: Syed Rameez Mustafa <rameezmustafa@codeaurora.org>
Diffstat (limited to 'kernel/sched/boost.c')
-rw-r--r--kernel/sched/boost.c226
1 files changed, 226 insertions, 0 deletions
diff --git a/kernel/sched/boost.c b/kernel/sched/boost.c
new file mode 100644
index 000000000000..fcfda385b74a
--- /dev/null
+++ b/kernel/sched/boost.c
@@ -0,0 +1,226 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "sched.h"
+#include <linux/of.h>
+#include <linux/sched/core_ctl.h>
+#include <trace/events/sched.h>
+
+/*
+ * Scheduler boost is a mechanism to temporarily place tasks on CPUs
+ * with higher capacity than those where a task would have normally
+ * ended up with their load characteristics. Any entity enabling
+ * boost is responsible for disabling it as well.
+ */
+
+unsigned int sysctl_sched_boost;
+static enum sched_boost_policy boost_policy;
+static enum sched_boost_policy boost_policy_dt = SCHED_BOOST_NONE;
+static DEFINE_MUTEX(boost_mutex);
+static unsigned int freq_aggr_threshold_backup;
+
+static inline void boost_kick(int cpu)
+{
+ struct rq *rq = cpu_rq(cpu);
+
+ if (!test_and_set_bit(BOOST_KICK, &rq->hmp_flags))
+ smp_send_reschedule(cpu);
+}
+
+static void boost_kick_cpus(void)
+{
+ int i;
+ struct cpumask kick_mask;
+
+ if (boost_policy != SCHED_BOOST_ON_BIG)
+ return;
+
+ cpumask_andnot(&kick_mask, cpu_online_mask, cpu_isolated_mask);
+
+ for_each_cpu(i, &kick_mask) {
+ if (cpu_capacity(i) != max_capacity)
+ boost_kick(i);
+ }
+}
+
+int got_boost_kick(void)
+{
+ int cpu = smp_processor_id();
+ struct rq *rq = cpu_rq(cpu);
+
+ return test_bit(BOOST_KICK, &rq->hmp_flags);
+}
+
+void clear_boost_kick(int cpu)
+{
+ struct rq *rq = cpu_rq(cpu);
+
+ clear_bit(BOOST_KICK, &rq->hmp_flags);
+}
+
+/*
+ * Scheduler boost type and boost policy might at first seem unrelated,
+ * however, there exists a connection between them that will allow us
+ * to use them interchangeably during placement decisions. We'll explain
+ * the connection here in one possible way so that the implications are
+ * clear when looking at placement policies.
+ *
+ * When policy = SCHED_BOOST_NONE, type is either none or RESTRAINED
+ * When policy = SCHED_BOOST_ON_ALL or SCHED_BOOST_ON_BIG, type can
+ * neither be none nor RESTRAINED.
+ */
+static void set_boost_policy(int type)
+{
+ if (type == SCHED_BOOST_NONE || type == RESTRAINED_BOOST) {
+ boost_policy = SCHED_BOOST_NONE;
+ return;
+ }
+
+ if (boost_policy_dt) {
+ boost_policy = boost_policy_dt;
+ return;
+ }
+
+ if (min_possible_efficiency != max_possible_efficiency) {
+ boost_policy = SCHED_BOOST_ON_BIG;
+ return;
+ }
+
+ boost_policy = SCHED_BOOST_ON_ALL;
+}
+
+enum sched_boost_policy sched_boost_policy(void)
+{
+ return boost_policy;
+}
+
+static bool verify_boost_params(int old_val, int new_val)
+{
+ /*
+ * Boost can only be turned on or off. There is no possiblity of
+ * switching from one boost type to another or to set the same
+ * kind of boost several times.
+ */
+ return !(!!old_val == !!new_val);
+}
+
+static void _sched_set_boost(int old_val, int type)
+{
+ switch (type) {
+ case NO_BOOST:
+ if (old_val == FULL_THROTTLE_BOOST)
+ core_ctl_set_boost(false);
+ else if (old_val == CONSERVATIVE_BOOST)
+ restore_cgroup_boost_settings();
+ else
+ update_freq_aggregate_threshold(
+ freq_aggr_threshold_backup);
+ break;
+
+ case FULL_THROTTLE_BOOST:
+ core_ctl_set_boost(true);
+ boost_kick_cpus();
+ break;
+
+ case CONSERVATIVE_BOOST:
+ update_cgroup_boost_settings();
+ boost_kick_cpus();
+ break;
+
+ case RESTRAINED_BOOST:
+ freq_aggr_threshold_backup =
+ update_freq_aggregate_threshold(1);
+ break;
+
+ default:
+ WARN_ON(1);
+ return;
+ }
+
+ set_boost_policy(type);
+ sysctl_sched_boost = type;
+ trace_sched_set_boost(type);
+}
+
+void sched_boost_parse_dt(void)
+{
+ struct device_node *sn;
+ const char *boost_policy;
+
+ if (!sched_enable_hmp)
+ return;
+
+ sn = of_find_node_by_path("/sched-hmp");
+ if (!sn)
+ return;
+
+ if (!of_property_read_string(sn, "boost-policy", &boost_policy)) {
+ if (!strcmp(boost_policy, "boost-on-big"))
+ boost_policy_dt = SCHED_BOOST_ON_BIG;
+ else if (!strcmp(boost_policy, "boost-on-all"))
+ boost_policy_dt = SCHED_BOOST_ON_ALL;
+ }
+}
+
+int sched_set_boost(int type)
+{
+ int ret = 0;
+
+ if (!sched_enable_hmp)
+ return -EINVAL;
+
+ mutex_lock(&boost_mutex);
+
+ if (verify_boost_params(sysctl_sched_boost, type))
+ _sched_set_boost(sysctl_sched_boost, type);
+ else
+ ret = -EINVAL;
+
+ mutex_unlock(&boost_mutex);
+ return ret;
+}
+
+int sched_boost_handler(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ int ret;
+ unsigned int *data = (unsigned int *)table->data;
+ unsigned int old_val;
+
+ if (!sched_enable_hmp)
+ return -EINVAL;
+
+ mutex_lock(&boost_mutex);
+
+ old_val = *data;
+ ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+
+ if (ret || !write)
+ goto done;
+
+ if (verify_boost_params(old_val, *data)) {
+ _sched_set_boost(old_val, *data);
+ } else {
+ *data = old_val;
+ ret = -EINVAL;
+ }
+
+done:
+ mutex_unlock(&boost_mutex);
+ return ret;
+}
+
+int sched_boost(void)
+{
+ return sysctl_sched_boost;
+}