blob: a66a80b35a84426ee116e0c37fc7615b8798f632 [file] [log] [blame]
#ifdef OPLUS_FEATURE_TASK_CPUSTATS
#include <linux/kernel_stat.h>
#include <linux/cpufreq.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/sched/topology.h>
#include <linux/sched/energy.h>
#include "../../drivers/misc/mediatek/base/power/include/mtk_upower.h"
/* FIXME get max_pid on the runtime.*/
#define MAX_PID (32768)
#define CTP_WINDOW_SZ (5)
extern unsigned int sysctl_task_cpustats_enable;
struct acct_cpustat {
pid_t tgid;
unsigned int pwr;
char comm[TASK_COMM_LEN];
};
static struct acct_cpustat cpustats[MAX_PID];
#ifdef CONFIG_MTK_UNIFY_POWER
int get_power(int cpu, unsigned long cap) {
int i;
unsigned long pwr,dyn_pwr,stc_pwr;
const struct sched_group_energy *sge = cpu_core_energy(cpu);
if (!sge)
goto err_found;
for (i = sge->nr_cap_states - 1; i > -1; i--) {
struct upower_tbl_row* cap_states = sge->cap_states + i;
if (cap_states->cap == cap){
dyn_pwr = cap_states->dyn_pwr;
stc_pwr = cap_states->lkg_pwr[sge->lkg_idx];
pwr = dyn_pwr + stc_pwr;
return pwr;
}
}
err_found:
pr_err("not found %d %d in sge.\n", cpu);
return 0;
}
#else
int get_power(int cpu, int freq) {
int i;
struct sched_group_energy *sge = sge_array[cpu][SD_LEVEL0];
if (!sge)
goto err_found;
for (i = sge->nr_cap_states - 1; i > -1; i--) {
struct capacity_state* cs = sge->cap_states + i;
if (cs->frequency == freq)
return cs->power;
}
err_found:
pr_err("not found %d %d in sge.\n", cpu, freq);
return 0;
}
#endif
EXPORT_SYMBOL(get_power);
static int task_cpustats_show(struct seq_file *m, void *v)
{
int *idx = (int *) v;
struct acct_cpustat *s = cpustats + *idx;
seq_printf(m, "%d\t%d\t%d\t%s\n", *idx, s->tgid,
s->pwr, s->comm);
return 0;
}
static void *task_cpustats_next(struct seq_file *m, void *v, loff_t *ppos)
{
int *idx = (int *)v;
(*idx)++;
(*ppos)++;
for (; *idx < MAX_PID; (*idx)++, (*ppos)++) {
struct acct_cpustat *s = cpustats + *idx;
if (s->pwr)
return idx;
}
return NULL;
}
static void *task_cpustats_start(struct seq_file *m, loff_t *ppos)
{
int i, j;
unsigned long begin = jiffies - CTP_WINDOW_SZ * HZ, end = jiffies;
int *idx= m->private;
if (!sysctl_task_cpustats_enable)
return NULL;
*idx = *ppos;
if (*idx >= MAX_PID)
goto start_error;
memset(cpustats, 0, sizeof(cpustats));
for_each_possible_cpu(i) {
struct kernel_task_cpustat* kstat = &per_cpu(ktask_cpustat, i);
for (j = 0; j < MAX_CTP_WINDOW; j++) {
struct task_cpustat *ts = kstat->cpustat + j;
unsigned long r_time = ts->end - ts->begin;
if (ts->pid >= MAX_PID)
continue;
if (ts->begin >= begin && ts->end <= end) {
struct acct_cpustat *as = cpustats + ts->pid;
if (as->pwr == 0)
memcpy(as->comm, ts->comm, TASK_COMM_LEN);
/* 4 ms each tick */
#ifdef CONFIG_MTK_UNIFY_POWER
as->pwr += get_power(i, ts->cap) * jiffies_to_msecs(r_time);
#else
as->pwr += get_power(i, ts->freq) * jiffies_to_msecs(r_time);
#endif
as->tgid = ts->tgid;
}
}
}
for (; *idx < MAX_PID; (*idx)++, (*ppos)++) {
struct acct_cpustat *as = cpustats + *idx;
if (as->pwr)
return idx;
}
start_error:
return NULL;
}
static void task_cpustats_stop(struct seq_file *m, void *v)
{
}
static const struct seq_operations seq_ops = {
.start = task_cpustats_start,
.next = task_cpustats_next,
.stop = task_cpustats_stop,
.show = task_cpustats_show
};
static int sge_show(struct seq_file *m, void *v)
{
#ifdef CONFIG_MTK_UNIFY_POWER
unsigned long dyn_pwr;
unsigned long stc_pwr;
unsigned long pwr;
#else
unsigned long pwr;
unsigned long freq;
#endif
const struct sched_group_energy *core_eng;
int cpu;
int core_nr_cap = 0;
unsigned long cap_idx;
int i;
for_each_possible_cpu(cpu) {
core_eng = cpu_core_energy(cpu);
core_nr_cap = core_eng->nr_cap_states;
seq_printf(m, "cpu %d\n", cpu);
for (i = 0; i < core_nr_cap; i++) {
#ifdef CONFIG_MTK_UNIFY_POWER
dyn_pwr =
core_eng->cap_states[i].dyn_pwr;
stc_pwr =
core_eng->cap_states[i].lkg_pwr[core_eng->lkg_idx];
pwr = dyn_pwr + stc_pwr;
cap_idx =
core_eng->cap_states[i].cap;
seq_printf(m, "cap %lu pwr %lu \n", cap_idx, pwr);
#else
pwr = core_eng->cap_states[i].power;
seq_printf(m, "freq: %lu pwr: %lu\n", freq, pwr);
#endif
}
}
return 0;
}
static int sge_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, sge_show, NULL);
}
static int task_cpustats_open(struct inode *inode, struct file *file)
{
int *offs = __seq_open_private(file, &seq_ops, sizeof(int));
if (!offs)
return -ENOMEM;
return 0;
}
static int task_cpustats_release(struct inode *inode, struct file *file)
{
return seq_release_private(inode, file);
}
static const struct file_operations sge_proc_fops = {
.open = sge_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations task_cpustats_proc_fops = {
.open = task_cpustats_open,
.read = seq_read,
.llseek = seq_lseek,
.release = task_cpustats_release,
};
static int __init proc_task_cpustat_init(void)
{
proc_create("sgeinfo", 0, NULL, &sge_proc_fops);
proc_create("task_cpustats", 0, NULL, &task_cpustats_proc_fops);
return 0;
}
fs_initcall(proc_task_cpustat_init);
#endif