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
find_ifc(struct stats_array * a,unsigned int ifindex)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
count(struct stat * s,unsigned int l,struct netdev_page_pool_get_rsp * pp)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
main(int argc,char ** argv)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