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>
This commit is contained in:
parent
5c8690eb75
commit
86d4e1554f
1 changed files with 238 additions and 73 deletions
|
@ -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, ¶m_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, ¶m_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, ¶m_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;
|
||||
|
||||
return cpulist_scnprintf(buf, PAGE_SIZE, &tmp_mask);
|
||||
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);
|
||||
|
||||
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, ¶m_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, ¶m_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_cluster_control(void)
|
||||
{
|
||||
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(&evaluate_hotplug_work, check_cluster_status);
|
||||
mutex_init(&managed_cpus_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init msm_performance_init(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
INIT_DELAYED_WORK(&try_hotplug_work, try_hotplug);
|
||||
mutex_init(&managed_cpus_lock);
|
||||
cpumask_clear(&managed_offline_cpus);
|
||||
unsigned int cpu;
|
||||
|
||||
cpufreq_register_notifier(&perf_cpufreq_nb, CPUFREQ_POLICY_NOTIFIER);
|
||||
for_each_present_cpu(cpu)
|
||||
|
|
Loading…
Add table
Reference in a new issue