1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 4 #include <stdio.h> 5 #include <string.h> 6 7 #include <ynl.h> 8 9 #include <net/if.h> 10 11 #include "netdev-user.h" 12 13 struct stat { 14 unsigned int ifc; 15 16 struct { 17 unsigned int cnt; 18 size_t refs, bytes; 19 } live[2]; 20 21 size_t alloc_slow, alloc_fast, recycle_ring, recycle_cache; 22 }; 23 24 struct stats_array { 25 unsigned int i, max; 26 struct stat *s; 27 }; 28 29 static struct stat *find_ifc(struct stats_array *a, unsigned int ifindex) 30 { 31 unsigned int i; 32 33 for (i = 0; i < a->i; i++) { 34 if (a->s[i].ifc == ifindex) 35 return &a->s[i]; 36 } 37 38 a->i++; 39 if (a->i == a->max) { 40 a->max *= 2; 41 a->s = reallocarray(a->s, a->max, sizeof(*a->s)); 42 } 43 a->s[i].ifc = ifindex; 44 return &a->s[i]; 45 } 46 47 static void count(struct stat *s, unsigned int l, 48 struct netdev_page_pool_get_rsp *pp) 49 { 50 s->live[l].cnt++; 51 if (pp->_present.inflight) 52 s->live[l].refs += pp->inflight; 53 if (pp->_present.inflight_mem) 54 s->live[l].bytes += pp->inflight_mem; 55 } 56 57 int main(int argc, char **argv) 58 { 59 struct netdev_page_pool_stats_get_list *pp_stats; 60 struct netdev_page_pool_get_list *pools; 61 struct stats_array a = {}; 62 struct ynl_error yerr; 63 struct ynl_sock *ys; 64 65 ys = ynl_sock_create(&ynl_netdev_family, &yerr); 66 if (!ys) { 67 fprintf(stderr, "YNL: %s\n", yerr.msg); 68 return 1; 69 } 70 71 a.max = 128; 72 a.s = calloc(a.max, sizeof(*a.s)); 73 if (!a.s) 74 goto err_close; 75 76 pools = netdev_page_pool_get_dump(ys); 77 if (!pools) 78 goto err_free; 79 80 ynl_dump_foreach(pools, pp) { 81 struct stat *s = find_ifc(&a, pp->ifindex); 82 83 count(s, 1, pp); 84 if (pp->_present.detach_time) 85 count(s, 0, pp); 86 } 87 netdev_page_pool_get_list_free(pools); 88 89 pp_stats = netdev_page_pool_stats_get_dump(ys); 90 if (!pp_stats) 91 goto err_free; 92 93 ynl_dump_foreach(pp_stats, pp) { 94 struct stat *s = find_ifc(&a, pp->info.ifindex); 95 96 if (pp->_present.alloc_fast) 97 s->alloc_fast += pp->alloc_fast; 98 if (pp->_present.alloc_refill) 99 s->alloc_fast += pp->alloc_refill; 100 if (pp->_present.alloc_slow) 101 s->alloc_slow += pp->alloc_slow; 102 if (pp->_present.recycle_ring) 103 s->recycle_ring += pp->recycle_ring; 104 if (pp->_present.recycle_cached) 105 s->recycle_cache += pp->recycle_cached; 106 } 107 netdev_page_pool_stats_get_list_free(pp_stats); 108 109 for (unsigned int i = 0; i < a.i; i++) { 110 char ifname[IF_NAMESIZE]; 111 struct stat *s = &a.s[i]; 112 const char *name; 113 double recycle; 114 115 if (!s->ifc) { 116 name = "<orphan>\t"; 117 } else { 118 name = if_indextoname(s->ifc, ifname); 119 if (name) 120 printf("%8s", name); 121 printf("[%d]\t", s->ifc); 122 } 123 124 printf("page pools: %u (zombies: %u)\n", 125 s->live[1].cnt, s->live[0].cnt); 126 printf("\t\trefs: %zu bytes: %zu (refs: %zu bytes: %zu)\n", 127 s->live[1].refs, s->live[1].bytes, 128 s->live[0].refs, s->live[0].bytes); 129 130 /* We don't know how many pages are sitting in cache and ring 131 * so we will under-count the recycling rate a bit. 132 */ 133 recycle = (double)(s->recycle_ring + s->recycle_cache) / 134 (s->alloc_fast + s->alloc_slow) * 100; 135 printf("\t\trecycling: %.1lf%% (alloc: %zu:%zu recycle: %zu:%zu)\n", 136 recycle, s->alloc_slow, s->alloc_fast, 137 s->recycle_ring, s->recycle_cache); 138 } 139 140 ynl_sock_destroy(ys); 141 return 0; 142 143 err_free: 144 free(a.s); 145 err_close: 146 fprintf(stderr, "YNL: %s\n", ys->err.msg); 147 ynl_sock_destroy(ys); 148 return 2; 149 } 150