Annotation of src/usr.bin/systat/pool.c, Revision 1.16
1.16 ! martijn 1: /* $OpenBSD: pool.c,v 1.15 2017/07/31 04:23:30 dlg Exp $ */
1.1 canacar 2: /*
3: * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17:
18: #include <sys/types.h>
1.10 deraadt 19: #include <sys/signal.h>
1.1 canacar 20: #include <sys/sysctl.h>
21: #include <sys/pool.h>
22: #include <errno.h>
23: #include <stdlib.h>
1.3 chl 24: #include <string.h>
1.10 deraadt 25: #include <limits.h>
1.1 canacar 26:
27: #include "systat.h"
28:
1.12 dlg 29: #ifndef nitems
30: #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
31: #endif
32:
33: static int sysctl_rdint(const int *, unsigned int);
34: static int hw_ncpusfound(void);
35:
36: static int pool_get_npools(void);
37: static int pool_get_name(int, char *, size_t);
38: static int pool_get_cache(int, struct kinfo_pool_cache *);
39: static int pool_get_cache_cpus(int, struct kinfo_pool_cache_cpu *,
40: unsigned int);
41:
1.1 canacar 42: void print_pool(void);
43: int read_pool(void);
44: void sort_pool(void);
45: int select_pool(void);
46: void showpool(int k);
1.8 mpi 47: int pool_keyboard_callback(int);
1.1 canacar 48:
49: /* qsort callbacks */
50: int sort_name_callback(const void *s1, const void *s2);
1.2 canacar 51: int sort_req_callback(const void *s1, const void *s2);
1.4 canacar 52: int sort_psize_callback(const void *s1, const void *s2);
53: int sort_npage_callback(const void *s1, const void *s2);
1.1 canacar 54:
55: struct pool_info {
56: char name[32];
1.7 dlg 57: struct kinfo_pool pool;
1.1 canacar 58: };
59:
1.12 dlg 60: /*
61: * the kernel gives an array of ncpusfound * kinfo_pool_cache structs, but
62: * it's idea of how big that struct is may differ from here. we fetch both
63: * ncpusfound and the size it thinks kinfo_pool_cache is from sysctl, and
64: * then allocate the memory for this here.
65: */
66: struct pool_cache_info {
67: char name[32];
68: struct kinfo_pool_cache cache;
69: struct kinfo_pool_cache_cpu *cache_cpus;
70: };
1.1 canacar 71:
1.8 mpi 72: int print_all = 0;
1.1 canacar 73: int num_pools = 0;
74: struct pool_info *pools = NULL;
1.12 dlg 75: int num_pool_caches = 0;
76: struct pool_cache_info *pool_caches = NULL;
1.1 canacar 77:
1.12 dlg 78: int ncpusfound = 0;
1.1 canacar 79:
80: field_def fields_pool[] = {
1.11 sthen 81: {"NAME", 12, 32, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
1.1 canacar 82: {"SIZE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
83: {"REQUESTS", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
84: {"FAIL", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
85: {"INUSE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
86: {"PGREQ", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
87: {"PGREL", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
88: {"NPAGE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
89: {"HIWAT", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
90: {"MINPG", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
91: {"MAXPG", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
92: {"IDLE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}
93: };
94:
1.6 jasper 95: #define FLD_POOL_NAME FIELD_ADDR(fields_pool,0)
96: #define FLD_POOL_SIZE FIELD_ADDR(fields_pool,1)
97: #define FLD_POOL_REQS FIELD_ADDR(fields_pool,2)
98: #define FLD_POOL_FAIL FIELD_ADDR(fields_pool,3)
99: #define FLD_POOL_INUSE FIELD_ADDR(fields_pool,4)
100: #define FLD_POOL_PGREQ FIELD_ADDR(fields_pool,5)
101: #define FLD_POOL_PGREL FIELD_ADDR(fields_pool,6)
102: #define FLD_POOL_NPAGE FIELD_ADDR(fields_pool,7)
103: #define FLD_POOL_HIWAT FIELD_ADDR(fields_pool,8)
104: #define FLD_POOL_MINPG FIELD_ADDR(fields_pool,9)
105: #define FLD_POOL_MAXPG FIELD_ADDR(fields_pool,10)
106: #define FLD_POOL_IDLE FIELD_ADDR(fields_pool,11)
1.1 canacar 107:
108: /* Define views */
109: field_def *view_pool_0[] = {
110: FLD_POOL_NAME, FLD_POOL_SIZE, FLD_POOL_REQS, FLD_POOL_FAIL,
111: FLD_POOL_INUSE, FLD_POOL_PGREQ, FLD_POOL_PGREL, FLD_POOL_NPAGE,
112: FLD_POOL_HIWAT, FLD_POOL_MINPG, FLD_POOL_MAXPG, FLD_POOL_IDLE, NULL
113: };
114:
115: order_type pool_order_list[] = {
1.2 canacar 116: {"name", "name", 'N', sort_name_callback},
117: {"requests", "requests", 'Q', sort_req_callback},
1.4 canacar 118: {"size", "size", 'Z', sort_psize_callback},
119: {"npages", "npages", 'P', sort_npage_callback},
1.1 canacar 120: {NULL, NULL, 0, NULL}
121: };
122:
123: /* Define view managers */
124: struct view_manager pool_mgr = {
125: "Pool", select_pool, read_pool, sort_pool, print_header,
1.8 mpi 126: print_pool, pool_keyboard_callback, pool_order_list, pool_order_list
1.1 canacar 127: };
128:
1.12 dlg 129: field_view pool_view = {
130: view_pool_0,
131: "pool",
132: '5',
133: &pool_mgr
134: };
135:
136: void pool_cache_print(void);
137: int pool_cache_read(void);
138: void pool_cache_sort(void);
139: void pool_cache_show(const struct pool_cache_info *);
140: int pool_cache_kbd_cb(int);
141:
142: field_def pool_cache_fields[] = {
143: {"NAME", 12, 32, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
144: {"LEN", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
1.13 dlg 145: {"IDLE", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
1.12 dlg 146: {"NGC", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
147: {"CPU", 4, 4, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
148: {"REQ", 8, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
149: {"REL", 8, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
150: {"LREQ", 8, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
151: {"LREL", 8, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
152: };
153:
154: #define FLD_POOL_CACHE_NAME FIELD_ADDR(pool_cache_fields, 0)
155: #define FLD_POOL_CACHE_LEN FIELD_ADDR(pool_cache_fields, 1)
1.13 dlg 156: #define FLD_POOL_CACHE_IDLE FIELD_ADDR(pool_cache_fields, 2)
1.12 dlg 157: #define FLD_POOL_CACHE_NGC FIELD_ADDR(pool_cache_fields, 3)
158: #define FLD_POOL_CACHE_CPU FIELD_ADDR(pool_cache_fields, 4)
159: #define FLD_POOL_CACHE_GET FIELD_ADDR(pool_cache_fields, 5)
160: #define FLD_POOL_CACHE_PUT FIELD_ADDR(pool_cache_fields, 6)
161: #define FLD_POOL_CACHE_LGET FIELD_ADDR(pool_cache_fields, 7)
162: #define FLD_POOL_CACHE_LPUT FIELD_ADDR(pool_cache_fields, 8)
163:
164: field_def *view_pool_cache_0[] = {
165: FLD_POOL_CACHE_NAME,
166: FLD_POOL_CACHE_LEN,
1.13 dlg 167: FLD_POOL_CACHE_IDLE,
1.12 dlg 168: FLD_POOL_CACHE_NGC,
169: FLD_POOL_CACHE_CPU,
170: FLD_POOL_CACHE_GET,
171: FLD_POOL_CACHE_PUT,
172: FLD_POOL_CACHE_LGET,
173: FLD_POOL_CACHE_LPUT,
174: NULL,
175: };
176:
177: order_type pool_cache_order_list[] = {
178: {"name", "name", 'N', sort_name_callback},
179: {"requests", "requests", 'G', sort_req_callback},
180: {"releases", "releases", 'P', sort_req_callback},
1.1 canacar 181: {NULL, NULL, 0, NULL}
182: };
183:
1.12 dlg 184: /* Define view managers */
185: struct view_manager pool_cache_mgr = {
186: "PoolCache",
187: select_pool,
188: pool_cache_read,
189: pool_cache_sort,
190: print_header,
191: pool_cache_print,
192: pool_keyboard_callback,
193: pool_cache_order_list,
194: pool_cache_order_list
195: };
196:
197: field_view pool_cache_view = {
198: view_pool_cache_0,
199: "pcaches",
200: '5',
201: &pool_cache_mgr
202: };
1.1 canacar 203:
204: int
205: sort_name_callback(const void *s1, const void *s2)
206: {
207: struct pool_info *p1, *p2;
208: p1 = (struct pool_info *)s1;
209: p2 = (struct pool_info *)s2;
210:
1.2 canacar 211: return strcmp(p1->name, p2->name) * sortdir;
212: }
213:
214: int
215: sort_req_callback(const void *s1, const void *s2)
216: {
217: struct pool_info *p1, *p2;
218: p1 = (struct pool_info *)s1;
219: p2 = (struct pool_info *)s2;
220:
221: if (p1->pool.pr_nget < p2->pool.pr_nget)
222: return sortdir;
223: if (p1->pool.pr_nget > p2->pool.pr_nget)
224: return -sortdir;
225:
226: return sort_name_callback(s1, s2);
1.4 canacar 227: }
228:
229: int
230: sort_npage_callback(const void *s1, const void *s2)
231: {
232: struct pool_info *p1, *p2;
233: p1 = (struct pool_info *)s1;
234: p2 = (struct pool_info *)s2;
235:
236: if (p1->pool.pr_npages < p2->pool.pr_npages)
237: return sortdir;
238: if (p1->pool.pr_npages > p2->pool.pr_npages)
239: return -sortdir;
240:
241: return sort_name_callback(s1, s2);
242: }
243:
244: int
245: sort_psize_callback(const void *s1, const void *s2)
246: {
247: struct pool_info *p1, *p2;
248:
249: p1 = (struct pool_info *)s1;
250: p2 = (struct pool_info *)s2;
251:
1.16 ! martijn 252: if (p1->pool.pr_size < p2->pool.pr_size)
1.4 canacar 253: return sortdir;
1.16 ! martijn 254: if (p1->pool.pr_size > p2->pool.pr_size)
1.4 canacar 255: return -sortdir;
256:
257: return sort_npage_callback(s1, s2);
1.1 canacar 258: }
259:
260: void
261: sort_pool(void)
262: {
263: order_type *ordering;
264:
265: if (curr_mgr == NULL)
266: return;
267:
268: ordering = curr_mgr->order_curr;
269:
270: if (ordering == NULL)
271: return;
272: if (ordering->func == NULL)
273: return;
274: if (pools == NULL)
275: return;
276: if (num_pools <= 0)
277: return;
278:
1.2 canacar 279: mergesort(pools, num_pools, sizeof(struct pool_info), ordering->func);
1.1 canacar 280: }
281:
282: int
283: select_pool(void)
284: {
285: num_disp = num_pools;
286: return (0);
287: }
288:
289: int
290: read_pool(void)
291: {
1.12 dlg 292: int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_POOL, 0 };
293: struct pool_info *p;
294: int np, i;
1.1 canacar 295: size_t size;
296:
1.12 dlg 297: np = pool_get_npools();
298: if (np == -1) {
1.1 canacar 299: error("sysctl(npools): %s", strerror(errno));
300: return (-1);
301: }
302:
1.12 dlg 303: if (np == 0) {
304: free(pools);
305: pools = NULL;
1.1 canacar 306: num_pools = 0;
307: return (0);
308: }
309:
310: if (np > num_pools || pools == NULL) {
1.12 dlg 311: p = reallocarray(pools, np, sizeof(*pools));
1.1 canacar 312: if (p == NULL) {
313: error("realloc: %s", strerror(errno));
314: return (-1);
315: }
1.12 dlg 316: /* commit */
1.1 canacar 317: pools = p;
318: num_pools = np;
319: }
320:
1.5 canacar 321: num_disp = num_pools;
322:
1.1 canacar 323: for (i = 0; i < num_pools; i++) {
1.12 dlg 324: p = &pools[i];
325: np = i + 1;
326:
327: mib[3] = np;
1.7 dlg 328: size = sizeof(pools[i].pool);
1.12 dlg 329: if (sysctl(mib, nitems(mib), &p->pool, &size, NULL, 0) < 0) {
330: p->name[0] = '\0';
1.5 canacar 331: num_disp--;
332: continue;
1.1 canacar 333: }
334:
1.12 dlg 335: if (pool_get_name(np, p->name, sizeof(p->name)) < 0)
336: snprintf(p->name, sizeof(p->name), "#%d#", i + 1);
1.1 canacar 337: }
338:
339: return 0;
340: }
341:
342:
343: void
344: print_pool(void)
345: {
1.8 mpi 346: struct pool_info *p;
1.5 canacar 347: int i, n, count = 0;
1.1 canacar 348:
349: if (pools == NULL)
350: return;
351:
1.5 canacar 352: for (n = i = 0; i < num_pools; i++) {
1.8 mpi 353: p = &pools[i];
354: if (p->name[0] == 0)
1.5 canacar 355: continue;
1.8 mpi 356:
357: if (!print_all &&
358: (p->pool.pr_nget == 0 && p->pool.pr_npagealloc == 0))
359: continue;
360:
1.5 canacar 361: if (n++ < dispstart)
362: continue;
363: showpool(i);
1.1 canacar 364: count++;
365: if (maxprint > 0 && count >= maxprint)
366: break;
367: }
368: }
369:
370: int
371: initpool(void)
372: {
373: field_view *v;
374:
1.12 dlg 375: add_view(&pool_view);
376: read_pool();
377:
378: ncpusfound = hw_ncpusfound();
379: if (ncpusfound == -1) {
380: error("sysctl(ncpusfound): %s", strerror(errno));
381: exit(1);
382: }
1.1 canacar 383:
1.12 dlg 384: add_view(&pool_cache_view);
385: pool_cache_read();
1.1 canacar 386:
387: return(0);
388: }
389:
390: void
391: showpool(int k)
392: {
393: struct pool_info *p = pools + k;
394:
395: if (k < 0 || k >= num_pools)
396: return;
397:
398: print_fld_str(FLD_POOL_NAME, p->name);
399: print_fld_uint(FLD_POOL_SIZE, p->pool.pr_size);
400:
401: print_fld_size(FLD_POOL_REQS, p->pool.pr_nget);
402: print_fld_size(FLD_POOL_FAIL, p->pool.pr_nfail);
403: print_fld_ssize(FLD_POOL_INUSE, p->pool.pr_nget - p->pool.pr_nput);
404: print_fld_size(FLD_POOL_PGREQ, p->pool.pr_npagealloc);
405: print_fld_size(FLD_POOL_PGREL, p->pool.pr_npagefree);
406:
407: print_fld_size(FLD_POOL_NPAGE, p->pool.pr_npages);
408: print_fld_size(FLD_POOL_HIWAT, p->pool.pr_hiwat);
409: print_fld_size(FLD_POOL_MINPG, p->pool.pr_minpages);
410:
411: if (p->pool.pr_maxpages == UINT_MAX)
412: print_fld_str(FLD_POOL_MAXPG, "inf");
413: else
414: print_fld_size(FLD_POOL_MAXPG, p->pool.pr_maxpages);
415:
416: print_fld_size(FLD_POOL_IDLE, p->pool.pr_nidle);
417:
418: end_line();
1.8 mpi 419: }
420:
421: int
422: pool_keyboard_callback(int ch)
423: {
424: switch (ch) {
425: case 'A':
426: print_all ^= 1;
427: gotsig_alarm = 1;
428: default:
429: return keyboard_callback(ch);
430: };
431:
432: return (1);
1.12 dlg 433: }
434:
435: int
436: pool_cache_read(void)
437: {
438: struct pool_cache_info *pc;
439: int np, i;
440:
441: np = pool_get_npools();
442: if (np == -1) {
443: error("sysctl(npools): %s", strerror(errno));
444: return (-1);
445: }
446:
447: if (np > num_pool_caches) {
448: pc = reallocarray(pool_caches, np, sizeof(*pc));
449: if (pc == NULL) {
450: error("realloc: %s", strerror(errno));
451: return (-1);
452: }
453: /* commit to using the new memory */
454: pool_caches = pc;
455:
456: for (i = num_pool_caches; i < np; i++) {
457: pc = &pool_caches[i];
458: pc->name[0] = '\0';
459:
460: pc->cache_cpus = reallocarray(NULL, ncpusfound,
461: sizeof(*pc->cache_cpus));
462: if (pc->cache_cpus == NULL) {
463: error("malloc cache cpus: %s", strerror(errno));
464: goto unalloc;
465: }
466: }
467:
468: /* commit to using the new cache_infos */
469: num_pool_caches = np;
470: }
471:
472: for (i = 0; i < num_pool_caches; i++) {
473: pc = &pool_caches[i];
474: np = i + 1;
475:
476: if (pool_get_cache(np, &pc->cache) < 0 ||
477: pool_get_cache_cpus(np, pc->cache_cpus, ncpusfound) < 0) {
478: pc->name[0] = '\0';
479: continue;
480: }
481:
482: if (pool_get_name(np, pc->name, sizeof(pc->name)) < 0)
483: snprintf(pc->name, sizeof(pc->name), "#%d#", i + 1);
484: }
485:
486: return 0;
487:
488: unalloc:
489: while (i > num_pool_caches) {
490: pc = &pool_caches[--i];
491: free(pc->cache_cpus);
492: }
1.14 florian 493: return (-1);
1.12 dlg 494: }
495:
496: void
497: pool_cache_sort(void)
498: {
499: /* XXX */
500: order_type *ordering;
501:
502: if (curr_mgr == NULL)
503: return;
504:
505: ordering = curr_mgr->order_curr;
506:
507: if (ordering == NULL)
508: return;
509: if (ordering->func == NULL)
510: return;
511: if (pools == NULL)
512: return;
513: if (num_pools <= 0)
514: return;
515:
516: mergesort(pools, num_pools, sizeof(struct pool_info), ordering->func);
517: }
518:
519: void
520: pool_cache_print(void)
521: {
522: struct pool_cache_info *pc;
523: int i, n, count = 0;
524:
525: if (pool_caches == NULL)
526: return;
527:
528: for (n = i = 0; i < num_pool_caches; i++) {
529: pc = &pool_caches[i];
530: if (pc->name[0] == '\0')
531: continue;
532:
533: if (n++ < dispstart)
534: continue;
535:
536: pool_cache_show(pc);
537: count++;
538: if (maxprint > 0 && count >= maxprint)
539: break;
540: }
541: }
542:
543: void
544: pool_cache_show(const struct pool_cache_info *pc)
545: {
546: const struct kinfo_pool_cache *kpc;
547: const struct kinfo_pool_cache_cpu *kpcc;
548: int cpu;
549:
550: kpc = &pc->cache;
551:
552: print_fld_str(FLD_POOL_CACHE_NAME, pc->name);
553: print_fld_uint(FLD_POOL_CACHE_LEN, kpc->pr_len);
1.13 dlg 554: print_fld_uint(FLD_POOL_CACHE_IDLE, kpc->pr_nitems);
1.15 dlg 555: print_fld_size(FLD_POOL_CACHE_NGC, kpc->pr_ngc);
1.12 dlg 556:
557: for (cpu = 0; cpu < ncpusfound; cpu++) {
558: kpcc = &pc->cache_cpus[cpu];
559:
560: print_fld_uint(FLD_POOL_CACHE_CPU, kpcc->pr_cpu);
561:
562: print_fld_size(FLD_POOL_CACHE_GET, kpcc->pr_nget);
563: print_fld_size(FLD_POOL_CACHE_PUT, kpcc->pr_nput);
564: print_fld_size(FLD_POOL_CACHE_LGET, kpcc->pr_nlget);
565: print_fld_size(FLD_POOL_CACHE_LPUT, kpcc->pr_nlput);
566: end_line();
567: }
568:
569: }
570:
571: static int
572: pool_get_npools(void)
573: {
574: int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_NPOOLS };
575:
576: return (sysctl_rdint(mib, nitems(mib)));
577: }
578:
579: static int
580: pool_get_cache(int pool, struct kinfo_pool_cache *kpc)
581: {
582: int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_CACHE, pool };
583: size_t len = sizeof(*kpc);
584:
585: return (sysctl(mib, nitems(mib), kpc, &len, NULL, 0));
586: }
587:
588: static int
589: pool_get_cache_cpus(int pool, struct kinfo_pool_cache_cpu *kpcc,
590: unsigned int ncpus)
591: {
592: int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_CACHE_CPUS, pool };
593: size_t len = sizeof(*kpcc) * ncpus;
594:
595: return (sysctl(mib, nitems(mib), kpcc, &len, NULL, 0));
596: }
597:
598: static int
599: pool_get_name(int pool, char *name, size_t len)
600: {
601: int mib[] = { CTL_KERN, KERN_POOL, KERN_POOL_NAME, pool };
602:
603: return (sysctl(mib, nitems(mib), name, &len, NULL, 0));
604: }
605:
606: static int
607: hw_ncpusfound(void)
608: {
609: int mib[] = { CTL_HW, HW_NCPUFOUND };
610:
611: return (sysctl_rdint(mib, nitems(mib)));
612: }
613:
614: static int
615: sysctl_rdint(const int *mib, unsigned int nmib)
616: {
617: int i;
618: size_t size = sizeof(i);
619:
620: if (sysctl(mib, nmib, &i, &size, NULL, 0) == -1)
621: return (-1);
622:
623: return (i);
1.1 canacar 624: }