> On 14 Jun 2017, at 13:50, David Gwynne <[email protected]> wrote:
>
> i have a few things left to do in the pools per cpu caches, one of
> which is make their activity visibile. to that end, here's a diff
> provides a way for userland to request stats from the per cpu caches,
> and uses that in systat so you can watch them.
>
> there are two added pool sysctls. one copies an array of stats from
> each cpus cache. the interesting bits in those stats are how many
> items each cpu handled, and how many list operations the cpu did
> against the global pool cache.
>
> the second sysctl reports stats about the global pool cache. currently
> this is the target for the list length the cpus build is, how many
> lists its holding, and how many times the gc has moved a list of
> items back into the pool for recovery.
>
> these are used by sysctl for a new view which ive called pcaches,
> short for pool caches.
>
> ksh: Fmt: not found
har. this was supposed to say that an example of the output is below. this is
an 8 way box after running tcpbench for a bit.
>
> 2 users Load 1.42 0.78 0.35 (1-88 of 158) v880.embarrassm.net 13:48:40
>
> NAME LEN NL NGC CPU REQ REL LREQ
> LREL
> mbufpl 8 72 0 0 955967 2622839 794
> 209151
> 1 907108 362300 88367
> 20266
> 2 915933 356927 90031
> 20155
> 3 878324 339160 86646
> 19250
> 4 30620 7851 3301
> 454
> 5 1401 885 117
> 52
> 6 56 57 0 > 0
> 7 37 45 0 > 0
> mtagpl 8 0 0 0 0 0 0
> 0
> 1 0 0 0 > 0
> 2 0 0 0 > 0
> 3 0 0 0 > 0
> 4 0 0 0 > 0
> 5 0 0 0 > 0
> 6 0 0 0 > 0
> 7 0 0 0 > 0
> mcl2k 8 32 0 0 944634 7776 117605
> 497
> 1 122 318421 0
> 39786
> 2 114 313498 0
> 39171
> 3 105 298051 0
> 37242
> 4 22 6834 0
> 850
> 5 0 744 0
> 91
> 6 0 1 0 > 0
> 7 1 3 0 > 0
> mcl2k2 8 0 0 0 0 0 0
> 0
> 1 0 0 0 > 0
> 2 0 0 0 > 0
> 3 0 0 0 > 0
> 4 0 0 0 > 0
> 5 0 0 0 > 0
> 6 0 0 0 > 0
> 7 0 0 0 > 0
> mcl4k 8 2 0 0 20 44 1
> 2
> 1 1978 2072 85
> 96
> 2 2100 1988 97
> 83
> 3 1910 1986 83
> 91
> 4 87 61 6
> 2
> 5 1 9 0 > 0
> 6 0 0 0 > 0
> 7 0 0 0 > 0
> mcl8k 8 17 0 0 97 956 11
> 117
> 1 19517 20418 2254
> 2366
> 2 20934 20704 2441
> 2412
> 3 20294 19734 2347
> 2275
> 4 1234 399 150
> 45
> 5 0 54 0
> 5
> 6 0 0 0 > 0
> 7 0 0 0 > 0
> mcl9k 8 0 0 0 1 5 0
> 0
> 1 145 176 2
> 4
> 2 156 162 2
> 2
> 3 159 154 1 > 0
> 4 3 2 1 > 0
> 5 0 1 0 > 0
> 6 0 0 0 > 0
> 7 0 0 0 > 0
> mcl12k 8 2 0 0 2 8 0
> 0
> 1 271 294 6
> 8
> 2 302 256 8
> 2
> 3 223 294 1
> 9
> 4 20 4 2 > 0
> 5 0 2 0 > 0
> 6 0 0 0 > 0
> 7 0 0 0 > 0
> mcl16k 8 2 0 0 7 34 0
> 2
> 1 1108 1137 39
> 42
> 2 1095 1141 40
> 45
> 3 1021 998 39
> 35
> 4 51 27 4 > 0
> 5 1 3 0 > 0
> 6 0 0 0 > 0
> 7 0 0 0 > 0
> mcl64k 8 3 0 0 68 327 3
> 34
> 1 17492 18495 1694
> 1819
> 2 18369 18135 1842
> 1812
> 3 18017 17252 1808
> 1711
> 4 618 409 69
> 41
> 5 22 41 1
> 3
> 6 0 0 0 > 0
> 7 0 0 0 > 0
>
>
> the systat view could be improved, but id prefer to do that in tree.
>
> ok?
>
> Index: sys/kern/subr_pool.c
> ===================================================================
> RCS file: /cvs/src/sys/kern/subr_pool.c,v
> retrieving revision 1.209
> diff -u -p -r1.209 subr_pool.c
> --- sys/kern/subr_pool.c 13 Jun 2017 11:41:11 -0000 1.209
> +++ sys/kern/subr_pool.c 14 Jun 2017 03:13:47 -0000
> @@ -122,9 +122,12 @@ struct pool_cache {
> struct pool_cache_item *pc_prev; /* previous list of items */
>
> uint64_t pc_gen; /* generation number */
> - uint64_t pc_gets;
> - uint64_t pc_puts;
> - uint64_t pc_fails;
> + uint64_t pc_nget; /* # of successful requests */
> + uint64_t pc_nfail; /* # of unsuccessful reqs */
> + uint64_t pc_nput; /* # of releases */
> + uint64_t pc_nlget; /* # of list requests */
> + uint64_t pc_nlfail; /* # of fails getting a list */
> + uint64_t pc_nlput; /* # of list releases */
>
> int pc_nout;
> };
> @@ -133,7 +136,9 @@ void *pool_cache_get(struct pool *);
> void pool_cache_put(struct pool *, void *);
> void pool_cache_destroy(struct pool *);
> #endif
> -void pool_cache_info(struct pool *, struct kinfo_pool *);
> +void pool_cache_pool_info(struct pool *, struct kinfo_pool *);
> +int pool_cache_info(struct pool *, void *, size_t *);
> +int pool_cache_cpus_info(struct pool *, void *, size_t *);
>
> #ifdef POOL_DEBUG
> int pool_debug = 1;
> @@ -1379,6 +1384,8 @@ sysctl_dopool(int *name, u_int namelen,
>
> case KERN_POOL_NAME:
> case KERN_POOL_POOL:
> + case KERN_POOL_CACHE:
> + case KERN_POOL_CACHE_CPUS:
> break;
> default:
> return (EOPNOTSUPP);
> @@ -1423,10 +1430,18 @@ sysctl_dopool(int *name, u_int namelen,
> pi.pr_nidle = pp->pr_nidle;
> mtx_leave(&pp->pr_mtx);
>
> - pool_cache_info(pp, &pi);
> + pool_cache_pool_info(pp, &pi);
>
> rv = sysctl_rdstruct(oldp, oldlenp, NULL, &pi, sizeof(pi));
> break;
> +
> + case KERN_POOL_CACHE:
> + rv = pool_cache_info(pp, oldp, oldlenp);
> + break;
> +
> + case KERN_POOL_CACHE_CPUS:
> + rv = pool_cache_cpus_info(pp, oldp, oldlenp);
> + break;
> }
>
> done:
> @@ -1608,7 +1623,7 @@ pool_cache_init(struct pool *pp)
> IPL_NONE, PR_WAITOK, "plcache", NULL);
> }
>
> - /* must be able to use the pool items as cache list items */
> + /* must be able to use the pool items as cache list entries */
> KASSERT(pp->pr_size >= sizeof(struct pool_cache_item));
>
> cm = cpumem_get(&pool_caches);
> @@ -1625,9 +1640,12 @@ pool_cache_init(struct pool *pp)
> pc->pc_nactv = 0;
> pc->pc_prev = NULL;
>
> - pc->pc_gets = 0;
> - pc->pc_puts = 0;
> - pc->pc_fails = 0;
> + pc->pc_nget = 0;
> + pc->pc_nfail = 0;
> + pc->pc_nput = 0;
> + pc->pc_nlget = 0;
> + pc->pc_nlfail = 0;
> + pc->pc_nlput = 0;
> pc->pc_nout = 0;
> }
>
> @@ -1694,7 +1712,10 @@ pool_cache_list_alloc(struct pool *pp, s
> pp->pr_cache_nlist--;
>
> pool_cache_item_magic(pp, pl);
> - }
> +
> + pc->pc_nlget++;
> + } else
> + pc->pc_nlfail++;
>
> /* fold this cpus nout into the global while we have the lock */
> pp->pr_cache_nout += pc->pc_nout;
> @@ -1712,6 +1733,8 @@ pool_cache_list_free(struct pool *pp, st
> TAILQ_INSERT_TAIL(&pp->pr_cache_lists, ci, ci_nextl);
> pp->pr_cache_nlist++;
>
> + pc->pc_nlput++;
> +
> /* fold this cpus nout into the global while we have the lock */
> pp->pr_cache_nout += pc->pc_nout;
> pc->pc_nout = 0;
> @@ -1753,7 +1776,7 @@ pool_cache_get(struct pool *pp)
> ci = pc->pc_prev;
> pc->pc_prev = NULL;
> } else if ((ci = pool_cache_list_alloc(pp, pc)) == NULL) {
> - pc->pc_fails++;
> + pc->pc_nfail++;
> goto done;
> }
>
> @@ -1778,7 +1801,7 @@ pool_cache_get(struct pool *pp)
>
> pc->pc_actv = ci->ci_next;
> pc->pc_nactv = POOL_CACHE_ITEM_NITEMS(ci) - 1;
> - pc->pc_gets++;
> + pc->pc_nget++;
> pc->pc_nout++;
>
> done:
> @@ -1825,7 +1848,7 @@ pool_cache_put(struct pool *pp, void *v)
> pc->pc_actv = ci;
> pc->pc_nactv = nitems;
>
> - pc->pc_puts++;
> + pc->pc_nput++;
> pc->pc_nout--;
>
> pool_cache_leave(pp, pc, s);
> @@ -1874,7 +1897,7 @@ pool_cache_destroy(struct pool *pp)
> }
>
> void
> -pool_cache_info(struct pool *pp, struct kinfo_pool *pi)
> +pool_cache_pool_info(struct pool *pp, struct kinfo_pool *pi)
> {
> struct pool_cache *pc;
> struct cpumem_iter i;
> @@ -1892,8 +1915,8 @@ pool_cache_info(struct pool *pp, struct
> while ((gen = pc->pc_gen) & 1)
> yield();
>
> - nget = pc->pc_gets;
> - nput = pc->pc_puts;
> + nget = pc->pc_nget;
> + nput = pc->pc_nput;
> } while (gen != pc->pc_gen);
>
> pi->pr_nget += nget;
> @@ -1908,6 +1931,80 @@ pool_cache_info(struct pool *pp, struct
> pi->pr_nout += pp->pr_cache_nout;
> mtx_leave(&pp->pr_cache_mtx);
> }
> +
> +int
> +pool_cache_info(struct pool *pp, void *oldp, size_t *oldlenp)
> +{
> + struct kinfo_pool_cache kpc;
> +
> + if (pp->pr_cache == NULL)
> + return (EOPNOTSUPP);
> +
> + memset(&kpc, 0, sizeof(kpc)); /* don't leak padding */
> +
> + mtx_enter(&pp->pr_cache_mtx);
> + kpc.pr_ngc = 0; /* notyet */
> + kpc.pr_len = pp->pr_cache_items;
> + kpc.pr_nlist = pp->pr_cache_nlist;
> + mtx_leave(&pp->pr_cache_mtx);
> +
> + return (sysctl_rdstruct(oldp, oldlenp, NULL, &kpc, sizeof(kpc)));
> +}
> +
> +int
> +pool_cache_cpus_info(struct pool *pp, void *oldp, size_t *oldlenp)
> +{
> + struct pool_cache *pc;
> + struct kinfo_pool_cache_cpu *kpcc, *info;
> + unsigned int cpu = 0;
> + struct cpumem_iter i;
> + int error = 0;
> + size_t len;
> +
> + if (pp->pr_cache == NULL)
> + return (EOPNOTSUPP);
> + if (*oldlenp % sizeof(*kpcc))
> + return (EINVAL);
> +
> + kpcc = mallocarray(ncpusfound, sizeof(*kpcc), M_TEMP,
> + M_WAITOK|M_CANFAIL|M_ZERO);
> + if (kpcc == NULL)
> + return (EIO);
> +
> + len = ncpusfound * sizeof(*kpcc);
> +
> + CPUMEM_FOREACH(pc, &i, pp->pr_cache) {
> + uint64_t gen;
> +
> + if (cpu >= ncpusfound) {
> + error = EIO;
> + goto err;
> + }
> +
> + info = &kpcc[cpu];
> + info->pr_cpu = cpu;
> +
> + do {
> + while ((gen = pc->pc_gen) & 1)
> + yield();
> +
> + info->pr_nget = pc->pc_nget;
> + info->pr_nfail = pc->pc_nfail;
> + info->pr_nput = pc->pc_nput;
> + info->pr_nlget = pc->pc_nlget;
> + info->pr_nlfail = pc->pc_nlfail;
> + info->pr_nlput = pc->pc_nlput;
> + } while (gen != pc->pc_gen);
> +
> + cpu++;
> + }
> +
> + error = sysctl_rdstruct(oldp, oldlenp, NULL, kpcc, len);
> +err:
> + free(kpcc, M_TEMP, len);
> +
> + return (error);
> +}
> #else /* MULTIPROCESSOR */
> void
> pool_cache_init(struct pool *pp)
> @@ -1916,8 +2013,20 @@ pool_cache_init(struct pool *pp)
> }
>
> void
> -pool_cache_info(struct pool *pp, struct kinfo_pool *pi)
> +pool_cache_pool_info(struct pool *pp, struct kinfo_pool *pi)
> {
> /* nop */
> +}
> +
> +int
> +pool_cache_info(struct pool *pp, void *oldp, size_t *oldlenp)
> +{
> + return (EOPNOTSUPP);
> +}
> +
> +int
> +pool_cache_cpus_info(struct pool *pp, void *oldp, size_t *oldlenp)
> +{
> + return (EOPNOTSUPP);
> }
> #endif /* MULTIPROCESSOR */
> Index: sys/sys/pool.h
> ===================================================================
> RCS file: /cvs/src/sys/sys/pool.h,v
> retrieving revision 1.69
> diff -u -p -r1.69 pool.h
> --- sys/sys/pool.h 7 Feb 2017 05:39:17 -0000 1.69
> +++ sys/sys/pool.h 14 Jun 2017 03:13:47 -0000
> @@ -43,6 +43,8 @@
> #define KERN_POOL_NPOOLS 1
> #define KERN_POOL_NAME 2
> #define KERN_POOL_POOL 3
> +#define KERN_POOL_CACHE 4 /* global pool cache info */
> +#define KERN_POOL_CACHE_CPUS 5 /* all cpus cache info */
>
> struct kinfo_pool {
> unsigned int pr_size; /* size of a pool item */
> @@ -66,6 +68,30 @@ struct kinfo_pool {
> unsigned long pr_nidle; /* # of idle pages */
> };
>
> +struct kinfo_pool_cache {
> + uint64_t pr_ngc; /* # of times a list has been gc'ed */
> + unsigned int pr_len; /* current target for list len */
> + unsigned int pr_nlist; /* # of lists in the pool */
> +};
> +
> +/*
> + * KERN_POOL_CACHE_CPUS access to an array, not a single struct. ie, it
> + * provides struct kinfo_pool_cache_cpu kppc[ncpusfound].
> + */
> +struct kinfo_pool_cache_cpu {
> + unsigned int pr_cpu; /* which cpu is this a cache on */
> +
> + /* counters for times items were hanled by the cache */
> + uint64_t pr_nget; /* # of requests */
> + uint64_t pr_nfail; /* # of unsuccessful requests */
> + uint64_t pr_nput; /* # of releases */
> +
> + /* counters for times the cache interacted with the pool */
> + uint64_t pr_nlget; /* # of list requests */
> + uint64_t pr_nlfail; /* # of unsuccessful list requests */
> + uint64_t pr_nlput; /* # of list releases */
> +};
> +
> #if defined(_KERNEL) || defined(_LIBKVM)
>
> #include <sys/queue.h>
> @@ -159,8 +185,8 @@ struct pool {
> struct mutex pr_cache_mtx;
> struct pool_cache_lists
> pr_cache_lists;
> - u_int pr_cache_nlist;
> - u_int pr_cache_items;
> + u_int pr_cache_nlist; /* # of lists */
> + u_int pr_cache_items; /* target list length */
> u_int pr_cache_contention;
> int pr_cache_nout;
>
> Index: usr.bin/systat/pool.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/systat/pool.c,v
> retrieving revision 1.11
> diff -u -p -r1.11 pool.c
> --- usr.bin/systat/pool.c 12 Mar 2016 12:45:27 -0000 1.11
> +++ usr.bin/systat/pool.c 14 Jun 2017 03:13:48 -0000
> @@ -26,6 +26,19 @@
>
> #include "systat.h"
>
> +#ifndef nitems
> +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
> +#endif
> +
> +static int sysctl_rdint(const int *, unsigned int);
> +static int hw_ncpusfound(void);
> +
> +static int pool_get_npools(void);
> +static int pool_get_name(int, char *, size_t);
> +static int pool_get_cache(int, struct kinfo_pool_cache *);
> +static int pool_get_cache_cpus(int, struct kinfo_pool_cache_cpu *,
> + unsigned int);
> +
> void print_pool(void);
> int read_pool(void);
> void sort_pool(void);
> @@ -44,11 +57,25 @@ struct pool_info {
> struct kinfo_pool pool;
> };
>
> +/*
> + * the kernel gives an array of ncpusfound * kinfo_pool_cache structs, but
> + * it's idea of how big that struct is may differ from here. we fetch both
> + * ncpusfound and the size it thinks kinfo_pool_cache is from sysctl, and
> + * then allocate the memory for this here.
> + */
> +struct pool_cache_info {
> + char name[32];
> + struct kinfo_pool_cache cache;
> + struct kinfo_pool_cache_cpu *cache_cpus;
> +};
>
> int print_all = 0;
> int num_pools = 0;
> struct pool_info *pools = NULL;
> +int num_pool_caches = 0;
> +struct pool_cache_info *pool_caches = NULL;
>
> +int ncpusfound = 0;
>
> field_def fields_pool[] = {
> {"NAME", 12, 32, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
> @@ -65,7 +92,6 @@ field_def fields_pool[] = {
> {"IDLE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}
> };
>
> -
> #define FLD_POOL_NAME FIELD_ADDR(fields_pool,0)
> #define FLD_POOL_SIZE FIELD_ADDR(fields_pool,1)
> #define FLD_POOL_REQS FIELD_ADDR(fields_pool,2)
> @@ -100,11 +126,80 @@ struct view_manager pool_mgr = {
> print_pool, pool_keyboard_callback, pool_order_list, pool_order_list
> };
>
> -field_view views_pool[] = {
> - {view_pool_0, "pool", '5', &pool_mgr},
> +field_view pool_view = {
> + view_pool_0,
> + "pool",
> + '5',
> + &pool_mgr
> +};
> +
> +void pool_cache_print(void);
> +int pool_cache_read(void);
> +void pool_cache_sort(void);
> +void pool_cache_show(const struct pool_cache_info *);
> +int pool_cache_kbd_cb(int);
> +
> +field_def pool_cache_fields[] = {
> + {"NAME", 12, 32, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
> + {"LEN", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
> + {"NL", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
> + {"NGC", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
> + {"CPU", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
> + {"REQ", 8, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
> + {"REL", 8, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
> + {"LREQ", 8, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
> + {"LREL", 8, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
> +};
> +
> +#define FLD_POOL_CACHE_NAME FIELD_ADDR(pool_cache_fields, 0)
> +#define FLD_POOL_CACHE_LEN FIELD_ADDR(pool_cache_fields, 1)
> +#define FLD_POOL_CACHE_NL FIELD_ADDR(pool_cache_fields, 2)
> +#define FLD_POOL_CACHE_NGC FIELD_ADDR(pool_cache_fields, 3)
> +#define FLD_POOL_CACHE_CPU FIELD_ADDR(pool_cache_fields, 4)
> +#define FLD_POOL_CACHE_GET FIELD_ADDR(pool_cache_fields, 5)
> +#define FLD_POOL_CACHE_PUT FIELD_ADDR(pool_cache_fields, 6)
> +#define FLD_POOL_CACHE_LGET FIELD_ADDR(pool_cache_fields, 7)
> +#define FLD_POOL_CACHE_LPUT FIELD_ADDR(pool_cache_fields, 8)
> +
> +field_def *view_pool_cache_0[] = {
> + FLD_POOL_CACHE_NAME,
> + FLD_POOL_CACHE_LEN,
> + FLD_POOL_CACHE_NL,
> + FLD_POOL_CACHE_NGC,
> + FLD_POOL_CACHE_CPU,
> + FLD_POOL_CACHE_GET,
> + FLD_POOL_CACHE_PUT,
> + FLD_POOL_CACHE_LGET,
> + FLD_POOL_CACHE_LPUT,
> + NULL,
> +};
> +
> +order_type pool_cache_order_list[] = {
> + {"name", "name", 'N', sort_name_callback},
> + {"requests", "requests", 'G', sort_req_callback},
> + {"releases", "releases", 'P', sort_req_callback},
> {NULL, NULL, 0, NULL}
> };
>
> +/* Define view managers */
> +struct view_manager pool_cache_mgr = {
> + "PoolCache",
> + select_pool,
> + pool_cache_read,
> + pool_cache_sort,
> + print_header,
> + pool_cache_print,
> + pool_keyboard_callback,
> + pool_cache_order_list,
> + pool_cache_order_list
> +};
> +
> +field_view pool_cache_view = {
> + view_pool_cache_0,
> + "pcaches",
> + '5',
> + &pool_cache_mgr
> +};
>
> int
> sort_name_callback(const void *s1, const void *s2)
> @@ -200,30 +295,31 @@ select_pool(void)
> int
> read_pool(void)
> {
> - int mib[4], np, i;
> + int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_POOL, 0 };
> + struct pool_info *p;
> + int np, i;
> size_t size;
>
> - mib[0] = CTL_KERN;
> - mib[1] = KERN_POOL;
> - mib[2] = KERN_POOL_NPOOLS;
> - size = sizeof(np);
> -
> - if (sysctl(mib, 3, &np, &size, NULL, 0) < 0) {
> + np = pool_get_npools();
> + if (np == -1) {
> error("sysctl(npools): %s", strerror(errno));
> return (-1);
> }
>
> - if (np <= 0) {
> + if (np == 0) {
> + free(pools);
> + pools = NULL;
> num_pools = 0;
> return (0);
> }
>
> if (np > num_pools || pools == NULL) {
> - struct pool_info *p = reallocarray(pools, np, sizeof(*pools));
> + p = reallocarray(pools, np, sizeof(*pools));
> if (p == NULL) {
> error("realloc: %s", strerror(errno));
> return (-1);
> }
> + /* commit */
> pools = p;
> num_pools = np;
> }
> @@ -231,26 +327,19 @@ read_pool(void)
> num_disp = num_pools;
>
> for (i = 0; i < num_pools; i++) {
> - mib[0] = CTL_KERN;
> - mib[1] = KERN_POOL;
> - mib[2] = KERN_POOL_POOL;
> - mib[3] = i + 1;
> + p = &pools[i];
> + np = i + 1;
> +
> + mib[3] = np;
> size = sizeof(pools[i].pool);
> - if (sysctl(mib, 4, &pools[i].pool, &size, NULL, 0) < 0) {
> - memset(&pools[i], 0, sizeof(pools[i]));
> + if (sysctl(mib, nitems(mib), &p->pool, &size, NULL, 0) < 0) {
> + p->name[0] = '\0';
> num_disp--;
> continue;
> }
> - mib[2] = KERN_POOL_NAME;
> - size = sizeof(pools[i].name);
> - if (sysctl(mib, 4, &pools[i].name, &size, NULL, 0) < 0) {
> - snprintf(pools[i].name, size, "#%d#", mib[3]);
> - }
> - }
>
> - if (i != num_pools) {
> - memset(pools, 0, sizeof(*pools) * num_pools);
> - return (-1);
> + if (pool_get_name(np, p->name, sizeof(p->name)) < 0)
> + snprintf(p->name, sizeof(p->name), "#%d#", i + 1);
> }
>
> return 0;
> @@ -289,11 +378,18 @@ initpool(void)
> {
> field_view *v;
>
> - for (v = views_pool; v->name != NULL; v++)
> - add_view(v);
> -
> + add_view(&pool_view);
> read_pool();
>
> + ncpusfound = hw_ncpusfound();
> + if (ncpusfound == -1) {
> + error("sysctl(ncpusfound): %s", strerror(errno));
> + exit(1);
> + }
> +
> + add_view(&pool_cache_view);
> + pool_cache_read();
> +
> return(0);
> }
>
> @@ -340,4 +436,194 @@ pool_keyboard_callback(int ch)
> };
>
> return (1);
> +}
> +
> +int
> +pool_cache_read(void)
> +{
> + struct pool_cache_info *pc;
> + int np, i;
> +
> + np = pool_get_npools();
> + if (np == -1) {
> + error("sysctl(npools): %s", strerror(errno));
> + return (-1);
> + }
> +
> + if (np > num_pool_caches) {
> + pc = reallocarray(pool_caches, np, sizeof(*pc));
> + if (pc == NULL) {
> + error("realloc: %s", strerror(errno));
> + return (-1);
> + }
> + /* commit to using the new memory */
> + pool_caches = pc;
> +
> + for (i = num_pool_caches; i < np; i++) {
> + pc = &pool_caches[i];
> + pc->name[0] = '\0';
> +
> + pc->cache_cpus = reallocarray(NULL, ncpusfound,
> + sizeof(*pc->cache_cpus));
> + if (pc->cache_cpus == NULL) {
> + error("malloc cache cpus: %s", strerror(errno));
> + goto unalloc;
> + }
> + }
> +
> + /* commit to using the new cache_infos */
> + num_pool_caches = np;
> + }
> +
> + for (i = 0; i < num_pool_caches; i++) {
> + pc = &pool_caches[i];
> + np = i + 1;
> +
> + if (pool_get_cache(np, &pc->cache) < 0 ||
> + pool_get_cache_cpus(np, pc->cache_cpus, ncpusfound) < 0) {
> + pc->name[0] = '\0';
> + continue;
> + }
> +
> + if (pool_get_name(np, pc->name, sizeof(pc->name)) < 0)
> + snprintf(pc->name, sizeof(pc->name), "#%d#", i + 1);
> + }
> +
> + return 0;
> +
> +unalloc:
> + while (i > num_pool_caches) {
> + pc = &pool_caches[--i];
> + free(pc->cache_cpus);
> + }
> +}
> +
> +void
> +pool_cache_sort(void)
> +{
> + /* XXX */
> + order_type *ordering;
> +
> + if (curr_mgr == NULL)
> + return;
> +
> + ordering = curr_mgr->order_curr;
> +
> + if (ordering == NULL)
> + return;
> + if (ordering->func == NULL)
> + return;
> + if (pools == NULL)
> + return;
> + if (num_pools <= 0)
> + return;
> +
> + mergesort(pools, num_pools, sizeof(struct pool_info), ordering->func);
> +}
> +
> +void
> +pool_cache_print(void)
> +{
> + struct pool_cache_info *pc;
> + int i, n, count = 0;
> +
> + if (pool_caches == NULL)
> + return;
> +
> + for (n = i = 0; i < num_pool_caches; i++) {
> + pc = &pool_caches[i];
> + if (pc->name[0] == '\0')
> + continue;
> +
> + if (n++ < dispstart)
> + continue;
> +
> + pool_cache_show(pc);
> + count++;
> + if (maxprint > 0 && count >= maxprint)
> + break;
> + }
> +}
> +
> +void
> +pool_cache_show(const struct pool_cache_info *pc)
> +{
> + const struct kinfo_pool_cache *kpc;
> + const struct kinfo_pool_cache_cpu *kpcc;
> + int cpu;
> +
> + kpc = &pc->cache;
> +
> + print_fld_str(FLD_POOL_CACHE_NAME, pc->name);
> + print_fld_uint(FLD_POOL_CACHE_LEN, kpc->pr_len);
> + print_fld_uint(FLD_POOL_CACHE_NL, kpc->pr_nlist);
> + print_fld_uint(FLD_POOL_CACHE_NGC, kpc->pr_ngc);
> +
> + for (cpu = 0; cpu < ncpusfound; cpu++) {
> + kpcc = &pc->cache_cpus[cpu];
> +
> + print_fld_uint(FLD_POOL_CACHE_CPU, kpcc->pr_cpu);
> +
> + print_fld_size(FLD_POOL_CACHE_GET, kpcc->pr_nget);
> + print_fld_size(FLD_POOL_CACHE_PUT, kpcc->pr_nput);
> + print_fld_size(FLD_POOL_CACHE_LGET, kpcc->pr_nlget);
> + print_fld_size(FLD_POOL_CACHE_LPUT, kpcc->pr_nlput);
> + end_line();
> + }
> +
> +}
> +
> +static int
> +pool_get_npools(void)
> +{
> + int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_NPOOLS };
> +
> + return (sysctl_rdint(mib, nitems(mib)));
> +}
> +
> +static int
> +pool_get_cache(int pool, struct kinfo_pool_cache *kpc)
> +{
> + int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_CACHE, pool };
> + size_t len = sizeof(*kpc);
> +
> + return (sysctl(mib, nitems(mib), kpc, &len, NULL, 0));
> +}
> +
> +static int
> +pool_get_cache_cpus(int pool, struct kinfo_pool_cache_cpu *kpcc,
> + unsigned int ncpus)
> +{
> + int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_CACHE_CPUS, pool };
> + size_t len = sizeof(*kpcc) * ncpus;
> +
> + return (sysctl(mib, nitems(mib), kpcc, &len, NULL, 0));
> +}
> +
> +static int
> +pool_get_name(int pool, char *name, size_t len)
> +{
> + int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_NAME, pool };
> +
> + return (sysctl(mib, nitems(mib), name, &len, NULL, 0));
> +}
> +
> +static int
> +hw_ncpusfound(void)
> +{
> + int mib[] = { CTL_HW, HW_NCPUFOUND };
> +
> + return (sysctl_rdint(mib, nitems(mib)));
> +}
> +
> +static int
> +sysctl_rdint(const int *mib, unsigned int nmib)
> +{
> + int i;
> + size_t size = sizeof(i);
> +
> + if (sysctl(mib, nmib, &i, &size, NULL, 0) == -1)
> + return (-1);
> +
> + return (i);
> }
> Index: usr.bin/systat/systat.1
> ===================================================================
> RCS file: /cvs/src/usr.bin/systat/systat.1,v
> retrieving revision 1.101
> diff -u -p -r1.101 systat.1
> --- usr.bin/systat/systat.1 12 Mar 2015 01:03:00 -0000 1.101
> +++ usr.bin/systat/systat.1 14 Jun 2017 03:13:48 -0000
> @@ -136,6 +136,7 @@ argument expects to be one of:
> .Ic queues ,
> .Ic pf ,
> .Ic pool ,
> +.Ic pcache ,
> .Ic malloc ,
> .Ic buckets ,
> .Ic nfsclient ,
> @@ -379,6 +380,10 @@ and
> By default only the statistics of active pools are displayed but pressing
> .Ic A
> changes the view to show all of them.
> +.It Ic pcache
> +Display kernel
> +.Xr pool 9
> +per CPU cache statistics.
> .It Ic queues
> Display statistics about the active queues,
> similar to the output of