Index: sys/sys/kstat.h =================================================================== RCS file: /cvs/src/sys/sys/kstat.h,v retrieving revision 1.3 diff -u -p -r1.3 kstat.h --- sys/sys/kstat.h 22 Apr 2022 00:27:55 -0000 1.3 +++ sys/sys/kstat.h 11 Nov 2023 16:37:57 -0000 @@ -82,6 +82,9 @@ enum kstat_kv_type { KSTAT_KV_T_COUNTER16, KSTAT_KV_T_UINT16, KSTAT_KV_T_INT16, + KSTAT_KV_T_FREQ, /* frequency (Hz) */ + KSTAT_KV_T_VOLTS_DC, /* voltage (uV DC) */ + KSTAT_KV_T_VOLTS_AC, /* voltage (uV AC) */ }; /* units only apply to integer types */ @@ -119,6 +122,8 @@ struct kstat_kv { #define kstat_kv_s16(_kv) (_kv)->kv_v.v_s16 #define kstat_kv_len(_kv) (_kv)->kv_v.v_len #define kstat_kv_temp(_kv) (_kv)->kv_v.v_u64 +#define kstat_kv_freq(_kv) (_kv)->kv_v.v_u64 +#define kstat_kv_volts(_kv) (_kv)->kv_v.v_u64 #ifdef _KERNEL Index: usr.bin/kstat/kstat.c =================================================================== RCS file: /cvs/src/usr.bin/kstat/kstat.c,v retrieving revision 1.11 diff -u -p -r1.11 kstat.c --- usr.bin/kstat/kstat.c 10 Jul 2022 19:51:37 -0000 1.11 +++ usr.bin/kstat/kstat.c 11 Nov 2023 16:37:57 -0000 @@ -351,6 +351,34 @@ strdumpnl(const void *s, size_t len) printf("\n"); } +struct fmt_result { + uint64_t val; + unsigned int frac; + unsigned int exp; +}; + +static void +fmt_thing(struct fmt_result *fr, uint64_t val, uint64_t chunk) +{ + unsigned int exp = 0; + uint64_t rem = 0; + + while (val > chunk) { + rem = val % chunk; + val /= chunk; + exp++; + } + + fr->val = val; + fr->exp = exp; + fr->frac = (rem * 1000) / chunk; +} + +static const char *si_prefixes[] = { "", "k", "M", "G", "T", "P", "E" }; +#if 0 +static const char *iec_prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; +#endif + static void kstat_kv(const void *d, ssize_t len) { @@ -359,6 +387,7 @@ kstat_kv(const void *d, ssize_t len) ssize_t blen; void (*trailer)(const void *, size_t); double f; + struct fmt_result fr; if (len < (ssize_t)sizeof(*kv)) { warn("short kv (len %zu < size %zu)", len, sizeof(*kv)); @@ -425,6 +454,23 @@ kstat_kv(const void *d, ssize_t len) printf("%.2f degC", (f - 273150000.0) / 1000000.0); break; + case KSTAT_KV_T_FREQ: + fmt_thing(&fr, kstat_kv_freq(kv), 1000); + printf("%llu", fr.val); + if (fr.frac > 10) + printf(".%02u", fr.frac / 10); + printf(" %sHz", si_prefixes[fr.exp]); + break; + + case KSTAT_KV_T_VOLTS_DC: + f = kstat_kv_volts(kv) / 1000000.0; + printf("%.2f VDC", f); + break; + case KSTAT_KV_T_VOLTS_AC: + f = kstat_kv_volts(kv) / 1000000.0; + printf("%.2f VAC", f); + break; + default: printf("unknown type %u, stopping\n", kv->kv_type); return; @@ -482,10 +528,9 @@ kstat_list(struct kstat_tree *kt, int fd ksreq->ks_version = version; ksreq->ks_id = ++id; - ksreq->ks_datalen = len = 64; /* magic */ - ksreq->ks_data = malloc(len); - if (ksreq->ks_data == NULL) - err(1, "data alloc"); + /* some kstat reads are slow, so avoid them for now */ + ksreq->ks_datalen = 0; + ksreq->ks_data = NULL; if (ioctl(fd, KSTATIOC_NFIND_ID, ksreq) == -1) { if (errno == ENOENT) { @@ -504,13 +549,7 @@ kstat_list(struct kstat_tree *kt, int fd continue; } - if (RBT_INSERT(kstat_tree, kt, kse) != NULL) - errx(1, "duplicate kstat entry"); - - if (kse->serrno != 0) - continue; - - while (ksreq->ks_datalen > len) { + do { len = ksreq->ks_datalen; ksreq->ks_data = realloc(ksreq->ks_data, len); if (ksreq->ks_data == NULL) @@ -518,7 +557,10 @@ kstat_list(struct kstat_tree *kt, int fd if (ioctl(fd, KSTATIOC_FIND_ID, ksreq) == -1) err(1, "find id %llu", ksreq->ks_id); - } + } while (ksreq->ks_datalen > len); + + if (RBT_INSERT(kstat_tree, kt, kse) != NULL) + errx(1, "duplicate kstat entry"); } } Index: sys/arch/arm64/arm64/cpu.c =================================================================== RCS file: /cvs/src/sys/arch/arm64/arm64/cpu.c,v retrieving revision 1.99 diff -u -p -r1.99 cpu.c --- sys/arch/arm64/arm64/cpu.c 24 Oct 2023 13:20:09 -0000 1.99 +++ sys/arch/arm64/arm64/cpu.c 11 Nov 2023 16:37:57 -0000 @@ -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,10 @@ 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); +#endif + void cpu_identify(struct cpu_info *ci) { @@ -937,6 +944,10 @@ cpu_attach(struct device *parent, struct } cpu_init(); + +#if NKSTAT > 0 + cpu_kstat_attach(ci); +#endif #ifdef MULTIPROCESSOR } #endif @@ -1181,6 +1192,10 @@ cpu_init_secondary(struct cpu_info *ci) spllower(IPL_NONE); +#if NKSTAT > 0 + cpu_kstat_attach(ci); +#endif + sched_toidle(); } @@ -1817,3 +1832,109 @@ 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_device; + struct kstat_kv ck_impl; + struct kstat_kv ck_part; + struct kstat_kv ck_rev; + + struct kstat_kv ck_freq; + struct kstat_kv ck_volts; +}; + +int +cpu_kstat_read(struct kstat *ks) +{ + struct cpu_info *ci = ks->ks_softc; + struct cpu_kstats *ck = ks->ks_data; + + kstat_kv_freq(&ck->ck_freq) = clock_get_frequency(ci->ci_node, NULL); + kstat_kv_volts(&ck->ck_volts) = + regulator_get_voltage(ci->ci_cpu_supply); + + getnanouptime(&ks->ks_updated); /* accuracy is not important here */ + + return (0); +} + +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("mach", 0, "cpu", ci->ci_cpuid, 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); + + kstat_kv_init(&ck->ck_device, "device", KSTAT_KV_T_ISTR); + if (strlcpy(kstat_kv_istr(&ck->ck_device), ci->ci_dev->dv_xname, + sizeof(kstat_kv_istr(&ck->ck_device))) >= + sizeof(kstat_kv_istr(&ck->ck_device))) + panic("%s: devname too long", __func__); + + 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)); + + kstat_kv_init(&ck->ck_freq, "freq", KSTAT_KV_T_FREQ); + kstat_kv_init(&ck->ck_volts, "voltage", KSTAT_KV_T_VOLTS_DC); + + ks->ks_softc = ci; + ks->ks_data = ck; + ks->ks_datalen = sizeof(*ck); + ks->ks_read = cpu_kstat_read; + + kstat_install(ks); + + /* XXX should we have a ci->ci_kstat = ks? */ +} + +#endif /* NKSTAT > 0 */