1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2024-11-19 13:27:49 +00:00
a155-U-u1/kernel-5.10/drivers/misc/mediatek/perf_common/perf_freq_tracker.c
2024-03-11 06:53:12 +11:00

159 lines
3.7 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 MediaTek Inc.
*/
#include <linux/pm_qos.h>
#include <trace/hooks/power.h>
#include <linux/kallsyms.h>
#include <linux/hashtable.h>
#include <perf_tracker_internal.h>
#include <perf_tracker_trace.h>
struct h_node {
unsigned long addr;
char symbol[KSYM_SYMBOL_LEN];
struct hlist_node node;
};
static DECLARE_HASHTABLE(tbl, 5);
static int is_inited;
static int is_hooked;
static struct freq_constraints *qos_in_cluster[MAX_CLUSTER_NR] = {0};
static const char *find_and_get_symobls(unsigned long caller_addr)
{
struct h_node *cur_node = NULL;
struct h_node *new_node = NULL;
const char *cur_symbol = NULL;
unsigned int cur_key = 0;
cur_key = (unsigned int) caller_addr & 0x1f;
// Try to find symbols from history records
hash_for_each_possible(tbl, cur_node, node, cur_key) {
if (cur_node->addr == caller_addr) {
cur_symbol = cur_node->symbol;
break;
}
}
// Symbols not found. Add new records
if (!cur_symbol) {
new_node = kzalloc(sizeof(struct h_node), GFP_KERNEL);
if (!new_node)
return NULL;
new_node->addr = caller_addr;
sprint_symbol(new_node->symbol, caller_addr);
cur_symbol = new_node->symbol;
hash_add(tbl, &new_node->node, cur_key);
}
return cur_symbol;
}
static inline int find_qos_in_cluster(struct freq_constraints *qos)
{
int cid = 0;
for (cid = 0; cid < cluster_nr; cid++) {
if (qos_in_cluster[cid] == qos)
break;
}
return (cid < cluster_nr) ? cid : -1;
}
static void mtk_freq_qos_add_request(void *data, struct freq_constraints *qos,
struct freq_qos_request *req, enum freq_qos_req_type type, int value, int ret)
{
int cid = 0;
const char *caller_info = find_and_get_symobls(
(unsigned long)__builtin_return_address(1));
if (caller_info) {
cid = find_qos_in_cluster(qos);
trace_freq_qos_user_setting(cid, type, value, caller_info);
}
}
static void mtk_freq_qos_update_request(void *data, struct freq_qos_request *req, int value)
{
int cid = 0;
const char *caller_info = find_and_get_symobls(
(unsigned long)__builtin_return_address(1));
if (caller_info) {
cid = find_qos_in_cluster(req->qos);
trace_freq_qos_user_setting(cid, req->type, value, caller_info);
}
}
int insert_freq_qos_hook(void)
{
int ret = 0;
if (is_hooked || !is_inited)
return ret;
ret = register_trace_android_vh_freq_qos_add_request(mtk_freq_qos_add_request, NULL);
if (ret) {
pr_info("mtk_freq_qos_add_requests: register hooks failed, returned %d\n", ret);
goto register_failed;
}
ret = register_trace_android_vh_freq_qos_update_request(mtk_freq_qos_update_request, NULL);
if (ret) {
pr_info("mtk_freq_qos_update_requests: register hooks failed, returned %d\n", ret);
goto register_failed;
}
is_hooked = 1;
return ret;
register_failed:
remove_freq_qos_hook();
return ret;
}
void remove_freq_qos_hook(void)
{
is_hooked = 0;
unregister_trace_android_vh_freq_qos_add_request(mtk_freq_qos_add_request, NULL);
unregister_trace_android_vh_freq_qos_update_request(mtk_freq_qos_update_request, NULL);
}
static void init_cluster_qos_info(void)
{
struct cpufreq_policy *policy;
int cpu;
int num = 0;
for_each_possible_cpu(cpu) {
if (num >= cluster_nr)
break;
policy = cpufreq_cpu_get(cpu);
if (policy) {
qos_in_cluster[num++] = &(policy->constraints);
cpu = cpumask_last(policy->related_cpus);
cpufreq_cpu_put(policy);
}
}
}
void init_perf_freq_tracker(void)
{
is_hooked = 0;
is_inited = 1;
// Initialize hash table
hash_init(tbl);
init_cluster_qos_info();
}
void exit_perf_freq_tracker(void)
{
int bkt = 0;
struct h_node *cur = NULL;
struct hlist_node *tmp = NULL;
is_inited = 0;
remove_freq_qos_hook();
// Remove hash table
hash_for_each_safe(tbl, bkt, tmp, cur, node) {
hash_del(&cur->node);
kfree(cur);
}
}