From 5650389d0a250962e0a4fd43de7ec5cfe1bd0af8 Mon Sep 17 00:00:00 2001 From: David Gwynne Date: Thu, 23 Nov 2023 01:00:45 +0000 Subject: 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. --- sys/arch/arm64/arm64/cpu.c | 172 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 1 deletion(-) (limited to 'sys/arch/arm64') 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 @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "kstat.h" + #include #include #include @@ -25,6 +27,7 @@ #include #include #include +#include #include @@ -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 */ -- cgit v1.2.3