summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZiyi Cui <ziyic@google.com>2023-05-01 03:23:31 +0000
committerZiyi Cui <ziyic@google.com>2023-09-16 18:46:46 +0000
commit873b0158b6bb699cb1022cd52660567271a2ab37 (patch)
tree3d2b4cb648965dacfc65917464546f4418b2456e
parent9cc48065d0a4311a8351910c385955639382180e (diff)
downloadgs-873b0158b6bb699cb1022cd52660567271a2ab37.tar.gz
perf_metrics: Add long RT runnable metrics
Bug: 275806676 Change-Id: I2f319baea00f2514983b5f4f8024ba4dbc826889
-rw-r--r--drivers/soc/google/vh/include/sched.h1
-rw-r--r--drivers/soc/google/vh/kernel/metrics/perf_metrics.c309
-rw-r--r--drivers/soc/google/vh/kernel/metrics/perf_metrics.h34
-rw-r--r--drivers/soc/google/vh/kernel/sched/sched_priv.h1
4 files changed, 304 insertions, 41 deletions
diff --git a/drivers/soc/google/vh/include/sched.h b/drivers/soc/google/vh/include/sched.h
index 22e804de9..9af8bd519 100644
--- a/drivers/soc/google/vh/include/sched.h
+++ b/drivers/soc/google/vh/include/sched.h
@@ -72,6 +72,7 @@ struct vendor_task_struct {
struct vendor_binder_task_struct binder_task;
/* parameters for RT inheritance */
unsigned int uclamp_pi[UCLAMP_CNT];
+ u64 runnable_start_ns;
};
ANDROID_VENDOR_CHECK_SIZE_ALIGN(u64 android_vendor_data1[64], struct vendor_task_struct t);
diff --git a/drivers/soc/google/vh/kernel/metrics/perf_metrics.c b/drivers/soc/google/vh/kernel/metrics/perf_metrics.c
index 78851f324..f50e81291 100644
--- a/drivers/soc/google/vh/kernel/metrics/perf_metrics.c
+++ b/drivers/soc/google/vh/kernel/metrics/perf_metrics.c
@@ -21,17 +21,151 @@
#include <trace/events/power.h>
#include <trace/events/irq.h>
#include <trace/hooks/suspend.h>
+#include <trace/hooks/sched.h>
#include "perf_metrics.h"
#include <trace/hooks/systrace.h>
+#include "../../include/sched.h"
+#include <kernel/sched/sched.h>
+
+struct irq_storm_data {
+ atomic64_t storm_count;
+ s64 max_storm_count;
+ atomic64_t irq_storm_start;
+};
+
+struct resume_latency {
+ u64 resume_start;
+ u64 resume_end;
+ spinlock_t resume_latency_stat_lock;
+ s64 resume_count[RESUME_LATENCY_ARR_SIZE];
+ u64 resume_latency_max_ms;
+ u64 resume_latency_sum_ms;
+ u64 resume_latency_threshold;
+ bool display_warning;
+};
+
+struct long_irq {
+ ktime_t softirq_start[CONFIG_VH_SCHED_CPU_NR][NR_SOFTIRQS];
+ ktime_t irq_start[CONFIG_VH_SCHED_CPU_NR][MAX_IRQ_NUM];
+ atomic64_t long_softirq_count;
+ atomic64_t long_irq_count;
+ atomic64_t long_softirq_count_arr[CONFIG_VH_SCHED_CPU_NR];
+ atomic64_t long_irq_count_arr[CONFIG_VH_SCHED_CPU_NR];
+ s64 long_softirq_arr[NR_SOFTIRQS];
+ s64 long_irq_arr[MAX_IRQ_NUM];
+ struct irq_storm_data irq_storms[MAX_IRQ_NUM];
+ s64 long_softirq_threshold;
+ s64 long_irq_threshold;
+ s64 irq_storm_threshold_us;
+ bool display_warning;
+};
+
+struct rt_runnable {
+ u64 latency;
+ char *comm;
+ char *prev_comm;
+ pid_t pid;
+};
+
+struct top_rt_runnable {
+ struct rt_runnable rt_runnable[RT_RUNNABLE_ARR_SIZE];
+ int min_idx;
+ atomic64_t count;
+};
struct irq_entry {
int irq_num;
s64 latency;
s64 max_storm_count;
};
+
static struct resume_latency resume_latency_stats;
static struct long_irq long_irq_stat;
+static DEFINE_PER_CPU(struct top_rt_runnable, top_rt_runnable);
+static DEFINE_PER_CPU(spinlock_t, rt_runnable_lock);
+unsigned long long_rt_runnable_threshold_ns = 1500000UL;
+
+/*********************************************************************
+ * HELPER FUNCTIONS *
+ *********************************************************************/
+
+static bool is_top_latency(u64 latency)
+{
+ struct top_rt_runnable trr;
+ int cpu = raw_smp_processor_id();
+
+ spin_lock(&per_cpu(rt_runnable_lock, cpu));
+ trr = per_cpu(top_rt_runnable, cpu);
+ spin_unlock(&per_cpu(rt_runnable_lock, cpu));
+
+ return latency > trr.rt_runnable[trr.min_idx].latency;
+}
+
+static void update_min_latency(struct task_struct *prev, struct task_struct *next, u64 latency)
+{
+ u64 min_latency = -1LL;
+ struct rt_runnable *rt_runnable;
+ struct top_rt_runnable *trr;
+ int min_idx, i, cpu;
+ bool in_arr = false;
+
+ cpu = raw_smp_processor_id();
+ spin_lock(&per_cpu(rt_runnable_lock, cpu));
+ trr = this_cpu_ptr(&top_rt_runnable);
+
+ atomic64_inc(&(trr->count));
+
+ /* Search if the pid is already in the top_runnable, if so, update it */
+ for (i = 0; i < RT_RUNNABLE_ARR_SIZE; i++) {
+ rt_runnable = &trr->rt_runnable[i];
+ if (rt_runnable->pid == next->pid) {
+ in_arr = true;
+ rt_runnable->latency = latency;
+ break;
+ }
+ }
+ if (!in_arr){
+ rt_runnable = &trr->rt_runnable[trr->min_idx];
+ rt_runnable->latency = latency;
+ rt_runnable->comm = kstrdup(next->comm, GFP_KERNEL);
+ rt_runnable->prev_comm = kstrdup(prev->comm, GFP_KERNEL);
+ rt_runnable->pid = next->pid;
+ }
+
+ /* Update min entry among top_rt_runnable */
+ for (i = 0; i < RT_RUNNABLE_ARR_SIZE; i++) {
+ rt_runnable = &trr->rt_runnable[i];
+ if (rt_runnable->latency < min_latency) {
+ min_latency = rt_runnable->latency;
+ min_idx = i;
+ }
+ }
+
+ trr->min_idx = min_idx;
+ spin_unlock(&per_cpu(rt_runnable_lock, cpu));
+}
+
+static int irq_latency_cmp(const void *a, const void *b)
+{
+ return ((struct irq_entry *)b)->latency - ((struct irq_entry *)a)->latency;
+}
+
+static int irq_storm_count_cmp(const void *a, const void *b)
+{
+ return ((struct irq_entry *)b)->max_storm_count - ((struct irq_entry *)a)->max_storm_count;
+}
+
+static int runnable_latency_cmp(const void *a, const void *b)
+{
+ if (((struct rt_runnable *)a)->latency < ((struct rt_runnable *)b)->latency)
+ return 1;
+ else if (((struct rt_runnable *)a)->latency > ((struct rt_runnable *)b)->latency)
+ return -1;
+ else
+ return 0;
+}
+
/*********************************************************************
* SYSTEM TRACE
*********************************************************************/
@@ -204,18 +338,47 @@ static void hook_irq_end(void *data, int irq, struct irqaction *action, int ret)
curr_max_irq, irq_usec) != curr_max_irq);
}
-/*********************************************************************
- * HELPER FUNCTIONS *
- *********************************************************************/
-
-static int irq_latency_cmp(const void *a, const void *b)
+void vh_sched_wakeup_pixel_mod(void *data, struct task_struct *p)
{
- return ((struct irq_entry *)b)->latency - ((struct irq_entry *)a)->latency;
+ struct vendor_task_struct *vp;
+
+ if (!rt_task(p))
+ return;
+ vp = get_vendor_task_struct(p);
+ vp->runnable_start_ns = sched_clock();
}
-static int irq_storm_count_cmp(const void *a, const void *b)
+void vh_sched_switch_pixel_mod(void *data, bool preempt,
+ struct task_struct *prev,
+ struct task_struct *next)
{
- return ((struct irq_entry *)b)->max_storm_count - ((struct irq_entry *)a)->max_storm_count;
+ struct vendor_task_struct *vnext, *vprev;
+ u64 now, runnable_delta;
+
+ now = sched_clock();
+ vprev = get_vendor_task_struct(prev);
+
+ /*
+ * Update previous task's runnable_start_ns if it is in TASK_RUNNING state,
+ * which means it remains in rq. Otherwise, invalidate runnable_start_ns,
+ * given task is dequeued.
+ */
+ if (task_is_running(prev) && rt_task(prev))
+ vprev->runnable_start_ns = now;
+ else
+ vprev->runnable_start_ns = -1;
+
+ vnext = get_vendor_task_struct(next);
+ if (!rt_task(next) || vnext->runnable_start_ns > now)
+ return;
+
+ runnable_delta = now - vnext->runnable_start_ns;
+ if (runnable_delta < long_rt_runnable_threshold_ns ||
+ !is_top_latency(runnable_delta))
+ return;
+
+ update_min_latency(prev, next, runnable_delta);
+
}
/*******************************************************************
@@ -504,6 +667,7 @@ static ssize_t irq_display_warning_store(struct kobject *kobj,
}
return count;
}
+
static ssize_t irq_stats_reset_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf,
@@ -519,6 +683,82 @@ static ssize_t irq_stats_reset_store(struct kobject *kobj,
atomic64_set(&(long_irq_stat.long_softirq_count), 0);
return count;
}
+
+static ssize_t long_runnable_metrics_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ int cpu, i;
+ ssize_t count = 0;
+ struct top_rt_runnable trr;
+ struct rt_runnable long_rt_runnable;
+ struct rt_runnable sorted_trr[RT_RUNNABLE_ARR_SIZE];
+
+ for_each_possible_cpu(cpu) {
+ count += sysfs_emit_at(buf, count, "cpu %d\n",cpu);
+ spin_lock(&per_cpu(rt_runnable_lock, cpu));
+ trr = per_cpu(top_rt_runnable, cpu);
+ spin_unlock(&per_cpu(rt_runnable_lock, cpu));
+ count += sysfs_emit_at(buf, count, "LONG RT_RUNNABLE: %lld\n",
+ atomic64_read(&(trr.count)));
+
+ for (i = 0; i < RT_RUNNABLE_ARR_SIZE; i++) {
+ long_rt_runnable = trr.rt_runnable[i];
+ sorted_trr[i].comm = kstrdup(long_rt_runnable.comm, GFP_KERNEL);
+ sorted_trr[i].prev_comm = kstrdup(long_rt_runnable.prev_comm, GFP_KERNEL);
+ sorted_trr[i].latency = long_rt_runnable.latency;
+ }
+ sort(sorted_trr, RT_RUNNABLE_ARR_SIZE, sizeof(struct rt_runnable),
+ runnable_latency_cmp, NULL);
+ for (i = 0; i < RT_RUNNABLE_ARR_SIZE; i++) {
+ count += sysfs_emit_at(buf, count, "%s %llu %s\n",
+ (const char *)sorted_trr[i].comm,
+ sorted_trr[i].latency,
+ (const char *)sorted_trr[i].prev_comm);
+ }
+ count += sysfs_emit_at(buf, count, "\n");
+ }
+ return count;
+}
+
+static ssize_t runnable_stats_reset_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int cpu;
+ struct top_rt_runnable *trr;
+
+ for_each_possible_cpu(cpu) {
+ spin_lock(&per_cpu(rt_runnable_lock, cpu));
+ trr = &per_cpu(top_rt_runnable, cpu);
+ atomic64_set(&(trr->count), 0);
+ memset(trr->rt_runnable, 0, sizeof(struct top_rt_runnable));
+ spin_unlock(&per_cpu(rt_runnable_lock, cpu));
+ }
+ return count;
+}
+
+static ssize_t runnable_stats_enable_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ register_trace_sched_wakeup(vh_sched_wakeup_pixel_mod, NULL);
+ register_trace_sched_switch(vh_sched_switch_pixel_mod, NULL);
+ return count;
+}
+
+static ssize_t runnable_stats_disable_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ unregister_trace_sched_wakeup(vh_sched_wakeup_pixel_mod, NULL);
+ unregister_trace_sched_switch(vh_sched_switch_pixel_mod, NULL);
+ return count;
+}
+
static struct kobj_attribute resume_latency_metrics_attr = __ATTR(resume_latency_metrics,
0664,
resume_latency_metrics_show,
@@ -556,12 +796,29 @@ static struct kobj_attribute irq_display_warning_attr = __ATTR(display_warning,
0664,
irq_display_warning_show,
irq_display_warning_store);
-
static struct kobj_attribute irq_stats_reset_attr = __ATTR(
stats_reset,
0200,
NULL,
irq_stats_reset_store);
+static struct kobj_attribute long_runnable_metrics_attr = __ATTR(stats,
+ 0444,
+ long_runnable_metrics_show,
+ NULL);
+
+static struct kobj_attribute runnable_stats_reset_attr = __ATTR(
+ stats_reset,
+ 0200,
+ NULL,
+ runnable_stats_reset_store);
+static struct kobj_attribute runnable_stats_enable_attr = __ATTR(enable,
+ 0200,
+ NULL,
+ runnable_stats_enable_store);
+static struct kobj_attribute runnable_stats_disable_attr = __ATTR(disable,
+ 0200,
+ NULL,
+ runnable_stats_disable_store);
static struct attribute *irq_attrs[] = {
&long_irq_metrics_attr.attr,
@@ -591,13 +848,29 @@ static const struct attribute_group resume_latency_attr_group = {
.name = "resume_latency"
};
+static struct attribute *runnable_attrs[] = {
+ &long_runnable_metrics_attr.attr,
+ &runnable_stats_reset_attr.attr,
+ &runnable_stats_enable_attr.attr,
+ &runnable_stats_disable_attr.attr,
+ NULL
+};
+
+static const struct attribute_group runnable_attr_group = {
+ .attrs = runnable_attrs,
+ .name = "runnable"
+};
+
+
/*********************************************************************
* INITIALIZE DRIVER *
*********************************************************************/
int perf_metrics_init(struct kobject *metrics_kobj)
{
+ int cpu;
int ret = 0;
+
if (!metrics_kobj) {
pr_err("metrics_kobj is not initialized\n");
return -EINVAL;
@@ -610,6 +883,11 @@ int perf_metrics_init(struct kobject *metrics_kobj)
pr_err("failed to create irq folder\n");
return -ENOMEM;
}
+ if (sysfs_create_group(metrics_kobj, &runnable_attr_group)) {
+ pr_err("failed to create runnable folder\n");
+ return -ENOMEM;
+ }
+
spin_lock_init(&resume_latency_stats.resume_latency_stat_lock);
resume_latency_stats.resume_latency_threshold = RESUME_LATENCY_DEFAULT_THRESHOLD;
ret = register_trace_android_vh_early_resume_begin(
@@ -624,6 +902,7 @@ int perf_metrics_init(struct kobject *metrics_kobj)
pr_err("Register resume end vendor hook fail %d\n", ret);
return ret;
}
+
long_irq_stat.long_softirq_threshold = 10000;
long_irq_stat.long_irq_threshold = 500;
long_irq_stat.irq_storm_threshold_us = 500;
@@ -647,6 +926,18 @@ int perf_metrics_init(struct kobject *metrics_kobj)
pr_err("Register irq exit hook fail %d\n", ret);
return ret;
}
+
+ for_each_possible_cpu(cpu) {
+ spin_lock_init(&per_cpu(rt_runnable_lock, cpu));
+ }
+ ret = register_trace_sched_wakeup(vh_sched_wakeup_pixel_mod, NULL);
+ if (ret)
+ return ret;
+
+ ret = register_trace_sched_switch(vh_sched_switch_pixel_mod, NULL);
+ if (ret)
+ return ret;
+
pr_info("perf_metrics driver initialized! :D\n");
return ret;
}
diff --git a/drivers/soc/google/vh/kernel/metrics/perf_metrics.h b/drivers/soc/google/vh/kernel/metrics/perf_metrics.h
index 1174ad157..79902d5c9 100644
--- a/drivers/soc/google/vh/kernel/metrics/perf_metrics.h
+++ b/drivers/soc/google/vh/kernel/metrics/perf_metrics.h
@@ -20,6 +20,8 @@
#define MAX_IRQ_NUM 2048
#define IRQ_ARR_LIMIT 100
+#define RT_RUNNABLE_ARR_SIZE 5
+
#define LATENCY_CNT_SMALL (RESUME_LATENCY_BOUND_SMALL / RESUME_LATENCY_STEP_SMALL)
#define LATENCY_CNT_MID ((RESUME_LATENCY_BOUND_MID - RESUME_LATENCY_BOUND_SMALL) / \
RESUME_LATENCY_STEP_MID)
@@ -27,35 +29,3 @@
RESUME_LATENCY_STEP_LARGE)
#define RESUME_LATENCY_ARR_SIZE (LATENCY_CNT_SMALL + LATENCY_CNT_MID + LATENCY_CNT_LARGE + 1)
-struct irq_storm_data {
- atomic64_t storm_count;
- s64 max_storm_count;
- atomic64_t irq_storm_start;
-};
-
-struct resume_latency {
- u64 resume_start;
- u64 resume_end;
- spinlock_t resume_latency_stat_lock;
- s64 resume_count[RESUME_LATENCY_ARR_SIZE];
- u64 resume_latency_max_ms;
- u64 resume_latency_sum_ms;
- u64 resume_latency_threshold;
- bool display_warning;
-};
-
-struct long_irq {
- ktime_t softirq_start[CONFIG_VH_SCHED_CPU_NR][NR_SOFTIRQS];
- ktime_t irq_start[CONFIG_VH_SCHED_CPU_NR][MAX_IRQ_NUM];
- atomic64_t long_softirq_count;
- atomic64_t long_irq_count;
- atomic64_t long_softirq_count_arr[CONFIG_VH_SCHED_CPU_NR];
- atomic64_t long_irq_count_arr[CONFIG_VH_SCHED_CPU_NR];
- s64 long_softirq_arr[NR_SOFTIRQS];
- s64 long_irq_arr[MAX_IRQ_NUM];
- struct irq_storm_data irq_storms[MAX_IRQ_NUM];
- s64 long_softirq_threshold;
- s64 long_irq_threshold;
- s64 irq_storm_threshold_us;
- bool display_warning;
-};
diff --git a/drivers/soc/google/vh/kernel/sched/sched_priv.h b/drivers/soc/google/vh/kernel/sched/sched_priv.h
index c1fdfca07..12487fb3c 100644
--- a/drivers/soc/google/vh/kernel/sched/sched_priv.h
+++ b/drivers/soc/google/vh/kernel/sched/sched_priv.h
@@ -456,6 +456,7 @@ static inline void init_vendor_task_struct(struct vendor_task_struct *v_tsk)
v_tsk->binder_task.active = false;
v_tsk->uclamp_pi[UCLAMP_MIN] = uclamp_none(UCLAMP_MIN);
v_tsk->uclamp_pi[UCLAMP_MAX] = uclamp_none(UCLAMP_MAX);
+ v_tsk->runnable_start_ns = -1;
}
int acpu_init(void);