Module Name: src Committed By: simonb Date: Thu Apr 1 06:22:10 UTC 2021
Modified Files: src/sys/kern: subr_hash.c src/sys/sys: sysctl.h Log Message: Add support for kernel hash statistics generation for vmstat -h/-H. As well as not needing any kmem grovelling, also much faster as it doesn't need a kmem read for hash bucket and for each item in the hash bucket chains. To generate a diff of this commit: cvs rdiff -u -r1.7 -r1.8 src/sys/kern/subr_hash.c cvs rdiff -u -r1.231 -r1.232 src/sys/sys/sysctl.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/kern/subr_hash.c diff -u src/sys/kern/subr_hash.c:1.7 src/sys/kern/subr_hash.c:1.8 --- src/sys/kern/subr_hash.c:1.7 Wed Jul 6 05:20:48 2016 +++ src/sys/kern/subr_hash.c Thu Apr 1 06:22:09 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: subr_hash.c,v 1.7 2016/07/06 05:20:48 ozaki-r Exp $ */ +/* $NetBSD: subr_hash.c,v 1.8 2021/04/01 06:22:09 simonb Exp $ */ /* * Copyright (c) 1982, 1986, 1991, 1993 @@ -37,13 +37,17 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: subr_hash.c,v 1.7 2016/07/06 05:20:48 ozaki-r Exp $"); +__KERNEL_RCSID(0, "$NetBSD: subr_hash.c,v 1.8 2021/04/01 06:22:09 simonb Exp $"); #include <sys/param.h> #include <sys/bitops.h> #include <sys/kmem.h> #include <sys/systm.h> #include <sys/pslist.h> +#include <sys/rwlock.h> +#include <sys/sysctl.h> + +static int hashstat_sysctl(SYSCTLFN_PROTO); static size_t hash_list_size(enum hashtype htype) @@ -138,3 +142,119 @@ hashdone(void *hashtbl, enum hashtype ht const size_t esize = hash_list_size(htype); kmem_free(hashtbl, esize * (hashmask + 1)); } + +/* + * Support for hash statistics (vmstat -H / vmstat -h hashname). + */ + +struct hashstat { + const char *hs_name; + hashstat_func_t hs_func; + TAILQ_ENTRY(hashstat) hs_next; +}; +TAILQ_HEAD(, hashstat) hashstat_list = + TAILQ_HEAD_INITIALIZER(hashstat_list); +static krwlock_t hashstat_lock; + +void +hashstat_register(const char *name, hashstat_func_t func) +{ + struct hashstat *hs; + + hs = kmem_alloc(sizeof(*hs), KM_SLEEP); + + hs->hs_name = name; + hs->hs_func = func; + + rw_enter(&hashstat_lock, RW_WRITER); + TAILQ_INSERT_TAIL(&hashstat_list, hs, hs_next); + rw_exit(&hashstat_lock); +} + +/* + * sysctl support for returning kernel hash statistics. + * + * We (ab)use CTL_DESCRIBE and CTL_QUERY: + * When passed an OID of CTL_DESCRIBE, return a list and description + * of the available hashes. + * When passed an OID of CTL_QUERY, use the hash name passed in the + * "new" hash input as the name of a single hash to return stats on. + */ +static int +hashstat_sysctl(SYSCTLFN_ARGS) +{ + struct hashstat_sysctl hs; + struct hashstat *hash; + char queryname[SYSCTL_NAMELEN]; + size_t written; + bool fill, query; + int error; + + if (oldp == NULL) { + *oldlenp = 0; + TAILQ_FOREACH(hash, &hashstat_list, hs_next) + *oldlenp += sizeof(hs); + return 0; + } + + error = 0; + written = 0; + + if (namelen > 0 && name[0] == CTL_DESCRIBE) + fill = false; + else + fill = true; + + if (namelen > 0 && name[0] == CTL_QUERY) { + const struct hashstat_sysctl *h = newp; + + if (h == NULL) { + /* Can't QUERY one hash without supplying the hash name. */ + return EINVAL; + } + query = true; + h = newp; + strlcpy(queryname, h->hash_name, sizeof(queryname)); + } else { + query = false; + } + + sysctl_unlock(); + rw_enter(&hashstat_lock, RW_READER); + TAILQ_FOREACH(hash, &hashstat_list, hs_next) { + if (query && + (strncmp(hash->hs_name, queryname, sizeof(hash->hs_name)) != 0)) { + continue; + } + + memset(&hs, 0, sizeof(hs)); + error = hash->hs_func(&hs, fill); + if (error) + break; + + error = sysctl_copyout(l, &hs, oldp, sizeof(hs)); + if (error) + break; + written += sizeof(hs); + oldp = (char *)oldp + sizeof(hs); + } + rw_exit(&hashstat_lock); + sysctl_relock(); + + *oldlenp = written; + return error; +} + + +SYSCTL_SETUP(sysctl_hash_setup, "sysctl hash stats setup") +{ + + rw_init(&hashstat_lock); /* as good a place as any for this */ + + sysctl_createv(NULL, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_STRUCT, + "hashstat", SYSCTL_DESCR("kernel hash statistics"), + hashstat_sysctl, 0, NULL, 0, + CTL_KERN, CTL_CREATE, CTL_EOL); +} Index: src/sys/sys/sysctl.h diff -u src/sys/sys/sysctl.h:1.231 src/sys/sys/sysctl.h:1.232 --- src/sys/sys/sysctl.h:1.231 Sat Oct 17 09:06:15 2020 +++ src/sys/sys/sysctl.h Thu Apr 1 06:22:10 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: sysctl.h,v 1.231 2020/10/17 09:06:15 mlelstv Exp $ */ +/* $NetBSD: sysctl.h,v 1.232 2021/04/01 06:22:10 simonb Exp $ */ /* * Copyright (c) 1989, 1993 @@ -727,6 +727,28 @@ struct evcnt_sysctl { #define KERN_EVCNT_COUNT_ANY 0 #define KERN_EVCNT_COUNT_NONZERO 1 + +/* + * kern.hashstat returns an array of these structures, which are designed + * to be immune to 32/64 bit emulation issues. + * + * Hash users can register a filler function to fill the hashstat_sysctl + * which can then be exposed via vmstat(1). + * + * See comments for hashstat_sysctl() in kern/subr_hash.c for details + * on sysctl(3) usage. + */ +struct hashstat_sysctl { + char hash_name[SYSCTL_NAMELEN]; + char hash_desc[SYSCTL_NAMELEN]; + uint64_t hash_size; + uint64_t hash_used; + uint64_t hash_items; + uint64_t hash_maxchain; +}; +typedef int (*hashstat_func_t)(struct hashstat_sysctl *, bool); +void hashstat_register(const char *, hashstat_func_t); + /* * CTL_VM identifiers in <uvm/uvm_param.h> */