Files
2018-03-13 20:29:02 +01:00

367 lines
9.5 KiB
C

#ifdef CONFIG_MT_LOAD_BALANCE_PROFILER
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <mtlbprof/mtlbprof.h>
#include <linux/version.h>
#define MT_LBPROF_VERSION 4
/*
* Ease the printing of nsec fields:
*/
static long long nsec_high(unsigned long long nsec)
{
if ((long long)nsec < 0) {
nsec = -nsec;
do_div(nsec, 1000000000);
return -nsec;
}
do_div(nsec, 1000000000);
return nsec;
}
static unsigned long nsec_low(unsigned long long nsec)
{
if ((long long)nsec < 0)
nsec = -nsec;
return do_div(nsec, 1000000000);
}
#define SPLIT_NS(x) nsec_high(x), nsec_low(x)
/* ---------------------------------------------------------- */
#define SPLIT_PERCENT(x) ((x)/100), ((x)%100)
/* ---------------------------------------------------------- */
void mt_lbprof_rqinfo(char *strings)
{
char msg2[5];
int i;
for_each_possible_cpu(i) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
snprintf(msg2, 4, "%lu:", cpu_rq(i)->nr_running);
#else
snprintf(msg2, 4, "%u:", cpu_rq(i)->nr_running);
#endif
strcat(strings, msg2);
}
}
/* ---------------------------------------------------------- */
/* http://git.kernel.org/?p=linux/kernel/git/stable/linux-stable.git;a=commitdiff;h=6beea0cda8ce71c01354e688e5735c47e331e84f */
static unsigned long long mtprof_get_cpu_idle(int cpu)
{
unsigned long long *unused = 0, wall;
unsigned long long idle_time = get_cpu_idle_time_us(cpu, unused);
idle_time += get_cpu_iowait_time_us(cpu, &wall);
return idle_time;
}
/* ---------------------------------------------------------- */
DEFINE_SPINLOCK(mt_lbprof_check_cpu_idle_spinlock);
EXPORT_SYMBOL(mt_lbprof_check_cpu_idle_spinlock);
static int mt_lbprof_start;
unsigned int start_output;
unsigned long long last_update;
unsigned long long start;
static DEFINE_PER_CPU(unsigned long long, start_idle_time);
static DEFINE_PER_CPU(int, lb_state);
static unsigned long time_slice_ns[2][3] = { {0, 0, 0}, {0, 0, 0} };
static unsigned long unbalance_slice_ns;
static unsigned long long period_time;
/* --------------------------------------------------------- */
int mt_lbprof_enable(void)
{
int i, j;
int cpu;
unsigned long irq_flags;
char strings[128] = "";
spin_lock_irqsave(&mt_lbprof_check_cpu_idle_spinlock, irq_flags);
mt_lbprof_start = 1;
start_output = 0;
last_update = local_clock();
start = last_update;
for_each_present_cpu(cpu) {
per_cpu(start_idle_time, cpu) = mtprof_get_cpu_idle(cpu);
per_cpu(lb_state, cpu) = 0;
}
for (i = 0; i < 2; i++) {
for (j = 0; j < 3; j++) {
time_slice_ns[i][j] = 0;
}
}
unbalance_slice_ns = 0;
period_time = 0;
spin_unlock_irqrestore(&mt_lbprof_check_cpu_idle_spinlock, irq_flags);
snprintf(strings, 128, "enable mtk load balance profiler");
trace_sched_lbprof_update(strings);
return 0;
}
late_initcall(mt_lbprof_enable);
int mt_lbprof_disable(void)
{
unsigned long irq_flags;
char strings[128] = "";
spin_lock_irqsave(&mt_lbprof_check_cpu_idle_spinlock, irq_flags);
mt_lbprof_start = 0;
spin_unlock_irqrestore(&mt_lbprof_check_cpu_idle_spinlock, irq_flags);
snprintf(strings, 128, "disable mtk load balance profiler");
trace_sched_lbprof_update(strings);
return 0;
}
/* ---------------------------------------------------------- */
#ifndef arch_idle_time
#define arch_idle_time(cpu) 0
#endif
void mt_lbprof_update_state_has_lock(int cpu, int rq_cnt)
{
int unbalance = 0;
unsigned long long now, delta;
char pre_state[40] = "", post_state[40] = "", tmp_state[5] = "";
int state[2];
int cpu1, cpu2, tmp_cpu;
struct cpumask cpu_mask;
unsigned long irq_flags;
if (!mt_lbprof_start)
return;
spin_lock_irqsave(&mt_lbprof_check_cpu_idle_spinlock, irq_flags);
if ((MT_LBPROF_BALANCE_FAIL_STATE == rq_cnt) &&
(per_cpu(lb_state, cpu) == MT_LBPROF_HOTPLUG_STATE)) {
spin_unlock_irqrestore(&mt_lbprof_check_cpu_idle_spinlock, irq_flags);
return;
}
if (per_cpu(lb_state, cpu) == rq_cnt) {
spin_unlock_irqrestore(&mt_lbprof_check_cpu_idle_spinlock, irq_flags);
return;
}
now = local_clock();
delta = now - start;
if (delta > 0) {
cpumask_copy(&cpu_mask, cpu_possible_mask);
for_each_cpu(cpu1, cpu_possible_mask) {
cpumask_clear_cpu(cpu1, &cpu_mask);
state[0] = per_cpu(lb_state, cpu1);
if (state[0] < 5) {
for_each_cpu(cpu2, &cpu_mask) {
state[1] = per_cpu(lb_state, cpu2);
if (state[0] < 2 && (state[1] < 5 && state[1] >= 2)) {
unbalance = 1;
time_slice_ns[state[0]][state[1] - 2] += delta;
} else if (state[1] < 2 && state[0] >= 2) {
unbalance = 1;
time_slice_ns[state[1]][state[0] - 2] += delta;
}
}
}
}
if (unbalance)
unbalance_slice_ns += delta;
period_time += delta;
}
for_each_cpu(tmp_cpu, cpu_possible_mask) {
snprintf(tmp_state, 5, "%d ", per_cpu(lb_state, tmp_cpu));
strncat(pre_state, tmp_state, 3);
if (tmp_cpu == cpu && rq_cnt != MT_LBPROF_UPDATE_STATE) {
if ((per_cpu(lb_state, cpu) == MT_LBPROF_ALLOW_UNBLANCE_STATE) &&
((rq_cnt == MT_LBPROF_IDLE_STATE)
|| (rq_cnt == MT_LBPROF_NO_TASK_STATE))) {
continue;
} else {
per_cpu(lb_state, tmp_cpu) = rq_cnt;
}
continue;
}
if ((tmp_cpu != cpu)
&& (per_cpu(lb_state, tmp_cpu) == MT_LBPROF_ALLOW_UNBLANCE_STATE))
continue;
if (0 == cpu_online(tmp_cpu)) {
per_cpu(lb_state, tmp_cpu) = MT_LBPROF_HOTPLUG_STATE;
}
switch (cpu_rq(tmp_cpu)->nr_running) {
case 1:
per_cpu(lb_state, tmp_cpu) = MT_LBPROF_ONE_TASK_STATE;
break;
case 0:
if ((per_cpu(lb_state, tmp_cpu) == MT_LBPROF_HOTPLUG_STATE)
|| (per_cpu(lb_state, tmp_cpu) == MT_LBPROF_IDLE_STATE)
|| (per_cpu(lb_state, tmp_cpu) == MT_LBPROF_NO_TASK_STATE)) {
break;
}
per_cpu(lb_state, tmp_cpu) = MT_LBPROF_NO_TASK_STATE;
break;
default:
if ((per_cpu(lb_state, tmp_cpu) == MT_LBPROF_N_TASK_STATE)
|| (per_cpu(lb_state, tmp_cpu) == MT_LBPROF_AFFINITY_STATE)
|| (per_cpu(lb_state, tmp_cpu) == MT_LBPROF_FAILURE_STATE)
|| (per_cpu(lb_state, tmp_cpu) == MT_LBPROF_BALANCE_FAIL_STATE)
|| (per_cpu(lb_state, tmp_cpu) == MT_LBPROF_ALLPINNED)) {
break;
}
per_cpu(lb_state, tmp_cpu) = MT_LBPROF_N_TASK_STATE;
}
}
if (delta >= 1000000 && unbalance) {
char strings[128] = "";
if (0 == start_output) {
snprintf(strings, 128, "%llu.%06lu 0 %s", SPLIT_NS(start), pre_state);
trace_sched_lbprof_update(strings);
}
for_each_cpu(tmp_cpu, cpu_possible_mask) {
snprintf(tmp_state, 5, "%d ", per_cpu(lb_state, tmp_cpu));
strncat(post_state, tmp_state, 3);
}
snprintf(strings, 128, "%llu.%06lu %llu %s%lu %d %d ", SPLIT_NS(now), delta,
post_state, unbalance_slice_ns, cpu, rq_cnt);
mt_lbprof_rqinfo(strings);
trace_sched_lbprof_update(strings);
start_output = 1;
} else {
start_output = 0;
}
start = now;
spin_unlock_irqrestore(&mt_lbprof_check_cpu_idle_spinlock, irq_flags);
return;
}
void mt_lbprof_update_state(int cpu, int rq_cnt)
{
struct rq *rq;
unsigned long irq_flags;
rq = cpu_rq(cpu);
raw_spin_lock_irqsave(&rq->lock, irq_flags);
mt_lbprof_update_state_has_lock(cpu, rq_cnt);
raw_spin_unlock_irqrestore(&rq->lock, irq_flags);
}
#define TRIMz(x) ((tz = (unsigned long long)(x)) < 0 ? 0 : tz)
void mt_lbprof_update_status(void)
{
int cpu, i, j;
unsigned long long now, delta;
unsigned long irq_flags;
unsigned long long end_idle_time = 0;
unsigned long lb_idle_time = 0;
unsigned long cpu_load, period_time_32;
char cpu_load_info[80] = "", cpu_load_info_tmp[8];
if (!mt_lbprof_start)
return;
spin_lock_irqsave(&mt_lbprof_check_cpu_idle_spinlock, irq_flags);
now = local_clock();
delta = now - last_update;
if (delta < 1000000000) {
spin_unlock_irqrestore(&mt_lbprof_check_cpu_idle_spinlock, irq_flags);
return;
}
last_update = now;
spin_unlock_irqrestore(&mt_lbprof_check_cpu_idle_spinlock, irq_flags);
cpu = smp_processor_id();
mt_lbprof_update_state(cpu, MT_LBPROF_UPDATE_STATE);
spin_lock_irqsave(&mt_lbprof_check_cpu_idle_spinlock, irq_flags);
do_div(period_time, 1000);
period_time_32 = period_time;
for_each_present_cpu(cpu) {
end_idle_time = mtprof_get_cpu_idle(cpu);
lb_idle_time = end_idle_time - per_cpu(start_idle_time, cpu);
per_cpu(start_idle_time, cpu) = end_idle_time;
cpu_load = 10000 - lb_idle_time * 2500 / (period_time_32 / 4);
snprintf(cpu_load_info_tmp, 8, "%3lu.%02lu ", SPLIT_PERCENT(cpu_load));
strcat(cpu_load_info, cpu_load_info_tmp);
}
period_time_32 /= 10;
{
char strings[128] = "";
snprintf(strings, 128,
"%3lu.%02lu %s%3lu.%02lu %3lu.%02lu %3lu.%02lu %3lu.%02lu %3lu.%02lu %3lu.%02lu %3lu.%02lu %lu ",
SPLIT_PERCENT(10000 - (unbalance_slice_ns) / period_time_32),
cpu_load_info, SPLIT_PERCENT(unbalance_slice_ns / period_time_32),
SPLIT_PERCENT(time_slice_ns[0][0] / period_time_32),
SPLIT_PERCENT(time_slice_ns[0][1] / period_time_32),
SPLIT_PERCENT(time_slice_ns[0][2] / period_time_32),
SPLIT_PERCENT(time_slice_ns[1][0] / period_time_32),
SPLIT_PERCENT(time_slice_ns[1][1] / period_time_32),
SPLIT_PERCENT(time_slice_ns[1][2] / period_time_32), period_time_32);
trace_sched_lbprof_status(strings);
}
for (i = 0; i < 2; i++) {
for (j = 0; j < 3; j++) {
time_slice_ns[i][j] = 0;
}
}
unbalance_slice_ns = 0;
period_time = 0;
spin_unlock_irqrestore(&mt_lbprof_check_cpu_idle_spinlock, irq_flags);
}
#else /* CONFIG_MT_LOAD_BALANCE_PROFILER */
int mt_lbprof_enable(void)
{
return 0;
}
int mt_lbprof_disable(void)
{
return 0;
}
void mt_lbprof_update_status(void)
{
}
void mt_lbprof_update_state(int cpu, int rq_cnt)
{
}
void mt_lbprof_update_state_has_lock(int cpu, int rq_cnt)
{
}
#endif /* CONFIG_MT_LOAD_BALANCE_PROFILER */