summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorRohit Gupta <rohgup@codeaurora.org>2014-11-05 21:15:28 -0800
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 21:22:20 -0700
commit86d4e1554fd8a00875066d302cf141da0eb7dcf7 (patch)
tree31aed63833ff7448d9ad6f457ed8b1ca32f4a5e8 /drivers
parent5c8690eb75f10be6fd8a6b1cfff38eb9043da3bf (diff)
soc: qcom: Manage multipe clusters at a time in msm_perf module
Adding support to manage multiple clusters at a time in msm_performance driver. The number of clusters and the respective masks are initialized once from userspace and then the max_cpus tunable can be used to manage the CPUs to be put online in the cluster thereafter. Change-Id: I35a80af37e1ecedbc53670edd65698170fe349e1 Signed-off-by: Rohit Gupta <rohgup@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/soc/qcom/msm_performance.c309
1 files changed, 237 insertions, 72 deletions
diff --git a/drivers/soc/qcom/msm_performance.c b/drivers/soc/qcom/msm_performance.c
index 56de8c59fb7f..193cc8218bea 100644
--- a/drivers/soc/qcom/msm_performance.c
+++ b/drivers/soc/qcom/msm_performance.c
@@ -17,26 +17,25 @@
#include <linux/moduleparam.h>
#include <linux/cpumask.h>
#include <linux/cpufreq.h>
+#include <linux/slab.h>
#include <trace/events/power.h>
-/* Delay in jiffies for hotplugging to complete */
-#define MIN_HOTPLUG_DELAY 3
-
-/* Number of CPUs to maintain online */
-static unsigned int max_cpus;
-
-/* List of CPUs managed by this module */
-static struct cpumask managed_cpus;
static struct mutex managed_cpus_lock;
-/* To keep track of CPUs that the module decides to offline */
-static struct cpumask managed_offline_cpus;
+/* Maximum number to clusters that this module will manage*/
+static unsigned int num_clusters;
+struct cpu_hp {
+ cpumask_var_t cpus;
+ /* Number of CPUs to maintain online */
+ int max_cpu_request;
+ /* To track CPUs that the module decides to offline */
+ cpumask_var_t offlined_cpus;
+};
+static struct cpu_hp **managed_clusters;
/* Work to evaluate the onlining/offlining CPUs */
-struct delayed_work try_hotplug_work;
-
-static unsigned int num_online_managed(void);
+struct delayed_work evaluate_hotplug_work;
/* To handle cpufreq min/max request */
struct cpu_status {
@@ -45,25 +44,88 @@ struct cpu_status {
};
static DEFINE_PER_CPU(struct cpu_status, cpu_stats);
-static int set_max_cpus(const char *buf, const struct kernel_param *kp)
+static unsigned int num_online_managed(struct cpumask *mask);
+static int init_cluster_control(void);
+
+static int set_num_clusters(const char *buf, const struct kernel_param *kp)
{
unsigned int val;
if (sscanf(buf, "%u\n", &val) != 1)
return -EINVAL;
- if (val > cpumask_weight(&managed_cpus))
+ if (num_clusters)
return -EINVAL;
- max_cpus = val;
- schedule_delayed_work(&try_hotplug_work, 0);
- trace_set_max_cpus(cpumask_bits(&managed_cpus)[0], max_cpus);
+ num_clusters = val;
+
+ if (init_cluster_control()) {
+ num_clusters = 0;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int get_num_clusters(char *buf, const struct kernel_param *kp)
+{
+ return snprintf(buf, PAGE_SIZE, "%u", num_clusters);
+}
+
+static const struct kernel_param_ops param_ops_num_clusters = {
+ .set = set_num_clusters,
+ .get = get_num_clusters,
+};
+device_param_cb(num_clusters, &param_ops_num_clusters, NULL, 0644);
+
+static int set_max_cpus(const char *buf, const struct kernel_param *kp)
+{
+ unsigned int i, ntokens = 0;
+ const char *cp = buf;
+ int val;
+
+ if (!num_clusters)
+ return -EINVAL;
+
+ while ((cp = strpbrk(cp + 1, ":")))
+ ntokens++;
+
+ if (!ntokens)
+ return -EINVAL;
+
+ cp = buf;
+ for (i = 0; i < num_clusters; i++) {
+
+ if (sscanf(cp, "%d\n", &val) != 1)
+ return -EINVAL;
+ if (val > (int)cpumask_weight(managed_clusters[i]->cpus))
+ return -EINVAL;
+
+ managed_clusters[i]->max_cpu_request = val;
+
+ cp = strnchr(cp, strlen(cp), ':');
+ cp++;
+ trace_set_max_cpus(cpumask_bits(managed_clusters[i]->cpus)[0],
+ val);
+ }
+
+ schedule_delayed_work(&evaluate_hotplug_work, 0);
return 0;
}
static int get_max_cpus(char *buf, const struct kernel_param *kp)
{
- return snprintf(buf, PAGE_SIZE, "%u", max_cpus);
+ int i, cnt = 0;
+
+ if (!num_clusters)
+ return cnt;
+
+ for (i = 0; i < num_clusters; i++)
+ cnt += snprintf(buf + cnt, PAGE_SIZE - cnt,
+ "%d:", managed_clusters[i]->max_cpu_request);
+ cnt--;
+ cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, " ");
+ return cnt;
}
static const struct kernel_param_ops param_ops_max_cpus = {
@@ -75,19 +137,46 @@ device_param_cb(max_cpus, &param_ops_max_cpus, NULL, 0644);
static int set_managed_cpus(const char *buf, const struct kernel_param *kp)
{
- int ret;
+ int i, ret;
+ struct cpumask tmp_mask;
- mutex_lock(&managed_cpus_lock);
- ret = cpulist_parse(buf, &managed_cpus);
- cpumask_clear(&managed_offline_cpus);
- mutex_unlock(&managed_cpus_lock);
+ if (!num_clusters)
+ return -EINVAL;
+
+ ret = cpulist_parse(buf, &tmp_mask);
+
+ if (ret)
+ return ret;
+
+ for (i = 0; i < num_clusters; i++) {
+ if (cpumask_empty(managed_clusters[i]->cpus)) {
+ mutex_lock(&managed_cpus_lock);
+ cpumask_copy(managed_clusters[i]->cpus, &tmp_mask);
+ cpumask_clear(managed_clusters[i]->offlined_cpus);
+ mutex_unlock(&managed_cpus_lock);
+ break;
+ }
+ }
return ret;
}
static int get_managed_cpus(char *buf, const struct kernel_param *kp)
{
- return cpulist_scnprintf(buf, PAGE_SIZE, &managed_cpus);
+ int i, cnt = 0;
+
+ if (!num_clusters)
+ return cnt;
+
+ for (i = 0; i < num_clusters; i++) {
+ cnt += cpulist_scnprintf(buf + cnt, PAGE_SIZE - cnt,
+ managed_clusters[i]->cpus);
+ if ((i + 1) >= num_clusters)
+ break;
+ cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, ":");
+ }
+
+ return cnt;
}
static const struct kernel_param_ops param_ops_managed_cpus = {
@@ -96,18 +185,32 @@ static const struct kernel_param_ops param_ops_managed_cpus = {
};
device_param_cb(managed_cpus, &param_ops_managed_cpus, NULL, 0644);
-/* To display all the online managed CPUs */
+/* Read-only node: To display all the online managed CPUs */
static int get_managed_online_cpus(char *buf, const struct kernel_param *kp)
{
+ int i, cnt = 0;
struct cpumask tmp_mask;
+ struct cpu_hp *i_cpu_hp;
- cpumask_clear(&tmp_mask);
- mutex_lock(&managed_cpus_lock);
- cpumask_complement(&tmp_mask, &managed_offline_cpus);
- cpumask_and(&tmp_mask, &managed_cpus, &tmp_mask);
- mutex_unlock(&managed_cpus_lock);
+ if (!num_clusters)
+ return cnt;
+
+ for (i = 0; i < num_clusters; i++) {
+ i_cpu_hp = managed_clusters[i];
+
+ cpumask_clear(&tmp_mask);
+ cpumask_complement(&tmp_mask, i_cpu_hp->offlined_cpus);
+ cpumask_and(&tmp_mask, i_cpu_hp->cpus, &tmp_mask);
+
+ cnt += cpulist_scnprintf(buf + cnt, PAGE_SIZE - cnt,
+ &tmp_mask);
- return cpulist_scnprintf(buf, PAGE_SIZE, &tmp_mask);
+ if ((i + 1) >= num_clusters)
+ break;
+ cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, ":");
+ }
+
+ return cnt;
}
static const struct kernel_param_ops param_ops_managed_online_cpus = {
@@ -116,16 +219,6 @@ static const struct kernel_param_ops param_ops_managed_online_cpus = {
device_param_cb(managed_online_cpus, &param_ops_managed_online_cpus,
NULL, 0444);
-static unsigned int num_online_managed(void)
-{
- struct cpumask tmp_mask;
-
- cpumask_clear(&tmp_mask);
- cpumask_and(&tmp_mask, &managed_cpus, cpu_online_mask);
-
- return cpumask_weight(&tmp_mask);
-}
-
/*
* Userspace sends cpu#:min_freq_value to vote for min_freq_value as the new
* scaling_min. To withdraw its vote it needs to enter cpu#:0
@@ -284,6 +377,16 @@ static const struct kernel_param_ops param_ops_cpu_max_freq = {
};
module_param_cb(cpu_max_freq, &param_ops_cpu_max_freq, NULL, 0644);
+static unsigned int num_online_managed(struct cpumask *mask)
+{
+ struct cpumask tmp_mask;
+
+ cpumask_clear(&tmp_mask);
+ cpumask_and(&tmp_mask, mask, cpu_online_mask);
+
+ return cpumask_weight(&tmp_mask);
+}
+
static int perf_adjust_notify(struct notifier_block *nb, unsigned long val,
void *data)
{
@@ -315,38 +418,35 @@ static struct notifier_block perf_cpufreq_nb = {
/*
* try_hotplug tries to online/offline cores based on the current requirement.
* It loops through the currently managed CPUs and tries to online/offline
- * them until the max_cpus criteria is met.
+ * them until the max_cpu_request criteria is met.
*/
-static void __ref try_hotplug(struct work_struct *work)
+static void __ref try_hotplug(struct cpu_hp *data)
{
unsigned int i;
- if (cpumask_empty(&managed_cpus) || (num_online_managed() == max_cpus))
- return;
-
- pr_debug("msm_perf: Trying hotplug...%d:%d\n", num_online_managed(),
- num_online_cpus());
+ pr_debug("msm_perf: Trying hotplug...%d:%d\n",
+ num_online_managed(data->cpus), num_online_cpus());
mutex_lock(&managed_cpus_lock);
- if (num_online_managed() > max_cpus) {
+ if (num_online_managed(data->cpus) > data->max_cpu_request) {
for (i = num_present_cpus() - 1; i >= 0; i--) {
- if (!cpumask_test_cpu(i, &managed_cpus) ||
- !cpu_online(i))
+ if (!cpumask_test_cpu(i, data->cpus) || !cpu_online(i))
continue;
pr_debug("msm_perf: Offlining CPU%d\n", i);
- cpumask_set_cpu(i, &managed_offline_cpus);
+ cpumask_set_cpu(i, data->offlined_cpus);
if (cpu_down(i)) {
- cpumask_clear_cpu(i, &managed_offline_cpus);
+ cpumask_clear_cpu(i, data->offlined_cpus);
pr_debug("msm_perf: Offlining CPU%d failed\n",
i);
continue;
}
- if (num_online_managed() <= max_cpus)
+ if (num_online_managed(data->cpus) <=
+ data->max_cpu_request)
break;
}
} else {
- for_each_cpu(i, &managed_cpus) {
+ for_each_cpu(i, data->cpus) {
if (cpu_online(i))
continue;
pr_debug("msm_perf: Onlining CPU%d\n", i);
@@ -355,20 +455,65 @@ static void __ref try_hotplug(struct work_struct *work)
i);
continue;
}
- cpumask_clear_cpu(i, &managed_offline_cpus);
- if (num_online_managed() >= max_cpus)
+ cpumask_clear_cpu(i, data->offlined_cpus);
+ if (num_online_managed(data->cpus) >=
+ data->max_cpu_request)
break;
}
}
mutex_unlock(&managed_cpus_lock);
}
+static void __ref release_cluster_control(struct cpumask *off_cpus)
+{
+ int cpu;
+
+ for_each_cpu(cpu, off_cpus) {
+ pr_err("msm_perf: Release CPU %d\n", cpu);
+ if (!cpu_up(cpu))
+ cpumask_clear_cpu(cpu, off_cpus);
+ }
+}
+
+/* Work to evaluate current online CPU status and hotplug CPUs as per need*/
+static void check_cluster_status(struct work_struct *work)
+{
+ int i;
+ struct cpu_hp *i_chp;
+
+ for (i = 0; i < num_clusters; i++) {
+ i_chp = managed_clusters[i];
+
+ if (cpumask_empty(i_chp->cpus))
+ continue;
+
+ if (i_chp->max_cpu_request < 0) {
+ if (!cpumask_empty(i_chp->offlined_cpus))
+ release_cluster_control(i_chp->offlined_cpus);
+ continue;
+ }
+
+ if (num_online_managed(i_chp->cpus) !=
+ i_chp->max_cpu_request)
+ try_hotplug(i_chp);
+ }
+}
+
static int __ref msm_performance_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
uint32_t cpu = (uintptr_t)hcpu;
+ unsigned int i;
+ struct cpu_hp *i_hp = NULL;
- if (!cpumask_test_cpu(cpu, &managed_cpus))
+ for (i = 0; i < num_clusters; i++) {
+ if (cpumask_test_cpu(cpu, managed_clusters[i]->cpus)) {
+ i_hp = managed_clusters[i];
+ break;
+ }
+ }
+
+ if (i_hp == NULL)
return NOTIFY_OK;
if (action == CPU_UP_PREPARE || action == CPU_UP_PREPARE_FROZEN) {
@@ -376,22 +521,24 @@ static int __ref msm_performance_cpu_callback(struct notifier_block *nfb,
* Prevent onlining of a managed CPU if max_cpu criteria is
* already satisfied
*/
- if (max_cpus <= num_online_managed()) {
+ if (i_hp->max_cpu_request <=
+ num_online_managed(i_hp->cpus)) {
pr_debug("msm_perf: Prevent CPU%d onlining\n", cpu);
return NOTIFY_BAD;
}
- cpumask_clear_cpu(cpu, &managed_offline_cpus);
+ cpumask_clear_cpu(cpu, i_hp->offlined_cpus);
- } else if (!cpumask_test_cpu(cpu, &managed_offline_cpus) &&
- (action == CPU_DEAD)) {
+ } else if (action == CPU_DEAD) {
+ if (cpumask_test_cpu(cpu, i_hp->offlined_cpus))
+ return NOTIFY_OK;
/*
* Schedule a re-evaluation to check if any more CPUs can be
- * brought online to meet the max_cpus requirement. This work
- * is delayed to account for CPU hotplug latencies
+ * brought online to meet the max_cpu_request requirement. This
+ * work is delayed to account for CPU hotplug latencies
*/
- if (schedule_delayed_work(&try_hotplug_work, 0)) {
- trace_reevaluate_hotplug(cpumask_bits(&managed_cpus)[0],
- max_cpus);
+ if (schedule_delayed_work(&evaluate_hotplug_work, 0)) {
+ trace_reevaluate_hotplug(cpumask_bits(i_hp->cpus)[0],
+ i_hp->max_cpu_request);
pr_debug("msm_perf: Re-evaluation scheduled %d\n", cpu);
} else {
pr_debug("msm_perf: Work scheduling failed %d\n", cpu);
@@ -405,13 +552,31 @@ static struct notifier_block __refdata msm_performance_cpu_notifier = {
.notifier_call = msm_performance_cpu_callback,
};
-static int __init msm_performance_init(void)
+static int init_cluster_control(void)
{
- int cpu;
+ unsigned int i;
+
+ managed_clusters = kcalloc(num_clusters, sizeof(struct cpu_hp *),
+ GFP_KERNEL);
+ if (!managed_clusters)
+ return -ENOMEM;
+ for (i = 0; i < num_clusters; i++) {
+ managed_clusters[i] = kcalloc(1, sizeof(struct cpu_hp),
+ GFP_KERNEL);
+ if (!managed_clusters[i])
+ return -ENOMEM;
+ managed_clusters[i]->max_cpu_request = -1;
+ }
- INIT_DELAYED_WORK(&try_hotplug_work, try_hotplug);
+ INIT_DELAYED_WORK(&evaluate_hotplug_work, check_cluster_status);
mutex_init(&managed_cpus_lock);
- cpumask_clear(&managed_offline_cpus);
+
+ return 0;
+}
+
+static int __init msm_performance_init(void)
+{
+ unsigned int cpu;
cpufreq_register_notifier(&perf_cpufreq_nb, CPUFREQ_POLICY_NOTIFIER);
for_each_present_cpu(cpu)