? dev/pci/if_vmx.kstat Index: arch/amd64/amd64/conf.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/amd64/conf.c,v retrieving revision 1.70 diff -u -p -r1.70 conf.c --- arch/amd64/amd64/conf.c 25 May 2020 06:37:52 -0000 1.70 +++ arch/amd64/amd64/conf.c 23 Jun 2020 08:50:23 -0000 @@ -142,6 +142,7 @@ cdev_decl(cy); #include "pctr.h" #include "bktr.h" #include "ksyms.h" +#include "kstat.h" #include "usb.h" #include "uhid.h" #include "fido.h" @@ -238,7 +239,7 @@ struct cdevsw cdevsw[] = cdev_notdef(), /* 48 */ cdev_bktr_init(NBKTR,bktr), /* 49: Bt848 video capture device */ cdev_ksyms_init(NKSYMS,ksyms), /* 50: Kernel symbols device */ - cdev_notdef(), /* 51 */ + cdev_kstat_init(NKSTAT,kstat), /* 51: Kernel statistics */ cdev_midi_init(NMIDI,midi), /* 52: MIDI I/O */ cdev_notdef(), /* 53 was: sequencer I/O */ cdev_notdef(), /* 54 was: RAIDframe disk driver */ Index: arch/amd64/amd64/cpu.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/amd64/cpu.c,v retrieving revision 1.149 diff -u -p -r1.149 cpu.c --- arch/amd64/amd64/cpu.c 29 May 2020 04:42:23 -0000 1.149 +++ arch/amd64/amd64/cpu.c 23 Jun 2020 08:50:23 -0000 @@ -69,6 +69,7 @@ #include "vmm.h" #include "pctr.h" #include "pvbus.h" +#include "kstat.h" #include #include @@ -113,6 +114,10 @@ #include #endif +#if NKSTAT > 0 +#include +#endif + #include #include #include @@ -725,6 +730,56 @@ replacexsave(void) splx(s); } +#if NKSTAT > 0 +struct cpu_perf_kstat { + struct kstat_kv kp_tsc; + struct kstat_kv kp_aperf; + struct kstat_kv kp_mperf; +}; + +int +cpu_perf_read(struct kstat *ks) +{ + struct cpu_perf_kstat *kp = ks->ks_data; + + kp->kp_tsc.kv_v.v_u64 = rdtsc(); + + nanouptime(&ks->ks_updated); + + return (0); +} + +void +cpu_init_kstat(struct cpu_info *ci) +{ + struct kstat *ks; + struct cpu_perf_kstat *kp; + + ks = kstat_create("cpu", CPU_INFO_UNIT(ci), "perf", 0, KSTAT_T_KV, 0); + if (ks == NULL) { + printf("%s: unable to create kstats\n", ci->ci_dev->dv_xname); + return; + } + + kstat_set_cpu(ks, ci); + + kp = malloc(sizeof(*kp), M_DEVBUF, M_WAITOK|M_ZERO); + + kstat_kv_unit_init(&kp->kp_tsc, "tsc", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_CYCLES); + kstat_kv_unit_init(&kp->kp_aperf, "aperf", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_CYCLES); + kstat_kv_unit_init(&kp->kp_mperf, "mperf", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_CYCLES); + + ks->ks_data = kp; + ks->ks_datalen = sizeof(*kp); + ks->ks_read = cpu_perf_read; + + ci->ci_kstat = ks; + kstat_install(ks); +} +#endif /* NKSTAT > 0 */ /* * Initialize the processor appropriately. @@ -804,6 +859,10 @@ cpu_init(struct cpu_info *ci) /* Synchronize TSC */ if (cold && !CPU_IS_PRIMARY(ci)) tsc_sync_ap(ci); +#endif + +#if NKSTAT > 0 + cpu_init_kstat(ci); #endif } Index: arch/amd64/include/cpu.h =================================================================== RCS file: /cvs/src/sys/arch/amd64/include/cpu.h,v retrieving revision 1.137 diff -u -p -r1.137 cpu.h --- arch/amd64/include/cpu.h 3 Jun 2020 06:54:04 -0000 1.137 +++ arch/amd64/include/cpu.h 23 Jun 2020 08:50:23 -0000 @@ -90,6 +90,7 @@ union vmm_cpu_cap { }; struct x86_64_tss; +struct kstat; struct cpu_info { /* * The beginning of this structure in mapped in the userspace "u-k" @@ -208,6 +209,8 @@ struct cpu_info { struct vmxon_region *ci_vmxon_region; int64_t ci_tsc_skew; /* counter skew vs cpu0 */ + + struct kstat *ci_kstat; }; #define CPUF_BSP 0x0001 /* CPU is the original BSP */ Index: conf/GENERIC =================================================================== RCS file: /cvs/src/sys/conf/GENERIC,v retrieving revision 1.270 diff -u -p -r1.270 GENERIC --- conf/GENERIC 21 Jun 2020 12:30:07 -0000 1.270 +++ conf/GENERIC 23 Jun 2020 08:50:24 -0000 @@ -82,6 +82,7 @@ pseudo-device msts 1 # MSTS line discipl pseudo-device endrun 1 # EndRun line discipline pseudo-device vnd 4 # vnode disk devices pseudo-device ksyms 1 # kernel symbols device +pseudo-device kstat 1 # kernel statistics #pseudo-device dt # Dynamic Tracer # clonable devices Index: conf/files =================================================================== RCS file: /cvs/src/sys/conf/files,v retrieving revision 1.689 diff -u -p -r1.689 files --- conf/files 21 Jun 2020 12:14:48 -0000 1.689 +++ conf/files 23 Jun 2020 08:50:24 -0000 @@ -573,6 +573,9 @@ pseudo-device wg: ifnet pseudo-device ksyms file dev/ksyms.c ksyms needs-flag +pseudo-device kstat +file dev/kstat.c kstat needs-flag + pseudo-device fuse file miscfs/fuse/fuse_device.c fuse needs-flag file miscfs/fuse/fuse_file.c fuse Index: dev/kstat.c =================================================================== RCS file: dev/kstat.c diff -N dev/kstat.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dev/kstat.c 23 Jun 2020 08:50:24 -0000 @@ -0,0 +1,660 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2020 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +/* for kstat_set_cpu */ +#include +#include + +#include + +RBT_HEAD(kstat_id_tree, kstat); + +static inline int +kstat_id_cmp(const struct kstat *a, const struct kstat *b) +{ + if (a->ks_id > b->ks_id) + return (1); + if (a->ks_id < b->ks_id) + return (-1); + + return (0); +} + +RBT_PROTOTYPE(kstat_id_tree, kstat, ks_id_entry, kstat_id_cmp); + +RBT_HEAD(kstat_pv_tree, kstat); + +static inline int +kstat_pv_cmp(const struct kstat *a, const struct kstat *b) +{ + int rv; + + rv = strcmp(a->ks_provider, b->ks_provider); + if (rv != 0) + return (rv); + + if (a->ks_instance > b->ks_instance) + return (1); + if (a->ks_instance < b->ks_instance) + return (-1); + + rv = strcmp(a->ks_name, b->ks_name); + if (rv != 0) + return (rv); + + if (a->ks_unit > b->ks_unit) + return (1); + if (a->ks_unit < b->ks_unit) + return (-1); + + return (0); +} + +RBT_PROTOTYPE(kstat_pv_tree, kstat, ks_pv_entry, kstat_pv_cmp); + +RBT_HEAD(kstat_nm_tree, kstat); + +static inline int +kstat_nm_cmp(const struct kstat *a, const struct kstat *b) +{ + int rv; + + rv = strcmp(a->ks_name, b->ks_name); + if (rv != 0) + return (rv); + + if (a->ks_unit > b->ks_unit) + return (1); + if (a->ks_unit < b->ks_unit) + return (-1); + + rv = strcmp(a->ks_provider, b->ks_provider); + if (rv != 0) + return (rv); + + if (a->ks_instance > b->ks_instance) + return (1); + if (a->ks_instance < b->ks_instance) + return (-1); + + return (0); +} + +RBT_PROTOTYPE(kstat_nm_tree, kstat, ks_nm_entry, kstat_nm_cmp); + +struct kstat_lock_ops { + void (*enter)(void *); + void (*leave)(void *); +}; + +#define kstat_enter(_ks) (_ks)->ks_lock_ops->enter((_ks)->ks_lock) +#define kstat_leave(_ks) (_ks)->ks_lock_ops->leave((_ks)->ks_lock) + +static const struct kstat_lock_ops kstat_rlock_ops = { + (void (*)(void *))rw_enter_read, + (void (*)(void *))rw_exit_read, +}; + +static const struct kstat_lock_ops kstat_wlock_ops = { + (void (*)(void *))rw_enter_write, + (void (*)(void *))rw_exit_write, +}; + +static const struct kstat_lock_ops kstat_mutex_ops = { + (void (*)(void *))mtx_enter, + (void (*)(void *))mtx_leave, +}; + +static void kstat_cpu_enter(void *); +static void kstat_cpu_leave(void *); + +static const struct kstat_lock_ops kstat_cpu_ops = { + kstat_cpu_enter, + kstat_cpu_leave, +}; + +static struct rwlock kstat_lock = RWLOCK_INITIALIZER("kstat"); + +/* + * The global state is versioned so changes to the set of kstats + * can be detected. This is an int so it can be read atomically on + * any arch, which is ridiculous optimisation, really. + */ +static unsigned int kstat_version = 0; + +/* + * kstat structures have a unique identifier so they can be found + * quickly. Identifiers are 64bit in the hope that it won't wrap + * during the runtime of a system. The identifiers start at 1 so that + * 0 can be used as the first value for userland to iterate with. + */ +static uint64_t kstat_next_id = 1; + +static struct kstat_id_tree kstat_id_tree = RBT_INITIALIZER(); +static struct kstat_pv_tree kstat_pv_tree = RBT_INITIALIZER(); +static struct kstat_nm_tree kstat_nm_tree = RBT_INITIALIZER(); +static struct pool kstat_pool; + +static struct rwlock kstat_default_lock = + RWLOCK_INITIALIZER("kstatlk"); + +static int kstat_read(struct kstat *); +static int kstat_copy(struct kstat *, void *); + +int +kstatattach(int num) +{ + /* XXX install system stats here */ + return (0); +} + +int +kstatopen(dev_t dev, int flag, int mode, struct proc *p) +{ + return (0); +} + +int +kstatclose(dev_t dev, int flag, int mode, struct proc *p) +{ + return (0); +} + +static int +kstatioc_enter(struct kstat_req *ksreq) +{ + int error; + + error = rw_enter(&kstat_lock, RW_READ | RW_INTR); + if (error != 0) + return (error); + + if (!ISSET(ksreq->ks_rflags, KSTATIOC_F_IGNVER) && + ksreq->ks_version != kstat_version) { + error = EBUSY; + goto error; + } + + return (0); + +error: + rw_exit(&kstat_lock); + return (error); +} + +#define sstrlcpy(_dst, _src) \ + (strlcpy((_dst), (_src), sizeof((_dst))) >= sizeof((_dst))) + +static int +kstatioc_leave(struct kstat_req *ksreq, struct kstat *ks) +{ + void *buf = NULL; + size_t klen = 0, ulen = 0; + struct timespec updated; + int error = 0; + + if (ks == NULL) { + error = ENOENT; + goto error; + } + + switch (ks->ks_state) { + case KSTAT_S_CREATED: + ksreq->ks_updated = ks->ks_created; + ksreq->ks_interval.tv_sec = 0; + ksreq->ks_interval.tv_nsec = 0; + ksreq->ks_datalen = 0; + ksreq->ks_dataver = 0; + break; + + case KSTAT_S_INSTALLED: + ksreq->ks_dataver = ks->ks_dataver; + ksreq->ks_interval = ks->ks_interval; + + if (ksreq->ks_data == NULL) { + /* userland doesn't want actual data, so shortcut */ + kstat_enter(ks); + ksreq->ks_datalen = ks->ks_datalen; + ksreq->ks_updated = ks->ks_updated; + kstat_leave(ks); + break; + } + + klen = ks->ks_datalen; /* KSTAT_F_REALLOC */ + buf = malloc(klen, M_TEMP, M_WAITOK|M_CANFAIL); + if (buf == NULL) { + error = ENOMEM; + goto error; + } + + kstat_enter(ks); + error = (*ks->ks_read)(ks); + if (error == 0) { + updated = ks->ks_updated; + + /* KSTAT_F_REALLOC */ + KASSERTMSG(ks->ks_datalen == klen, + "kstat doesnt support resized data yet"); + + error = (*ks->ks_copy)(ks, buf); + } + kstat_leave(ks); + + if (error != 0) + goto error; + + ulen = ksreq->ks_datalen; + ksreq->ks_datalen = klen; /* KSTAT_F_REALLOC */ + ksreq->ks_updated = updated; + break; + default: + panic("ks %p unexpected state %u", ks, ks->ks_state); + } + + ksreq->ks_version = kstat_version; + ksreq->ks_id = ks->ks_id; + + if (sstrlcpy(ksreq->ks_provider, ks->ks_provider) != 0) + panic("kstat provider string has grown"); + ksreq->ks_instance = ks->ks_instance; + if (sstrlcpy(ksreq->ks_name, ks->ks_name) != 0) + panic("kstat name string has grown"); + ksreq->ks_unit = ks->ks_unit; + + ksreq->ks_created = ks->ks_created; + ksreq->ks_type = ks->ks_type; + ksreq->ks_state = ks->ks_state; + +error: + rw_exit(&kstat_lock); + + if (buf != NULL) { + if (error == 0) + error = copyout(buf, ksreq->ks_data, min(klen, ulen)); + + free(buf, M_TEMP, klen); + } + + return (error); +} + +static int +kstatioc_find_id(struct kstat_req *ksreq) +{ + struct kstat *ks, key; + int error; + + error = kstatioc_enter(ksreq); + if (error != 0) + return (error); + + key.ks_id = ksreq->ks_id; + + ks = RBT_FIND(kstat_id_tree, &kstat_id_tree, &key); + + return (kstatioc_leave(ksreq, ks)); +} + +static int +kstatioc_nfind_id(struct kstat_req *ksreq) +{ + struct kstat *ks, key; + int error; + + error = kstatioc_enter(ksreq); + if (error != 0) + return (error); + + key.ks_id = ksreq->ks_id; + + ks = RBT_NFIND(kstat_id_tree, &kstat_id_tree, &key); + + return (kstatioc_leave(ksreq, ks)); +} + +static int +kstatioc_find_pv(struct kstat_req *ksreq) +{ + struct kstat *ks, key; + int error; + + error = kstatioc_enter(ksreq); + if (error != 0) + return (error); + + key.ks_provider = ksreq->ks_provider; + key.ks_instance = ksreq->ks_instance; + key.ks_name = ksreq->ks_name; + key.ks_unit = ksreq->ks_unit; + + ks = RBT_FIND(kstat_pv_tree, &kstat_pv_tree, &key); + + return (kstatioc_leave(ksreq, ks)); +} + +static int +kstatioc_nfind_pv(struct kstat_req *ksreq) +{ + struct kstat *ks, key; + int error; + + error = kstatioc_enter(ksreq); + if (error != 0) + return (error); + + key.ks_provider = ksreq->ks_provider; + key.ks_instance = ksreq->ks_instance; + key.ks_name = ksreq->ks_name; + key.ks_unit = ksreq->ks_unit; + + ks = RBT_NFIND(kstat_pv_tree, &kstat_pv_tree, &key); + + return (kstatioc_leave(ksreq, ks)); +} + +static int +kstatioc_find_nm(struct kstat_req *ksreq) +{ + struct kstat *ks, key; + int error; + + error = kstatioc_enter(ksreq); + if (error != 0) + return (error); + + key.ks_name = ksreq->ks_name; + key.ks_unit = ksreq->ks_unit; + key.ks_provider = ksreq->ks_provider; + key.ks_instance = ksreq->ks_instance; + + ks = RBT_FIND(kstat_nm_tree, &kstat_nm_tree, &key); + + return (kstatioc_leave(ksreq, ks)); +} + +static int +kstatioc_nfind_nm(struct kstat_req *ksreq) +{ + struct kstat *ks, key; + int error; + + error = kstatioc_enter(ksreq); + if (error != 0) + return (error); + + key.ks_name = ksreq->ks_name; + key.ks_unit = ksreq->ks_unit; + key.ks_provider = ksreq->ks_provider; + key.ks_instance = ksreq->ks_instance; + + ks = RBT_NFIND(kstat_nm_tree, &kstat_nm_tree, &key); + + return (kstatioc_leave(ksreq, ks)); +} + +int +kstatioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct kstat_req *ksreq = (struct kstat_req *)data; + int error = 0; + + KERNEL_UNLOCK(); + + switch (cmd) { + case KSTATIOC_VERSION: + *(unsigned int *)data = kstat_version; + break; + + case KSTATIOC_FIND_ID: + error = kstatioc_find_id(ksreq); + break; + case KSTATIOC_NFIND_ID: + error = kstatioc_nfind_id(ksreq); + break; + case KSTATIOC_FIND_PROVIDER: + error = kstatioc_find_pv(ksreq); + break; + case KSTATIOC_NFIND_PROVIDER: + error = kstatioc_nfind_pv(ksreq); + break; + case KSTATIOC_FIND_NAME: + error = kstatioc_find_nm(ksreq); + break; + case KSTATIOC_NFIND_NAME: + error = kstatioc_nfind_nm(ksreq); + break; + + default: + error = ENOTTY; + break; + } + + KERNEL_LOCK(); + + return (error); +} + +static void +kstat_init(void) +{ + static int initialized = 0; + + if (initialized) + return; + + pool_init(&kstat_pool, sizeof(struct kstat), 0, IPL_NONE, + PR_WAITOK | PR_RWLOCK, "kstatmem", NULL); + + initialized = 1; +} + +struct kstat * +kstat_create(const char *provider, unsigned int instance, + const char *name, unsigned int unit, + unsigned int type, unsigned int flags) +{ + struct kstat *ks, *oks; + + KASSERT(strlen(provider) <= KSTAT_STRLEN); + KASSERT(strlen(name) <= KSTAT_STRLEN); + + kstat_init(); + + ks = pool_get(&kstat_pool, PR_WAITOK|PR_ZERO); + + ks->ks_provider = provider; + ks->ks_instance = instance; + ks->ks_name = name; + ks->ks_unit = unit; + ks->ks_flags = flags; + ks->ks_type = type; + ks->ks_state = KSTAT_S_CREATED; + + getnanouptime(&ks->ks_created); + ks->ks_updated = ks->ks_created; + + ks->ks_lock = &kstat_default_lock; + ks->ks_lock_ops = &kstat_wlock_ops; + ks->ks_read = kstat_read; + ks->ks_copy = kstat_copy; + + rw_enter_write(&kstat_lock); + ks->ks_id = kstat_next_id; + + oks = RBT_INSERT(kstat_pv_tree, &kstat_pv_tree, ks); + if (oks == NULL) { + /* commit */ + kstat_next_id++; + kstat_version++; + + oks = RBT_INSERT(kstat_nm_tree, &kstat_nm_tree, ks); + if (oks != NULL) + panic("kstat name collision! (%llu)", ks->ks_id); + + oks = RBT_INSERT(kstat_id_tree, &kstat_id_tree, ks); + if (oks != NULL) + panic("kstat id collision! (%llu)", ks->ks_id); + } + rw_exit_write(&kstat_lock); + + if (oks != NULL) { + pool_put(&kstat_pool, ks); + return (NULL); + } + + return (ks); +} + +void +kstat_set_rlock(struct kstat *ks, struct rwlock *rwl) +{ + KASSERT(ks->ks_state == KSTAT_S_CREATED); + + ks->ks_lock = rwl; + ks->ks_lock_ops = &kstat_rlock_ops; +} + +void +kstat_set_wlock(struct kstat *ks, struct rwlock *rwl) +{ + KASSERT(ks->ks_state == KSTAT_S_CREATED); + + ks->ks_lock = rwl; + ks->ks_lock_ops = &kstat_wlock_ops; +} + +void +kstat_set_mutex(struct kstat *ks, struct mutex *mtx) +{ + KASSERT(ks->ks_state == KSTAT_S_CREATED); + + ks->ks_lock = mtx; + ks->ks_lock_ops = &kstat_mutex_ops; +} + +static void +kstat_cpu_enter(void *p) +{ + struct cpu_info *ci = p; + sched_peg_curproc(ci); +} + +static void +kstat_cpu_leave(void *p) +{ + atomic_clearbits_int(&curproc->p_flag, P_CPUPEG); +} + +void +kstat_set_cpu(struct kstat *ks, struct cpu_info *ci) +{ + KASSERT(ks->ks_state == KSTAT_S_CREATED); + + ks->ks_lock = ci; + ks->ks_lock_ops = &kstat_cpu_ops; +} + +int +kstat_read_nop(struct kstat *ks) +{ + return (0); +} + +void +kstat_install(struct kstat *ks) +{ + if (!ISSET(ks->ks_flags, KSTAT_F_REALLOC)) { + KASSERTMSG(ks->ks_copy != NULL || ks->ks_data != NULL, + "kstat %s:%u:%s:%u must provide ks_copy or ks_data", + ks->ks_provider, ks->ks_instance, ks->ks_name, ks->ks_unit); + KASSERT(ks->ks_datalen > 0); + } + + rw_enter_write(&kstat_lock); + ks->ks_state = KSTAT_S_INSTALLED; + rw_exit_write(&kstat_lock); +} + +void +kstat_destroy(struct kstat *ks) +{ + rw_enter_write(&kstat_lock); + RBT_REMOVE(kstat_id_tree, &kstat_id_tree, ks); + RBT_REMOVE(kstat_pv_tree, &kstat_pv_tree, ks); + RBT_REMOVE(kstat_nm_tree, &kstat_nm_tree, ks); + kstat_version++; + rw_exit_write(&kstat_lock); + + pool_put(&kstat_pool, ks); +} + +static int +kstat_read(struct kstat *ks) +{ + getnanouptime(&ks->ks_updated); + return (0); +} + +static int +kstat_copy(struct kstat *ks, void *buf) +{ + memcpy(buf, ks->ks_data, ks->ks_datalen); + return (0); +} + +RBT_GENERATE(kstat_id_tree, kstat, ks_id_entry, kstat_id_cmp); +RBT_GENERATE(kstat_pv_tree, kstat, ks_pv_entry, kstat_pv_cmp); +RBT_GENERATE(kstat_nm_tree, kstat, ks_nm_entry, kstat_nm_cmp); + +void +kstat_kv_init(struct kstat_kv *kv, const char *name, enum kstat_kv_type type) +{ + memset(kv, 0, sizeof(*kv)); + strlcpy(kv->kv_key, name, sizeof(kv->kv_key)); /* XXX truncated? */ + kv->kv_type = type; + kv->kv_unit = KSTAT_KV_U_NONE; +} + +void +kstat_kv_unit_init(struct kstat_kv *kv, const char *name, + enum kstat_kv_type type, enum kstat_kv_unit unit) +{ + switch (type) { + case KSTAT_KV_T_COUNTER64: + case KSTAT_KV_T_COUNTER32: + case KSTAT_KV_T_UINT64: + case KSTAT_KV_T_INT64: + case KSTAT_KV_T_UINT32: + case KSTAT_KV_T_INT32: + break; + default: + panic("kv unit init %s: unit for non-integer type", name); + } + + memset(kv, 0, sizeof(*kv)); + strlcpy(kv->kv_key, name, sizeof(kv->kv_key)); /* XXX truncated? */ + kv->kv_type = type; + kv->kv_unit = unit; +} Index: net/ifq.c =================================================================== RCS file: /cvs/src/sys/net/ifq.c,v retrieving revision 1.40 diff -u -p -r1.40 ifq.c --- net/ifq.c 17 Jun 2020 06:45:22 -0000 1.40 +++ net/ifq.c 23 Jun 2020 08:50:25 -0000 @@ -17,6 +17,7 @@ */ #include "bpfilter.h" +#include "kstat.h" #include #include @@ -32,6 +33,10 @@ #include #endif +#if NKSTAT > 0 +#include +#endif + /* * priq glue */ @@ -122,7 +127,10 @@ ifq_is_serialized(struct ifqueue *ifq) void ifq_start(struct ifqueue *ifq) { - if (ifq_len(ifq) >= min(ifq->ifq_if->if_txmit, ifq->ifq_maxlen)) { + struct ifnet *ifp = ifq->ifq_if; + + if (ISSET(ifp->if_xflags, IFXF_MPSAFE) && + ifq_len(ifq) >= min(ifp->if_txmit, ifq->ifq_maxlen)) { task_del(ifq->ifq_softnet, &ifq->ifq_bundle); ifq_run_start(ifq); } else @@ -188,11 +196,42 @@ ifq_barrier_task(void *p) * ifqueue mbuf queue API */ +#if NKSTAT > 0 +struct ifq_kstat_data { + struct kstat_kv kd_packets; + struct kstat_kv kd_bytes; + struct kstat_kv kd_qdrops; + struct kstat_kv kd_errors; + struct kstat_kv kd_qlen; + struct kstat_kv kd_maxqlen; + struct kstat_kv kd_oactive; +}; + +static const struct ifq_kstat_data ifq_kstat_tpl = { + KSTAT_KV_UNIT_INITIALIZER("packets", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS), + KSTAT_KV_UNIT_INITIALIZER("bytes", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_BYTES), + KSTAT_KV_UNIT_INITIALIZER("qdrops", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS), + KSTAT_KV_UNIT_INITIALIZER("errors", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS), + KSTAT_KV_UNIT_INITIALIZER("qlen", + KSTAT_KV_T_UINT32, KSTAT_KV_U_PACKETS), + KSTAT_KV_UNIT_INITIALIZER("maxqlen", + KSTAT_KV_T_UINT32, KSTAT_KV_U_PACKETS), + KSTAT_KV_INITIALIZER("oactive", KSTAT_KV_T_BOOL), +}; + +static int ifq_kstat_copy(struct kstat *, void *); +#endif + void ifq_init(struct ifqueue *ifq, struct ifnet *ifp, unsigned int idx) { ifq->ifq_if = ifp; - ifq->ifq_softnet = net_tq(ifp->if_index); /* + idx */ + ifq->ifq_softnet = ISSET(ifp->if_xflags, IFXF_MPSAFE) ? + net_tq(ifp->if_index /* + idx */) : systq; ifq->ifq_softc = NULL; mtx_init(&ifq->ifq_mtx, IPL_NET); @@ -222,6 +261,18 @@ ifq_init(struct ifqueue *ifq, struct ifn ifq_set_maxlen(ifq, IFQ_MAXLEN); ifq->ifq_idx = idx; + +#if NKSTAT > 0 + /* XXX xname vs driver name and unit */ + ifq->ifq_kstat = kstat_create(ifp->if_xname, 0, + "txq", ifq->ifq_idx, KSTAT_T_KV, 0); + KASSERT(ifq->ifq_kstat != NULL); + kstat_set_mutex(ifq->ifq_kstat, &ifq->ifq_mtx); + ifq->ifq_kstat->ks_softc = ifq; + ifq->ifq_kstat->ks_datalen = sizeof(ifq_kstat_tpl); + ifq->ifq_kstat->ks_copy = ifq_kstat_copy; + kstat_install(ifq->ifq_kstat); +#endif } void @@ -265,6 +316,10 @@ ifq_destroy(struct ifqueue *ifq) { struct mbuf_list ml = MBUF_LIST_INITIALIZER(); +#if NKSTAT > 0 + kstat_destroy(ifq->ifq_kstat); +#endif + NET_ASSERT_UNLOCKED(); if (!task_del(ifq->ifq_softnet, &ifq->ifq_bundle)) taskq_barrier(ifq->ifq_softnet); @@ -289,6 +344,26 @@ ifq_add_data(struct ifqueue *ifq, struct mtx_leave(&ifq->ifq_mtx); } +#if NKSTAT > 0 +static int +ifq_kstat_copy(struct kstat *ks, void *dst) +{ + struct ifqueue *ifq = ks->ks_softc; + struct ifq_kstat_data *kd = dst; + + *kd = ifq_kstat_tpl; + kd->kd_packets.kv_v.v_u64 = ifq->ifq_packets; + kd->kd_bytes.kv_v.v_u64 = ifq->ifq_bytes; + kd->kd_qdrops.kv_v.v_u64 = ifq->ifq_qdrops; + kd->kd_errors.kv_v.v_u64 = ifq->ifq_errors; + kd->kd_qlen.kv_v.v_u32 = ifq->ifq_len; + kd->kd_maxqlen.kv_v.v_u32 = ifq->ifq_maxlen; + kd->kd_oactive.kv_v.v_bool = ifq->ifq_oactive; + + return (0); +} +#endif + int ifq_enqueue(struct ifqueue *ifq, struct mbuf *m) { @@ -505,6 +580,31 @@ ifq_mfreeml(struct ifqueue *ifq, struct * ifiq */ +#if NKSTAT > 0 +struct ifiq_kstat_data { + struct kstat_kv kd_packets; + struct kstat_kv kd_bytes; + struct kstat_kv kd_qdrops; + struct kstat_kv kd_errors; + struct kstat_kv kd_qlen; +}; + +static const struct ifiq_kstat_data ifiq_kstat_tpl = { + KSTAT_KV_UNIT_INITIALIZER("packets", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS), + KSTAT_KV_UNIT_INITIALIZER("bytes", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_BYTES), + KSTAT_KV_UNIT_INITIALIZER("qdrops", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS), + KSTAT_KV_UNIT_INITIALIZER("errors", + KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS), + KSTAT_KV_UNIT_INITIALIZER("qlen", + KSTAT_KV_T_UINT32, KSTAT_KV_U_PACKETS), +}; + +static int ifiq_kstat_copy(struct kstat *, void *); +#endif + static void ifiq_process(void *); void @@ -525,11 +625,27 @@ ifiq_init(struct ifiqueue *ifiq, struct ifiq->ifiq_errors = 0; ifiq->ifiq_idx = idx; + +#if NKSTAT > 0 + /* XXX xname vs driver name and unit */ + ifiq->ifiq_kstat = kstat_create(ifp->if_xname, 0, + "rxq", ifiq->ifiq_idx, KSTAT_T_KV, 0); + KASSERT(ifiq->ifiq_kstat != NULL); + kstat_set_mutex(ifiq->ifiq_kstat, &ifiq->ifiq_mtx); + ifiq->ifiq_kstat->ks_softc = ifiq; + ifiq->ifiq_kstat->ks_datalen = sizeof(ifiq_kstat_tpl); + ifiq->ifiq_kstat->ks_copy = ifiq_kstat_copy; + kstat_install(ifiq->ifiq_kstat); +#endif } void ifiq_destroy(struct ifiqueue *ifiq) { +#if NKSTAT > 0 + kstat_destroy(ifiq->ifiq_kstat); +#endif + NET_ASSERT_UNLOCKED(); if (!task_del(ifiq->ifiq_softnet, &ifiq->ifiq_task)) taskq_barrier(ifiq->ifiq_softnet); @@ -616,6 +732,24 @@ ifiq_add_data(struct ifiqueue *ifiq, str data->ifi_iqdrops += ifiq->ifiq_qdrops; mtx_leave(&ifiq->ifiq_mtx); } + +#if NKSTAT > 0 +static int +ifiq_kstat_copy(struct kstat *ks, void *dst) +{ + struct ifiqueue *ifiq = ks->ks_softc; + struct ifiq_kstat_data *kd = dst; + + *kd = ifiq_kstat_tpl; + kd->kd_packets.kv_v.v_u64 = ifiq->ifiq_packets; + kd->kd_bytes.kv_v.v_u64 = ifiq->ifiq_bytes; + kd->kd_qdrops.kv_v.v_u64 = ifiq->ifiq_qdrops; + kd->kd_errors.kv_v.v_u64 = ifiq->ifiq_errors; + kd->kd_qlen.kv_v.v_u32 = ml_len(&ifiq->ifiq_ml); + + return (0); +} +#endif int ifiq_enqueue(struct ifiqueue *ifiq, struct mbuf *m) Index: net/ifq.h =================================================================== RCS file: /cvs/src/sys/net/ifq.h,v retrieving revision 1.31 diff -u -p -r1.31 ifq.h --- net/ifq.h 22 May 2020 07:02:24 -0000 1.31 +++ net/ifq.h 23 Jun 2020 08:50:25 -0000 @@ -20,6 +20,7 @@ #define _NET_IFQ_H_ struct ifnet; +struct kstat; struct ifq_ops; @@ -54,6 +55,8 @@ struct ifqueue { uint64_t ifq_errors; uint64_t ifq_mcasts; + struct kstat *ifq_kstat; + /* work serialisation */ struct mutex ifq_task_mtx; struct task_list ifq_task_list; @@ -91,6 +94,8 @@ struct ifiqueue { uint64_t ifiq_errors; uint64_t ifiq_mcasts; uint64_t ifiq_noproto; + + struct kstat *ifiq_kstat; /* properties */ unsigned int ifiq_idx; Index: sys/conf.h =================================================================== RCS file: /cvs/src/sys/sys/conf.h,v retrieving revision 1.152 diff -u -p -r1.152 conf.h --- sys/conf.h 26 May 2020 07:53:00 -0000 1.152 +++ sys/conf.h 23 Jun 2020 08:50:26 -0000 @@ -328,6 +328,13 @@ extern struct cdevsw cdevsw[]; (dev_type_stop((*))) enodev, 0, seltrue, \ (dev_type_mmap((*))) enodev, 0, 0, seltrue_kqfilter } +/* open, close, ioctl */ +#define cdev_kstat_init(c,n) { \ + dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \ + (dev_type_write((*))) enodev, dev_init(c,n,ioctl), \ + (dev_type_stop((*))) enodev, 0, selfalse, \ + (dev_type_mmap((*))) enodev } + /* open, close, read, write, ioctl, stop, tty, poll, mmap, kqfilter */ #define cdev_wsdisplay_init(c,n) { \ dev_init(c,n,open), dev_init(c,n,close), dev_init(c,n,read), \ @@ -605,6 +612,7 @@ cdev_decl(wsmouse); cdev_decl(wsmux); cdev_decl(ksyms); +cdev_decl(kstat); cdev_decl(bio); cdev_decl(vscsi); Index: sys/kstat.h =================================================================== RCS file: sys/kstat.h diff -N sys/kstat.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/kstat.h 23 Jun 2020 08:50:26 -0000 @@ -0,0 +1,197 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2020 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SYS_KSTAT_H_ +#define _SYS_KSTAT_H_ + +#include + +#define KSTAT_STRLEN 32 + +#define KSTAT_T_RAW 0 +#define KSTAT_T_KV 1 +#define KSTAT_T_COUNTERS 2 + +struct kstat_req { + unsigned int ks_rflags; +#define KSTATIOC_F_IGNVER (1 << 0) + /* the current version of the kstat subsystem */ + unsigned int ks_version; + + uint64_t ks_id; + + char ks_provider[KSTAT_STRLEN]; + unsigned int ks_instance; + char ks_name[KSTAT_STRLEN]; + unsigned int ks_unit; + + struct timespec ks_created; + struct timespec ks_updated; + struct timespec ks_interval; + unsigned int ks_type; + unsigned int ks_state; + + void *ks_data; + size_t ks_datalen; + unsigned int ks_dataver; +}; + +/* ioctls */ + +#define KSTATIOC_VERSION _IOR('k', 1, unsigned int) +#define KSTATIOC_FIND_ID _IOWR('k', 2, struct kstat_req) +#define KSTATIOC_NFIND_ID _IOWR('k', 3, struct kstat_req) +#define KSTATIOC_FIND_PROVIDER _IOWR('k', 4, struct kstat_req) +#define KSTATIOC_NFIND_PROVIDER _IOWR('k', 5, struct kstat_req) +#define KSTATIOC_FIND_NAME _IOWR('k', 6, struct kstat_req) +#define KSTATIOC_NFIND_NAME _IOWR('k', 7, struct kstat_req) + +/* named data */ + +#define KSTAT_KV_NAMELEN 16 +#define KSTAT_KV_ALIGN sizeof(uint64_t) + +enum kstat_kv_type { + KSTAT_KV_T_NULL, + KSTAT_KV_T_BOOL, + KSTAT_KV_T_COUNTER64, + KSTAT_KV_T_COUNTER32, + KSTAT_KV_T_UINT64, + KSTAT_KV_T_INT64, + KSTAT_KV_T_UINT32, + KSTAT_KV_T_INT32, + KSTAT_KV_T_ISTR, /* inline string */ + KSTAT_KV_T_STR, /* trailing string */ + KSTAT_KV_T_BYTES, /* trailing bytes */ +}; + +/* units only apply to integer types */ +enum kstat_kv_unit { + KSTAT_KV_U_NONE = 0, + KSTAT_KV_U_PACKETS, /* packets */ + KSTAT_KV_U_BYTES, /* bytes */ + KSTAT_KV_U_CYCLES, /* cycles */ + KSTAT_KV_U_TEMP, /* temperature (uK) */ + KSTAT_KV_U_FANRPM, /* fan revolution speed */ + KSTAT_KV_U_VOLTS_DC, /* voltage (uV DC) */ + KSTAT_KV_U_VOLTS_AC, /* voltage (uV AC) */ + KSTAT_KV_U_OHMS, /* resistance */ + KSTAT_KV_U_WATTS, /* power (uW) */ + KSTAT_KV_U_AMPS, /* current (uA) */ + KSTAT_KV_U_WATTHOUR, /* power capacity (uWh) */ + KSTAT_KV_U_AMPHOUR, /* power capacity (uAh) */ + KSTAT_KV_U_PERCENT, /* percent (m%) */ + KSTAT_KV_U_LUX, /* illuminance (ulx) */ + KSTAT_KV_U_TIMEDELTA, /* system time error (nSec) */ + KSTAT_KV_U_HUMIDITY, /* humidity (m%RH) */ + KSTAT_KV_U_FREQ, /* frequency (uHz) */ + KSTAT_KV_U_ANGLE, /* angle (uDegrees) */ + KSTAT_KV_U_DISTANCE, /* distance (uMeter) */ + KSTAT_KV_U_PRESSURE, /* pressure (mPa) */ + KSTAT_KV_U_ACCEL, /* acceleration (u m/s^2) */ + KSTAT_KV_U_VELOCITY, /* velocity (u m/s) */ +}; + +struct kstat_kv { + char kv_key[KSTAT_KV_NAMELEN]; + union { + char v_istr[16]; + unsigned int v_bool; + uint64_t v_u64; + int64_t v_s64; + uint32_t v_u32; + int32_t v_s32; + size_t v_len; + } kv_v; + enum kstat_kv_type kv_type; + enum kstat_kv_unit kv_unit; +} __aligned(KSTAT_KV_ALIGN); + +#define KSTAT_KV_UNIT_INITIALIZER(_key, _type, _unit) { \ + .kv_key = (_key), \ + .kv_type = (_type), \ + .kv_unit = (_unit), \ +} + +#define KSTAT_KV_INITIALIZER(_key, _type) \ + KSTAT_KV_UNIT_INITIALIZER((_key), (_type), KSTAT_KV_U_NONE) + +void kstat_kv_init(struct kstat_kv *, const char *, enum kstat_kv_type); +void kstat_kv_unit_init(struct kstat_kv *, const char *, + enum kstat_kv_type, enum kstat_kv_unit); + +#ifdef _KERNEL + +#include + +struct kstat_lock_ops; + +struct kstat { + uint64_t ks_id; + + const char *ks_provider; + unsigned int ks_instance; + const char *ks_name; + unsigned int ks_unit; + + unsigned int ks_type; + unsigned int ks_flags; +#define KSTAT_F_REALLOC (1 << 0) + unsigned int ks_state; +#define KSTAT_S_CREATED 0 +#define KSTAT_S_INSTALLED 1 + + struct timespec ks_created; + RBT_ENTRY(kstat) ks_id_entry; + RBT_ENTRY(kstat) ks_pv_entry; + RBT_ENTRY(kstat) ks_nm_entry; + + /* the driver can update these between kstat creation and install */ + unsigned int ks_dataver; + void *ks_softc; + int (*ks_read)(struct kstat *); + int (*ks_copy)(struct kstat *, void *); + + const struct kstat_lock_ops * + ks_lock_ops; + void *ks_lock; + + /* the data that is updated by ks_read */ + void *ks_data; + size_t ks_datalen; + struct timespec ks_updated; + struct timespec ks_interval; +}; + +struct kstat *kstat_create(const char *, unsigned int, + const char *, unsigned int, + unsigned int, unsigned int); + +void kstat_set_rlock(struct kstat *, struct rwlock *); +void kstat_set_wlock(struct kstat *, struct rwlock *); +void kstat_set_mutex(struct kstat *, struct mutex *); +void kstat_set_cpu(struct kstat *, struct cpu_info *); + +int kstat_read_nop(struct kstat *); + +void kstat_install(struct kstat *); +void kstat_destroy(struct kstat *); + +#endif /* _KERNEL */ + +#endif /* _SYS_KSTAT_H_ */