From b666a7e42a83baafbfc7609727f70ff3a2535a3c Mon Sep 17 00:00:00 2001 From: Elliot Berman Date: Tue, 6 Aug 2019 17:06:01 -0700 Subject: [PATCH] time: Add rq_stats snapshot Add snapshot of runqueue stats module as of commit (Merge "uapi: Introduce V4L2_FLAG_CVPMETADATA_SKIP flag"). Added #ifdefery to allow rq_stats to actually be optional. Change-Id: I3352a472c5b0ad61cc04317a1d6f26549bfcdc04 Signed-off-by: Elliot Berman --- drivers/soc/qcom/Kconfig | 8 +++ drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/rq_stats.c | 108 ++++++++++++++++++++++++++++++++++++ include/linux/rq_stats.h | 22 ++++++++ kernel/time/tick-sched.c | 25 ++++++++- 5 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 drivers/soc/qcom/rq_stats.c create mode 100644 include/linux/rq_stats.h diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index ffd208bd9923..50435f67187a 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -282,6 +282,14 @@ config QCOM_RPMHPD value to RPMh which then translates it into corresponding voltage for the voltage rail. +config QCOM_RUN_QUEUE_STATS + bool "Enable collection and exporting of QTI Run Queue stats to userspace" + help + This option enables the driver to periodically collecting the statistics + of kernel run queue information and calculate the load of the system. + This information is exported to usespace via sysfs entries and userspace + algorithms uses info and decide when to turn on/off the cpu cores. + config QCOM_RPMPD bool "Qualcomm RPM Power domain driver" depends on QCOM_SMD_RPM=y diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index bd69f32afe73..3911cc948473 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_QCOM_SECURE_BUFFER) += secure_buffer.o obj-$(CONFIG_QCOM_SOCINFO) += socinfo.o obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o obj-$(CONFIG_QCOM_IPCC) += qcom_ipcc.o +obj-$(CONFIG_QCOM_RUN_QUEUE_STATS) += rq_stats.o obj-$(CONFIG_QCOM_APR) += apr.o obj-$(CONFIG_QCOM_LLCC) += llcc-slice.o obj-$(CONFIG_QCOM_LAHAINA_LLCC) += llcc-lahaina.o diff --git a/drivers/soc/qcom/rq_stats.c b/drivers/soc/qcom/rq_stats.c new file mode 100644 index 000000000000..a5ce0246404e --- /dev/null +++ b/drivers/soc/qcom/rq_stats.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2010-2015, 2017, 2019, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include + +#define MAX_LONG_SIZE 24 +#define DEFAULT_DEF_TIMER_JIFFIES 5 + +struct rq_data rq_info; +struct workqueue_struct *rq_wq; +spinlock_t rq_lock; + +static void def_work_fn(struct work_struct *work) +{ + /* Notify polling threads on change of value */ + sysfs_notify(rq_info.kobj, NULL, "def_timer_ms"); +} + +static ssize_t show_def_timer_ms(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int64_t diff; + unsigned int udiff; + + diff = ktime_to_ns(ktime_get()) - rq_info.def_start_time; + do_div(diff, 1000 * 1000); + udiff = (unsigned int) diff; + + return snprintf(buf, MAX_LONG_SIZE, "%u\n", udiff); +} + +static ssize_t store_def_timer_ms(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + unsigned int val = 0; + + if (kstrtouint(buf, 0, &val)) + return -EINVAL; + + rq_info.def_timer_jiffies = msecs_to_jiffies(val); + + rq_info.def_start_time = ktime_to_ns(ktime_get()); + return count; +} + +static struct kobj_attribute def_timer_ms_attr = + __ATTR(def_timer_ms, 0600, show_def_timer_ms, + store_def_timer_ms); + +static struct attribute *rq_attrs[] = { + &def_timer_ms_attr.attr, + NULL, +}; + +static struct attribute_group rq_attr_group = { + .attrs = rq_attrs, +}; + +static int init_rq_attribs(void) +{ + int err; + + rq_info.attr_group = &rq_attr_group; + + /* Create /sys/devices/system/cpu/cpu0/rq-stats/... */ + rq_info.kobj = kobject_create_and_add("rq-stats", + &get_cpu_device(0)->kobj); + if (!rq_info.kobj) + return -ENOMEM; + + err = sysfs_create_group(rq_info.kobj, rq_info.attr_group); + if (err) + kobject_put(rq_info.kobj); + else + kobject_uevent(rq_info.kobj, KOBJ_ADD); + + return err; +} + +static int __init msm_rq_stats_init(void) +{ + int ret; + +#ifndef CONFIG_SMP + /* Bail out if this is not an SMP Target */ + rq_info.init = 0; + return -EPERM; +#endif + + rq_wq = create_singlethread_workqueue("rq_stats"); + WARN_ON(!rq_wq); + INIT_WORK(&rq_info.def_timer_work, def_work_fn); + spin_lock_init(&rq_lock); + rq_info.def_timer_jiffies = DEFAULT_DEF_TIMER_JIFFIES; + rq_info.def_timer_last_jiffy = 0; + ret = init_rq_attribs(); + + rq_info.init = 1; + + return ret; +} +late_initcall(msm_rq_stats_init); diff --git a/include/linux/rq_stats.h b/include/linux/rq_stats.h new file mode 100644 index 000000000000..359f159354fa --- /dev/null +++ b/include/linux/rq_stats.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2011,2013-2014,2019, The Linux Foundation. All rights reserved. + */ + +#ifdef CONFIG_QCOM_RUN_QUEUE_STATS + +struct rq_data { + unsigned long def_timer_jiffies; + unsigned long def_timer_last_jiffy; + int64_t def_start_time; + struct attribute_group *attr_group; + struct kobject *kobj; + struct work_struct def_timer_work; + int init; +}; + +extern struct rq_data rq_info; +extern struct workqueue_struct *rq_wq; +extern spinlock_t rq_lock; + +#endif diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 3154af49ef0a..eee9b1a643ce 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1284,6 +1285,18 @@ void tick_irq_enter(void) * High resolution timer specific code */ #ifdef CONFIG_HIGH_RES_TIMERS +#ifdef CONFIG_QCOM_RUN_QUEUE_STATS +static void wakeup_user(void) +{ + unsigned long jiffy_gap; + + jiffy_gap = jiffies - rq_info.def_timer_last_jiffy; + if (jiffy_gap >= rq_info.def_timer_jiffies) { + rq_info.def_timer_last_jiffy = jiffies; + queue_work(rq_wq, &rq_info.def_timer_work); + } +} +#endif /* * We rearm the timer until we get disabled by the idle code. * Called with interrupts disabled. @@ -1301,8 +1314,18 @@ static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer) * Do not call, when we are not in irq context and have * no valid regs pointer */ - if (regs) + if (regs) { tick_sched_handle(ts, regs); +#ifdef CONFIG_QCOM_RUN_QUEUE_STATS + if (rq_info.init == 1 && + tick_do_timer_cpu == smp_processor_id()) { + /* + * wakeup user if needed + */ + wakeup_user(); + } +#endif + } else ts->next_tick = 0;