summaryrefslogtreecommitdiff
path: root/sys/arch/arm64
diff options
context:
space:
mode:
authorDavid Gwynne <dlg@cvs.openbsd.org>2023-11-23 01:00:45 +0000
committerDavid Gwynne <dlg@cvs.openbsd.org>2023-11-23 01:00:45 +0000
commit5650389d0a250962e0a4fd43de7ec5cfe1bd0af8 (patch)
tree92a02ac15410ce7208979edba9d34978146176be /sys/arch/arm64
parent4dc1f9bbd45521b158d0d28024c5389dcb0dcbcc (diff)
provide operating performance point info about each cpu via kstats.
if there's a device tree and it provides information about cpu speed, expose those stats. this is particularly useful on big.little or systems with multiple clusters/clock domains or cores that can scale indepenently because it can report the actual speed each cpu is operating at independently. ok patrick@ who used an earlier version of this diff to work on cpu scaling on an rk3588 system.
Diffstat (limited to 'sys/arch/arm64')
-rw-r--r--sys/arch/arm64/arm64/cpu.c172
1 files changed, 171 insertions, 1 deletions
diff --git a/sys/arch/arm64/arm64/cpu.c b/sys/arch/arm64/arm64/cpu.c
index 7825d2418fc..50446f67855 100644
--- a/sys/arch/arm64/arm64/cpu.c
+++ b/sys/arch/arm64/arm64/cpu.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.c,v 1.99 2023/10/24 13:20:09 claudio Exp $ */
+/* $OpenBSD: cpu.c,v 1.100 2023/11/23 01:00:44 dlg Exp $ */
/*
* Copyright (c) 2016 Dale Rahn <drahn@dalerahn.com>
@@ -17,6 +17,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "kstat.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
@@ -25,6 +27,7 @@
#include <sys/sysctl.h>
#include <sys/task.h>
#include <sys/user.h>
+#include <sys/kstat.h>
#include <uvm/uvm.h>
@@ -250,6 +253,11 @@ void cpu_psci_init(struct cpu_info *);
void cpu_flush_bp_noop(void);
void cpu_flush_bp_psci(void);
+#if NKSTAT > 0
+void cpu_kstat_attach(struct cpu_info *ci);
+void cpu_opp_kstat_attach(struct cpu_info *ci);
+#endif
+
void
cpu_identify(struct cpu_info *ci)
{
@@ -937,6 +945,10 @@ cpu_attach(struct device *parent, struct device *dev, void *aux)
}
cpu_init();
+
+#if NKSTAT > 0
+ cpu_kstat_attach(ci);
+#endif
#ifdef MULTIPROCESSOR
}
#endif
@@ -1181,6 +1193,10 @@ cpu_init_secondary(struct cpu_info *ci)
spllower(IPL_NONE);
+#if NKSTAT > 0
+ cpu_kstat_attach(ci);
+#endif
+
sched_toidle();
}
@@ -1534,6 +1550,10 @@ cpu_opp_mountroot(struct device *self)
if (ot == NULL)
continue;
+#if NKSTAT > 0
+ cpu_opp_kstat_attach(ci);
+#endif
+
/* Skip if this table is shared and we're not the master. */
if (ot->ot_master && ot->ot_master != ci)
continue;
@@ -1817,3 +1837,153 @@ cpu_psci_init(struct cpu_info *ci)
ci->ci_psci_suspend_param =
OF_getpropint(node, "arm,psci-suspend-param", 0);
}
+
+#if NKSTAT > 0
+
+struct cpu_kstats {
+ struct kstat_kv ck_impl;
+ struct kstat_kv ck_part;
+ struct kstat_kv ck_rev;
+};
+
+void
+cpu_kstat_attach(struct cpu_info *ci)
+{
+ struct kstat *ks;
+ struct cpu_kstats *ck;
+
+ uint64_t midr, impl, part;
+ const char *impl_name = NULL, *part_name = NULL;
+ const struct cpu_cores *coreselecter = cpu_cores_none;
+ size_t i;
+
+ ks = kstat_create(ci->ci_dev->dv_xname, 0, "mach", 0, KSTAT_T_KV, 0);
+ if (ks == NULL) {
+ printf("%s: unable to create cpu kstats\n",
+ ci->ci_dev->dv_xname);
+ /* printf? */
+ return;
+ }
+
+ ck = malloc(sizeof(*ck), M_DEVBUF, M_WAITOK);
+
+ midr = READ_SPECIALREG(midr_el1);
+ impl = CPU_IMPL(midr);
+ part = CPU_PART(midr);
+
+ for (i = 0; cpu_implementers[i].name; i++) {
+ if (impl == cpu_implementers[i].id) {
+ impl_name = cpu_implementers[i].name;
+ coreselecter = cpu_implementers[i].corelist;
+ break;
+ }
+ }
+
+ if (impl_name) {
+ kstat_kv_init(&ck->ck_impl, "impl", KSTAT_KV_T_ISTR);
+ strlcpy(kstat_kv_istr(&ck->ck_impl), impl_name,
+ sizeof(kstat_kv_istr(&ck->ck_impl)));
+ } else
+ kstat_kv_init(&ck->ck_impl, "impl", KSTAT_KV_T_NULL);
+
+ for (i = 0; coreselecter[i].name; i++) {
+ if (part == coreselecter[i].id) {
+ part_name = coreselecter[i].name;
+ break;
+ }
+ }
+
+ if (part_name) {
+ kstat_kv_init(&ck->ck_part, "part", KSTAT_KV_T_ISTR);
+ strlcpy(kstat_kv_istr(&ck->ck_part), part_name,
+ sizeof(kstat_kv_istr(&ck->ck_part)));
+ } else
+ kstat_kv_init(&ck->ck_part, "part", KSTAT_KV_T_NULL);
+
+ kstat_kv_init(&ck->ck_rev, "rev", KSTAT_KV_T_ISTR);
+ snprintf(kstat_kv_istr(&ck->ck_rev), sizeof(kstat_kv_istr(&ck->ck_rev)),
+ "r%llup%llu", CPU_VAR(midr), CPU_REV(midr));
+
+ ks->ks_softc = ci;
+ ks->ks_data = ck;
+ ks->ks_datalen = sizeof(*ck);
+ ks->ks_read = kstat_read_nop;
+
+ kstat_install(ks);
+
+ /* XXX should we have a ci->ci_kstat = ks? */
+}
+
+struct cpu_opp_kstats {
+ struct kstat_kv coppk_freq;
+ struct kstat_kv coppk_supply_v;
+};
+
+int
+cpu_opp_kstat_read(struct kstat *ks)
+{
+ struct cpu_info *ci = ks->ks_softc;
+ struct cpu_opp_kstats *coppk = ks->ks_data;
+
+ struct opp_table *ot = ci->ci_opp_table;
+ struct cpu_info *oci = ot->ot_master;
+ struct timespec now, diff;
+
+ /* rate limit */
+ getnanouptime(&now);
+ timespecsub(&now, &ks->ks_updated, &diff);
+ if (diff.tv_sec < 1)
+ return (0);
+
+ if (oci == NULL)
+ oci = ci;
+
+ kstat_kv_freq(&coppk->coppk_freq) =
+ clock_get_frequency(oci->ci_node, NULL);
+
+ if (oci->ci_cpu_supply) {
+ kstat_kv_volts(&coppk->coppk_supply_v) =
+ regulator_get_voltage(oci->ci_cpu_supply);
+ }
+
+ ks->ks_updated = now;
+
+ return (0);
+}
+
+void
+cpu_opp_kstat_attach(struct cpu_info *ci)
+{
+ struct kstat *ks;
+ struct cpu_opp_kstats *coppk;
+ struct opp_table *ot = ci->ci_opp_table;
+ struct cpu_info *oci = ot->ot_master;
+
+ if (oci == NULL)
+ oci = ci;
+
+ ks = kstat_create(ci->ci_dev->dv_xname, 0, "dt-opp", 0,
+ KSTAT_T_KV, 0);
+ if (ks == NULL) {
+ printf("%s: unable to create cpu dt-opp kstats\n",
+ ci->ci_dev->dv_xname);
+ return;
+ }
+
+ coppk = malloc(sizeof(*coppk), M_DEVBUF, M_WAITOK);
+
+ kstat_kv_init(&coppk->coppk_freq, "freq", KSTAT_KV_T_FREQ);
+ kstat_kv_init(&coppk->coppk_supply_v, "supply",
+ oci->ci_cpu_supply ? KSTAT_KV_T_VOLTS_DC : KSTAT_KV_T_NULL);
+
+ ks->ks_softc = oci;
+ ks->ks_data = coppk;
+ ks->ks_datalen = sizeof(*coppk);
+ ks->ks_read = cpu_opp_kstat_read;
+
+ kstat_install(ks);
+
+ /* XXX should we have a ci->ci_opp_kstat = ks? */
+}
+
+#endif /* NKSTAT > 0 */