mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 12:25:00 -04:00
Merge "soc: qcom: msm_perf: Add msm_performance module"
This commit is contained in:
commit
e447076b15
3 changed files with 521 additions and 0 deletions
|
|
@ -483,6 +483,14 @@ config QCOM_GLINK_PKT
|
|||
This enable the userspace clients to read and write to
|
||||
some glink packets channel.
|
||||
|
||||
config MSM_PERFORMANCE
|
||||
tristate "msm performance driver to support userspace fmin/fmax request"
|
||||
help
|
||||
This driver can restrict max freq or min freq of cpu cluster
|
||||
when requested by the userspace by changing the cpufreq policy
|
||||
fmin and fmax. The user space can request the cpu freq change by
|
||||
writing cpu#:freq values
|
||||
|
||||
config QMP_DEBUGFS_CLIENT
|
||||
bool "Debugfs Client to communicate with AOP using QMP protocol"
|
||||
depends on DEBUG_FS
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ obj-$(CONFIG_QTI_SYSTEM_PM) += system_pm.o
|
|||
obj-$(CONFIG_MSM_REMOTEQDSS) += remoteqdss.o
|
||||
obj-$(CONFIG_MSM_SPSS_UTILS) += spss_utils.o
|
||||
obj-$(CONFIG_MSM_IDLE_STATS) += lpm-stats.o
|
||||
obj-$(CONFIG_MSM_PERFORMANCE) += msm_performance.o
|
||||
obj-$(CONFIG_QTI_RPM_STATS_LOG) += rpmh_master_stat.o
|
||||
obj-$(CONFIG_QPNP_PBS) += qpnp-pbs.o
|
||||
obj-$(CONFIG_QCOM_WATCHDOG) += qcom_watchdog.o
|
||||
|
|
|
|||
512
drivers/soc/qcom/msm_performance.c
Normal file
512
drivers/soc/qcom/msm_performance.c
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/tick.h>
|
||||
#include <trace/events/power.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/sched/core_ctl.h>
|
||||
|
||||
/*
|
||||
* Sched will provide the data for every 20ms window,
|
||||
* will collect the data for 15 windows(300ms) and then update
|
||||
* sysfs nodes with aggregated data
|
||||
*/
|
||||
#define POLL_INT 25
|
||||
#define NODE_NAME_MAX_CHARS 16
|
||||
|
||||
enum cpu_clusters {
|
||||
MIN = 0,
|
||||
MID = 1,
|
||||
MAX = 2,
|
||||
CLUSTER_MAX
|
||||
};
|
||||
|
||||
/* To handle cpufreq min/max request */
|
||||
struct cpu_status {
|
||||
unsigned int min;
|
||||
unsigned int max;
|
||||
};
|
||||
static DEFINE_PER_CPU(struct cpu_status, msm_perf_cpu_stats);
|
||||
static DEFINE_PER_CPU(struct freq_qos_request, qos_req_min);
|
||||
static DEFINE_PER_CPU(struct freq_qos_request, qos_req_max);
|
||||
|
||||
struct events {
|
||||
spinlock_t cpu_hotplug_lock;
|
||||
bool cpu_hotplug;
|
||||
bool init_success;
|
||||
};
|
||||
static struct events events_group;
|
||||
static struct task_struct *events_notify_thread;
|
||||
|
||||
static unsigned int aggr_big_nr;
|
||||
static unsigned int aggr_top_load;
|
||||
static unsigned int top_load[CLUSTER_MAX];
|
||||
static unsigned int curr_cap[CLUSTER_MAX];
|
||||
|
||||
/*******************************sysfs start************************************/
|
||||
static int set_cpu_min_freq(const char *buf, const struct kernel_param *kp)
|
||||
{
|
||||
int i, j, ntokens = 0;
|
||||
unsigned int val, cpu;
|
||||
const char *cp = buf;
|
||||
struct cpu_status *i_cpu_stats;
|
||||
struct cpufreq_policy policy;
|
||||
struct freq_qos_request *req;
|
||||
cpumask_var_t limit_mask;
|
||||
|
||||
while ((cp = strpbrk(cp + 1, " :")))
|
||||
ntokens++;
|
||||
|
||||
/* CPU:value pair */
|
||||
if (!(ntokens % 2))
|
||||
return -EINVAL;
|
||||
|
||||
cp = buf;
|
||||
cpumask_clear(limit_mask);
|
||||
for (i = 0; i < ntokens; i += 2) {
|
||||
if (sscanf(cp, "%u:%u", &cpu, &val) != 2)
|
||||
return -EINVAL;
|
||||
if (cpu > (num_present_cpus() - 1))
|
||||
return -EINVAL;
|
||||
|
||||
i_cpu_stats = &per_cpu(msm_perf_cpu_stats, cpu);
|
||||
|
||||
i_cpu_stats->min = val;
|
||||
cpumask_set_cpu(cpu, limit_mask);
|
||||
|
||||
cp = strnchr(cp, strlen(cp), ' ');
|
||||
cp++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since on synchronous systems policy is shared amongst multiple
|
||||
* CPUs only one CPU needs to be updated for the limit to be
|
||||
* reflected for the entire cluster. We can avoid updating the policy
|
||||
* of other CPUs in the cluster once it is done for at least one CPU
|
||||
* in the cluster
|
||||
*/
|
||||
get_online_cpus();
|
||||
for_each_cpu(i, limit_mask) {
|
||||
i_cpu_stats = &per_cpu(msm_perf_cpu_stats, i);
|
||||
|
||||
if (cpufreq_get_policy(&policy, i))
|
||||
continue;
|
||||
|
||||
if (cpu_online(i) && (policy.min != i_cpu_stats->min)) {
|
||||
req = &per_cpu(qos_req_min, cpu);
|
||||
if (freq_qos_update_request(req, i_cpu_stats->min) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
for_each_cpu(j, policy.related_cpus)
|
||||
cpumask_clear_cpu(j, limit_mask);
|
||||
}
|
||||
put_online_cpus();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_cpu_min_freq(char *buf, const struct kernel_param *kp)
|
||||
{
|
||||
int cnt = 0, cpu;
|
||||
|
||||
for_each_present_cpu(cpu) {
|
||||
cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt,
|
||||
"%d:%u ", cpu,
|
||||
per_cpu(msm_perf_cpu_stats, cpu).min);
|
||||
}
|
||||
cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt, "\n");
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops param_ops_cpu_min_freq = {
|
||||
.set = set_cpu_min_freq,
|
||||
.get = get_cpu_min_freq,
|
||||
};
|
||||
module_param_cb(cpu_min_freq, ¶m_ops_cpu_min_freq, NULL, 0644);
|
||||
|
||||
static int set_cpu_max_freq(const char *buf, const struct kernel_param *kp)
|
||||
{
|
||||
int i, j, ntokens = 0;
|
||||
unsigned int val, cpu;
|
||||
const char *cp = buf;
|
||||
struct cpu_status *i_cpu_stats;
|
||||
struct cpufreq_policy policy;
|
||||
struct freq_qos_request *req;
|
||||
cpumask_var_t limit_mask;
|
||||
|
||||
while ((cp = strpbrk(cp + 1, " :")))
|
||||
ntokens++;
|
||||
|
||||
/* CPU:value pair */
|
||||
if (!(ntokens % 2))
|
||||
return -EINVAL;
|
||||
|
||||
cp = buf;
|
||||
cpumask_clear(limit_mask);
|
||||
for (i = 0; i < ntokens; i += 2) {
|
||||
if (sscanf(cp, "%u:%u", &cpu, &val) != 2)
|
||||
return -EINVAL;
|
||||
if (cpu > (num_present_cpus() - 1))
|
||||
return -EINVAL;
|
||||
|
||||
i_cpu_stats = &per_cpu(msm_perf_cpu_stats, cpu);
|
||||
|
||||
i_cpu_stats->max = val;
|
||||
cpumask_set_cpu(cpu, limit_mask);
|
||||
|
||||
cp = strnchr(cp, strlen(cp), ' ');
|
||||
cp++;
|
||||
}
|
||||
|
||||
get_online_cpus();
|
||||
for_each_cpu(i, limit_mask) {
|
||||
i_cpu_stats = &per_cpu(msm_perf_cpu_stats, i);
|
||||
if (cpufreq_get_policy(&policy, i))
|
||||
continue;
|
||||
|
||||
if (cpu_online(i) && (policy.max != i_cpu_stats->max)) {
|
||||
req = &per_cpu(qos_req_max, cpu);
|
||||
if (freq_qos_update_request(req, i_cpu_stats->max) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
for_each_cpu(j, policy.related_cpus)
|
||||
cpumask_clear_cpu(j, limit_mask);
|
||||
}
|
||||
put_online_cpus();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_cpu_max_freq(char *buf, const struct kernel_param *kp)
|
||||
{
|
||||
int cnt = 0, cpu;
|
||||
|
||||
for_each_present_cpu(cpu) {
|
||||
cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt,
|
||||
"%d:%u ", cpu,
|
||||
per_cpu(msm_perf_cpu_stats, cpu).max);
|
||||
}
|
||||
cnt += scnprintf(buf + cnt, PAGE_SIZE - cnt, "\n");
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops param_ops_cpu_max_freq = {
|
||||
.set = set_cpu_max_freq,
|
||||
.get = get_cpu_max_freq,
|
||||
};
|
||||
module_param_cb(cpu_max_freq, ¶m_ops_cpu_max_freq, NULL, 0644);
|
||||
|
||||
static struct kobject *events_kobj;
|
||||
|
||||
static ssize_t show_cpu_hotplug(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
return scnprintf(buf, PAGE_SIZE, "\n");
|
||||
}
|
||||
static struct kobj_attribute cpu_hotplug_attr =
|
||||
__ATTR(cpu_hotplug, 0444, show_cpu_hotplug, NULL);
|
||||
|
||||
static struct attribute *events_attrs[] = {
|
||||
&cpu_hotplug_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group events_attr_group = {
|
||||
.attrs = events_attrs,
|
||||
};
|
||||
|
||||
static ssize_t show_big_nr(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", aggr_big_nr);
|
||||
}
|
||||
|
||||
static struct kobj_attribute big_nr_attr =
|
||||
__ATTR(aggr_big_nr, 0444, show_big_nr, NULL);
|
||||
|
||||
static ssize_t show_top_load(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", aggr_top_load);
|
||||
}
|
||||
|
||||
static struct kobj_attribute top_load_attr =
|
||||
__ATTR(aggr_top_load, 0444, show_top_load, NULL);
|
||||
|
||||
|
||||
static ssize_t show_top_load_cluster(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return scnprintf(buf, PAGE_SIZE, "%u %u %u\n",
|
||||
top_load[MIN], top_load[MID],
|
||||
top_load[MAX]);
|
||||
}
|
||||
|
||||
static struct kobj_attribute cluster_top_load_attr =
|
||||
__ATTR(top_load_cluster, 0444, show_top_load_cluster, NULL);
|
||||
|
||||
static ssize_t show_curr_cap_cluster(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return scnprintf(buf, PAGE_SIZE, "%u %u %u\n",
|
||||
curr_cap[MIN], curr_cap[MID],
|
||||
curr_cap[MAX]);
|
||||
}
|
||||
|
||||
static struct kobj_attribute cluster_curr_cap_attr =
|
||||
__ATTR(curr_cap_cluster, 0444, show_curr_cap_cluster, NULL);
|
||||
|
||||
static struct attribute *notify_attrs[] = {
|
||||
&big_nr_attr.attr,
|
||||
&top_load_attr.attr,
|
||||
&cluster_top_load_attr.attr,
|
||||
&cluster_curr_cap_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group notify_attr_group = {
|
||||
.attrs = notify_attrs,
|
||||
};
|
||||
static struct kobject *notify_kobj;
|
||||
|
||||
/*******************************sysfs ends************************************/
|
||||
static int hotplug_notify(unsigned int cpu)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (events_group.init_success) {
|
||||
spin_lock_irqsave(&(events_group.cpu_hotplug_lock), flags);
|
||||
events_group.cpu_hotplug = true;
|
||||
spin_unlock_irqrestore(&(events_group.cpu_hotplug_lock), flags);
|
||||
wake_up_process(events_notify_thread);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int events_notify_userspace(void *data)
|
||||
{
|
||||
unsigned long flags;
|
||||
bool notify_change;
|
||||
|
||||
while (1) {
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
spin_lock_irqsave(&(events_group.cpu_hotplug_lock), flags);
|
||||
|
||||
if (!events_group.cpu_hotplug) {
|
||||
spin_unlock_irqrestore(&(events_group.cpu_hotplug_lock),
|
||||
flags);
|
||||
|
||||
schedule();
|
||||
if (kthread_should_stop())
|
||||
break;
|
||||
spin_lock_irqsave(&(events_group.cpu_hotplug_lock),
|
||||
flags);
|
||||
}
|
||||
|
||||
set_current_state(TASK_RUNNING);
|
||||
notify_change = events_group.cpu_hotplug;
|
||||
events_group.cpu_hotplug = false;
|
||||
spin_unlock_irqrestore(&(events_group.cpu_hotplug_lock), flags);
|
||||
|
||||
if (notify_change)
|
||||
sysfs_notify(events_kobj, NULL, "cpu_hotplug");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_notify_group(void)
|
||||
{
|
||||
int ret;
|
||||
struct kobject *module_kobj;
|
||||
|
||||
module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
|
||||
if (!module_kobj) {
|
||||
pr_err("msm_perf: Couldn't find module kobject\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
notify_kobj = kobject_create_and_add("notify", module_kobj);
|
||||
if (!notify_kobj) {
|
||||
pr_err("msm_perf: Failed to add notify_kobj\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(notify_kobj, ¬ify_attr_group);
|
||||
if (ret) {
|
||||
kobject_put(notify_kobj);
|
||||
pr_err("msm_perf: Failed to create sysfs\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_events_group(void)
|
||||
{
|
||||
int ret;
|
||||
struct kobject *module_kobj;
|
||||
|
||||
module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
|
||||
if (!module_kobj) {
|
||||
pr_err("msm_perf: Couldn't find module kobject\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
events_kobj = kobject_create_and_add("events", module_kobj);
|
||||
if (!events_kobj) {
|
||||
pr_err("msm_perf: Failed to add events_kobj\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(events_kobj, &events_attr_group);
|
||||
if (ret) {
|
||||
pr_err("msm_perf: Failed to create sysfs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
spin_lock_init(&(events_group.cpu_hotplug_lock));
|
||||
events_notify_thread = kthread_run(events_notify_userspace,
|
||||
NULL, "msm_perf:events_notify");
|
||||
if (IS_ERR(events_notify_thread))
|
||||
return PTR_ERR(events_notify_thread);
|
||||
|
||||
events_group.init_success = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nr_notify_userspace(struct work_struct *work)
|
||||
{
|
||||
sysfs_notify(notify_kobj, NULL, "aggr_top_load");
|
||||
sysfs_notify(notify_kobj, NULL, "aggr_big_nr");
|
||||
sysfs_notify(notify_kobj, NULL, "top_load_cluster");
|
||||
sysfs_notify(notify_kobj, NULL, "curr_cap_cluster");
|
||||
}
|
||||
|
||||
static int msm_perf_core_ctl_notify(struct notifier_block *nb,
|
||||
unsigned long unused,
|
||||
void *data)
|
||||
{
|
||||
static unsigned int tld, nrb, i;
|
||||
static unsigned int top_ld[CLUSTER_MAX], curr_cp[CLUSTER_MAX];
|
||||
static DECLARE_WORK(sysfs_notify_work, nr_notify_userspace);
|
||||
struct core_ctl_notif_data *d = data;
|
||||
int cluster = 0;
|
||||
|
||||
nrb += d->nr_big;
|
||||
tld += d->coloc_load_pct;
|
||||
for (cluster = 0; cluster < CLUSTER_MAX; cluster++) {
|
||||
top_ld[cluster] += d->ta_util_pct[cluster];
|
||||
curr_cp[cluster] += d->cur_cap_pct[cluster];
|
||||
}
|
||||
i++;
|
||||
if (i == POLL_INT) {
|
||||
aggr_big_nr = ((nrb%POLL_INT) ? 1 : 0) + nrb/POLL_INT;
|
||||
aggr_top_load = tld/POLL_INT;
|
||||
for (cluster = 0; cluster < CLUSTER_MAX; cluster++) {
|
||||
top_load[cluster] = top_ld[cluster]/POLL_INT;
|
||||
curr_cap[cluster] = curr_cp[cluster]/POLL_INT;
|
||||
top_ld[cluster] = 0;
|
||||
curr_cp[cluster] = 0;
|
||||
}
|
||||
tld = 0;
|
||||
nrb = 0;
|
||||
i = 0;
|
||||
schedule_work(&sysfs_notify_work);
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block msm_perf_nb = {
|
||||
.notifier_call = msm_perf_core_ctl_notify
|
||||
};
|
||||
|
||||
static bool core_ctl_register;
|
||||
static int set_core_ctl_register(const char *buf, const struct kernel_param *kp)
|
||||
{
|
||||
int ret;
|
||||
bool old_val = core_ctl_register;
|
||||
|
||||
ret = param_set_bool(buf, kp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (core_ctl_register == old_val)
|
||||
return 0;
|
||||
|
||||
if (core_ctl_register)
|
||||
core_ctl_notifier_register(&msm_perf_nb);
|
||||
else
|
||||
core_ctl_notifier_unregister(&msm_perf_nb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops param_ops_cc_register = {
|
||||
.set = set_core_ctl_register,
|
||||
.get = param_get_bool,
|
||||
};
|
||||
module_param_cb(core_ctl_register, ¶m_ops_cc_register,
|
||||
&core_ctl_register, 0644);
|
||||
|
||||
static int __init msm_performance_init(void)
|
||||
{
|
||||
unsigned int cpu;
|
||||
int ret;
|
||||
|
||||
struct cpufreq_policy *policy;
|
||||
struct freq_qos_request *req;
|
||||
|
||||
for_each_present_cpu(cpu) {
|
||||
per_cpu(msm_perf_cpu_stats, cpu).max = UINT_MAX;
|
||||
req = &per_cpu(qos_req_min, cpu);
|
||||
policy = cpufreq_cpu_get(cpu);
|
||||
ret = freq_qos_add_request(&policy->constraints, req,
|
||||
FREQ_QOS_MIN, policy->min);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Failed to add min freq constraint (%d)\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
req = &per_cpu(qos_req_max, cpu);
|
||||
ret = freq_qos_add_request(&policy->constraints, req,
|
||||
FREQ_QOS_MAX, policy->max);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Failed to add max freq constraint (%d)\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE,
|
||||
"msm_performance_cpu_hotplug",
|
||||
hotplug_notify,
|
||||
NULL);
|
||||
|
||||
init_events_group();
|
||||
init_notify_group();
|
||||
|
||||
return 0;
|
||||
}
|
||||
late_initcall(msm_performance_init);
|
||||
Loading…
Reference in a new issue