diff --git a/drivers/soc/google/vh/kernel/metrics.c b/drivers/soc/google/vh/kernel/metrics.c index 94618acfe8b6..5bae61f5c5ad 100644 --- a/drivers/soc/google/vh/kernel/metrics.c +++ b/drivers/soc/google/vh/kernel/metrics.c @@ -25,6 +25,7 @@ #include "metrics.h" static struct resume_latency resume_latency_stats; +static struct long_irq long_irq_stat; static struct kobject *primary_sysfs_folder; /********************************************************************* @@ -71,7 +72,67 @@ static void vendor_hook_resume_end(void *data, void *unused) resume_latency_stats.resume_start = resume_latency_stats.resume_end; } +static void hook_softirq_begin(void *data, unsigned int vec_nr) +{ + int cpu_num; + cpu_num = raw_smp_processor_id(); + long_irq_stat.softirq_start[cpu_num][vec_nr] = ktime_get(); +} + +static void hook_softirq_end(void *data, unsigned int vec_nr) +{ + s64 irq_usec; + int cpu_num; + s64 curr_max_irq; + if (vec_nr >= NR_SOFTIRQS) + return; + cpu_num = raw_smp_processor_id(); + long_irq_stat.softirq_end = ktime_get(); + irq_usec = ktime_to_us(ktime_sub(long_irq_stat.softirq_end, + long_irq_stat.softirq_start[cpu_num][vec_nr])); + if (irq_usec >= long_irq_stat.long_softirq_threshold) { + if (long_irq_stat.display_warning) + WARN("%s","Got a long running irq: softirq\n"); + atomic64_inc(&(long_irq_stat.long_softirq_count)); + } + do { + curr_max_irq = long_irq_stat.long_softirq_arr[vec_nr]; + if (irq_usec < curr_max_irq) + return; + } while (cmpxchg64(&long_irq_stat.long_softirq_arr[vec_nr], + curr_max_irq, irq_usec) != curr_max_irq); +} + +static void hook_irq_begin(void *data, int irq, struct irqaction *action) +{ + int cpu_num; + cpu_num = raw_smp_processor_id(); + long_irq_stat.irq_start[cpu_num][irq] = ktime_get(); +} +static void hook_irq_end(void *data, int irq, struct irqaction *action, int ret) +{ + s64 irq_usec; + int cpu_num; + s64 curr_max_irq; + if (irq >= MAX_IRQ_NUM) + return; + cpu_num = raw_smp_processor_id(); + long_irq_stat.irq_end = ktime_get(); + irq_usec = ktime_to_us(ktime_sub(long_irq_stat.irq_end, + long_irq_stat.irq_start[cpu_num][irq])); + if (irq_usec >= long_irq_stat.long_irq_threshold) { + if (long_irq_stat.display_warning) + WARN("%s","Got a long running irq: irq_handler\n"); + atomic64_inc(&(long_irq_stat.long_irq_count)); + } + do { + curr_max_irq = long_irq_stat.long_irq_arr[irq]; + if (irq_usec < curr_max_irq) + break; + } while (cmpxchg64(&long_irq_stat.long_irq_arr[irq], + curr_max_irq, irq_usec) != curr_max_irq); +} /******************************************************************* * SYSFS * *******************************************************************/ @@ -134,12 +195,148 @@ static ssize_t resume_latency_metrics_store(struct kobject *kobj, spin_unlock(&resume_latency_stats.resume_latency_stat_lock); return count; } +static ssize_t long_irq_metrics_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + ssize_t count = 0; + int index; + s64 latency; + int irq_num; + count += sysfs_emit_at(buf, count, "Long running SOFTIRQ count: %lld\n", + atomic64_read(&(long_irq_stat.long_softirq_count))); + for (index = 0; index < NR_SOFTIRQS; index++) { + latency = long_irq_stat.long_softirq_arr[index]; + irq_num = index; + count += sysfs_emit_at(buf, count, + "long SOFTIRQ latency: %lld, long SOFTIRQ num: %d\n", latency, irq_num); + } + count += sysfs_emit_at(buf, count, "Long running IRQ count: %lld\n", + atomic64_read(&(long_irq_stat.long_irq_count))); + for (index = 0; index < MAX_IRQ_NUM; index++) { + latency = long_irq_stat.long_irq_arr[index]; + irq_num = index; + count += sysfs_emit_at(buf, count, + "long IRQ latency: %lld, long IRQ num: %d\n", latency, irq_num); + } + return count; +} +static ssize_t modify_softirq_threshold_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + ssize_t count = 0; + count += sysfs_emit_at(buf, count,"%lld\n", long_irq_stat.long_softirq_threshold); + return count; +} + +static ssize_t modify_softirq_threshold_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t count) +{ + s64 new_threshold_us; + int err = sscanf (buf, "%lld", &new_threshold_us); + if (!err || new_threshold_us < 0) { + return count; + } + long_irq_stat.long_softirq_threshold = new_threshold_us; + atomic64_set(&(long_irq_stat.long_softirq_count), 0); + return count; +} + +static ssize_t modify_irq_threshold_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + ssize_t count = 0; + count += sysfs_emit_at(buf, count,"%lld\n", long_irq_stat.long_irq_threshold); + return count; +} + +static ssize_t modify_irq_threshold_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t count) +{ + s64 new_threshold_us; + int err = sscanf (buf, "%lld", &new_threshold_us); + if (!err || new_threshold_us < 0) { + return count; + } + long_irq_stat.long_irq_threshold = new_threshold_us; + atomic64_set(&(long_irq_stat.long_irq_count), 0); + return count; +} + +static ssize_t display_warning_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + ssize_t count = 0; + if (long_irq_stat.display_warning) { + count += sysfs_emit_at(buf, count,"%s", + "WARN is turned on\n"); + } else { + count += sysfs_emit_at(buf, count,"%s", + "WARN is turned off\n"); + } + return count; +} + +static ssize_t display_warning_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t count) +{ + int display_warn; + int err = sscanf (buf, "%d", &display_warn); + if (!err) { + return count; + } + if (display_warn == 0) { + long_irq_stat.display_warning = false; + } + if (display_warn == 1) { + long_irq_stat.display_warning = true; + } + return count; +} static struct kobj_attribute resume_latency_metrics_attr = __ATTR(resume_latency_metrics, 0664, resume_latency_metrics_show, resume_latency_metrics_store); +static struct kobj_attribute long_irq_metrics_attr = __ATTR(long_irq_metrics, + 0444, + long_irq_metrics_show, + NULL); +static struct kobj_attribute modify_softirq_threshold_attr = __ATTR(modify_softirq_threshold, + 0664, + modify_softirq_threshold_show, + modify_softirq_threshold_store); +static struct kobj_attribute modify_irq_threshold_attr = __ATTR(modify_irq_threshold, + 0664, + modify_irq_threshold_show, + modify_irq_threshold_store); +static struct kobj_attribute display_warning_attr = __ATTR(display_warning, + 0664, + display_warning_show, + display_warning_store); + +static struct attribute *irq_attrs[] = { + &long_irq_metrics_attr.attr, + &modify_softirq_threshold_attr.attr, + &modify_irq_threshold_attr.attr, + &display_warning_attr.attr, + NULL +}; + +static const struct attribute_group irq_attr_group = { + .attrs = irq_attrs, + .name = "irq" +}; static struct attribute *resume_latency_attrs[] = { &resume_latency_metrics_attr.attr, @@ -167,6 +364,10 @@ static int __init perf_metrics_init(void) pr_err("failed to create resume_latency folder\n"); return ret; } + if (sysfs_create_group(primary_sysfs_folder, &irq_attr_group)) { + pr_err("failed to create irq folder\n"); + return ret; + } spin_lock_init(&resume_latency_stats.resume_latency_stat_lock); ret = register_trace_android_vh_early_resume_begin( vendor_hook_resume_begin, NULL); @@ -180,6 +381,28 @@ static int __init perf_metrics_init(void) 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; + ret = register_trace_softirq_entry(hook_softirq_begin, NULL); + if (ret) { + pr_err("Register soft irq handler hook fail %d\n", ret); + return ret; + } + ret = register_trace_softirq_exit(hook_softirq_end, NULL); + if (ret) { + pr_err("Register soft irq exit hook fail %d\n", ret); + return ret; + } + ret = register_trace_irq_handler_entry(hook_irq_begin, NULL); + if (ret) { + pr_err("Register irq handler hook fail %d\n", ret); + return ret; + } + ret = register_trace_irq_handler_exit(hook_irq_end, NULL); + if (ret) { + pr_err("Register irq exit hook fail %d\n", ret); + return ret; + } pr_info("perf_metrics driver initialized! :D\n"); return ret; } diff --git a/drivers/soc/google/vh/kernel/metrics.h b/drivers/soc/google/vh/kernel/metrics.h index ed2cbeaa6941..059dbfa157df 100644 --- a/drivers/soc/google/vh/kernel/metrics.h +++ b/drivers/soc/google/vh/kernel/metrics.h @@ -15,6 +15,8 @@ #define RESUME_LATENCY_BOUND_MID 500 #define RESUME_LATENCY_BOUND_MAX 1000 +#define MAX_IRQ_NUM 1024 + #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) @@ -30,3 +32,17 @@ struct resume_latency { s64 resume_latency_max_ms; u64 resume_latency_sum_ms; }; + +struct long_irq { + ktime_t softirq_start[CONFIG_VH_SCHED_CPU_NR][NR_SOFTIRQS]; + ktime_t softirq_end; + ktime_t irq_start[CONFIG_VH_SCHED_CPU_NR][MAX_IRQ_NUM]; + ktime_t irq_end; + atomic64_t long_softirq_count; + atomic64_t long_irq_count; + s64 long_softirq_arr[NR_SOFTIRQS]; + s64 long_irq_arr[MAX_IRQ_NUM]; + s64 long_softirq_threshold; + s64 long_irq_threshold; + bool display_warning; +};