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
13struct 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
24struct stats_array {
25	unsigned int i, max;
26	struct stat *s;
27};
28
29static 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
47static 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
57int 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
143err_free:
144	free(a.s);
145err_close:
146	fprintf(stderr, "YNL: %s\n", ys->err.msg);
147	ynl_sock_destroy(ys);
148	return 2;
149}
150