xref: /linux/tools/net/ynl/ynltool/page-pool.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
1*124dac9bSJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2*124dac9bSJakub Kicinski 
3*124dac9bSJakub Kicinski #include <stdio.h>
4*124dac9bSJakub Kicinski #include <stdlib.h>
5*124dac9bSJakub Kicinski #include <string.h>
6*124dac9bSJakub Kicinski #include <errno.h>
7*124dac9bSJakub Kicinski #include <net/if.h>
8*124dac9bSJakub Kicinski 
9*124dac9bSJakub Kicinski #include <ynl.h>
10*124dac9bSJakub Kicinski #include "netdev-user.h"
11*124dac9bSJakub Kicinski 
12*124dac9bSJakub Kicinski #include "main.h"
13*124dac9bSJakub Kicinski 
14*124dac9bSJakub Kicinski struct pp_stat {
15*124dac9bSJakub Kicinski 	unsigned int ifc;
16*124dac9bSJakub Kicinski 
17*124dac9bSJakub Kicinski 	struct {
18*124dac9bSJakub Kicinski 		unsigned int cnt;
19*124dac9bSJakub Kicinski 		size_t refs, bytes;
20*124dac9bSJakub Kicinski 	} live[2];
21*124dac9bSJakub Kicinski 
22*124dac9bSJakub Kicinski 	size_t alloc_slow, alloc_fast, recycle_ring, recycle_cache;
23*124dac9bSJakub Kicinski };
24*124dac9bSJakub Kicinski 
25*124dac9bSJakub Kicinski struct pp_stats_array {
26*124dac9bSJakub Kicinski 	unsigned int i, max;
27*124dac9bSJakub Kicinski 	struct pp_stat *s;
28*124dac9bSJakub Kicinski };
29*124dac9bSJakub Kicinski 
30*124dac9bSJakub Kicinski static struct pp_stat *find_ifc(struct pp_stats_array *a, unsigned int ifindex)
31*124dac9bSJakub Kicinski {
32*124dac9bSJakub Kicinski 	unsigned int i;
33*124dac9bSJakub Kicinski 
34*124dac9bSJakub Kicinski 	for (i = 0; i < a->i; i++) {
35*124dac9bSJakub Kicinski 		if (a->s[i].ifc == ifindex)
36*124dac9bSJakub Kicinski 			return &a->s[i];
37*124dac9bSJakub Kicinski 	}
38*124dac9bSJakub Kicinski 
39*124dac9bSJakub Kicinski 	a->i++;
40*124dac9bSJakub Kicinski 	if (a->i == a->max) {
41*124dac9bSJakub Kicinski 		a->max *= 2;
42*124dac9bSJakub Kicinski 		a->s = reallocarray(a->s, a->max, sizeof(*a->s));
43*124dac9bSJakub Kicinski 	}
44*124dac9bSJakub Kicinski 	a->s[i].ifc = ifindex;
45*124dac9bSJakub Kicinski 	return &a->s[i];
46*124dac9bSJakub Kicinski }
47*124dac9bSJakub Kicinski 
48*124dac9bSJakub Kicinski static void count_pool(struct pp_stat *s, unsigned int l,
49*124dac9bSJakub Kicinski 		       struct netdev_page_pool_get_rsp *pp)
50*124dac9bSJakub Kicinski {
51*124dac9bSJakub Kicinski 	s->live[l].cnt++;
52*124dac9bSJakub Kicinski 	if (pp->_present.inflight)
53*124dac9bSJakub Kicinski 		s->live[l].refs += pp->inflight;
54*124dac9bSJakub Kicinski 	if (pp->_present.inflight_mem)
55*124dac9bSJakub Kicinski 		s->live[l].bytes += pp->inflight_mem;
56*124dac9bSJakub Kicinski }
57*124dac9bSJakub Kicinski 
58*124dac9bSJakub Kicinski /* We don't know how many pages are sitting in cache and ring
59*124dac9bSJakub Kicinski  * so we will under-count the recycling rate a bit.
60*124dac9bSJakub Kicinski  */
61*124dac9bSJakub Kicinski static void print_json_recycling_stats(struct pp_stat *s)
62*124dac9bSJakub Kicinski {
63*124dac9bSJakub Kicinski 	double recycle;
64*124dac9bSJakub Kicinski 
65*124dac9bSJakub Kicinski 	if (s->alloc_fast + s->alloc_slow) {
66*124dac9bSJakub Kicinski 		recycle = (double)(s->recycle_ring + s->recycle_cache) /
67*124dac9bSJakub Kicinski 			(s->alloc_fast + s->alloc_slow) * 100;
68*124dac9bSJakub Kicinski 		jsonw_float_field(json_wtr, "recycling_pct", recycle);
69*124dac9bSJakub Kicinski 	}
70*124dac9bSJakub Kicinski 
71*124dac9bSJakub Kicinski 	jsonw_name(json_wtr, "alloc");
72*124dac9bSJakub Kicinski 	jsonw_start_object(json_wtr);
73*124dac9bSJakub Kicinski 	jsonw_uint_field(json_wtr, "slow", s->alloc_slow);
74*124dac9bSJakub Kicinski 	jsonw_uint_field(json_wtr, "fast", s->alloc_fast);
75*124dac9bSJakub Kicinski 	jsonw_end_object(json_wtr);
76*124dac9bSJakub Kicinski 
77*124dac9bSJakub Kicinski 	jsonw_name(json_wtr, "recycle");
78*124dac9bSJakub Kicinski 	jsonw_start_object(json_wtr);
79*124dac9bSJakub Kicinski 	jsonw_uint_field(json_wtr, "ring", s->recycle_ring);
80*124dac9bSJakub Kicinski 	jsonw_uint_field(json_wtr, "cache", s->recycle_cache);
81*124dac9bSJakub Kicinski 	jsonw_end_object(json_wtr);
82*124dac9bSJakub Kicinski }
83*124dac9bSJakub Kicinski 
84*124dac9bSJakub Kicinski static void print_plain_recycling_stats(struct pp_stat *s)
85*124dac9bSJakub Kicinski {
86*124dac9bSJakub Kicinski 	double recycle;
87*124dac9bSJakub Kicinski 
88*124dac9bSJakub Kicinski 	if (s->alloc_fast + s->alloc_slow) {
89*124dac9bSJakub Kicinski 		recycle = (double)(s->recycle_ring + s->recycle_cache) /
90*124dac9bSJakub Kicinski 			(s->alloc_fast + s->alloc_slow) * 100;
91*124dac9bSJakub Kicinski 		printf("recycling: %.1lf%% (alloc: %zu:%zu recycle: %zu:%zu)",
92*124dac9bSJakub Kicinski 		       recycle, s->alloc_slow, s->alloc_fast,
93*124dac9bSJakub Kicinski 		       s->recycle_ring, s->recycle_cache);
94*124dac9bSJakub Kicinski 	}
95*124dac9bSJakub Kicinski }
96*124dac9bSJakub Kicinski 
97*124dac9bSJakub Kicinski static void print_json_stats(struct pp_stats_array *a)
98*124dac9bSJakub Kicinski {
99*124dac9bSJakub Kicinski 	jsonw_start_array(json_wtr);
100*124dac9bSJakub Kicinski 
101*124dac9bSJakub Kicinski 	for (unsigned int i = 0; i < a->i; i++) {
102*124dac9bSJakub Kicinski 		char ifname[IF_NAMESIZE];
103*124dac9bSJakub Kicinski 		struct pp_stat *s = &a->s[i];
104*124dac9bSJakub Kicinski 		const char *name;
105*124dac9bSJakub Kicinski 
106*124dac9bSJakub Kicinski 		jsonw_start_object(json_wtr);
107*124dac9bSJakub Kicinski 
108*124dac9bSJakub Kicinski 		if (!s->ifc) {
109*124dac9bSJakub Kicinski 			jsonw_string_field(json_wtr, "ifname", "<orphan>");
110*124dac9bSJakub Kicinski 			jsonw_uint_field(json_wtr, "ifindex", 0);
111*124dac9bSJakub Kicinski 		} else {
112*124dac9bSJakub Kicinski 			name = if_indextoname(s->ifc, ifname);
113*124dac9bSJakub Kicinski 			if (name)
114*124dac9bSJakub Kicinski 				jsonw_string_field(json_wtr, "ifname", name);
115*124dac9bSJakub Kicinski 			jsonw_uint_field(json_wtr, "ifindex", s->ifc);
116*124dac9bSJakub Kicinski 		}
117*124dac9bSJakub Kicinski 
118*124dac9bSJakub Kicinski 		jsonw_uint_field(json_wtr, "page_pools", s->live[1].cnt);
119*124dac9bSJakub Kicinski 		jsonw_uint_field(json_wtr, "zombies", s->live[0].cnt);
120*124dac9bSJakub Kicinski 
121*124dac9bSJakub Kicinski 		jsonw_name(json_wtr, "live");
122*124dac9bSJakub Kicinski 		jsonw_start_object(json_wtr);
123*124dac9bSJakub Kicinski 		jsonw_uint_field(json_wtr, "refs", s->live[1].refs);
124*124dac9bSJakub Kicinski 		jsonw_uint_field(json_wtr, "bytes", s->live[1].bytes);
125*124dac9bSJakub Kicinski 		jsonw_end_object(json_wtr);
126*124dac9bSJakub Kicinski 
127*124dac9bSJakub Kicinski 		jsonw_name(json_wtr, "zombie");
128*124dac9bSJakub Kicinski 		jsonw_start_object(json_wtr);
129*124dac9bSJakub Kicinski 		jsonw_uint_field(json_wtr, "refs", s->live[0].refs);
130*124dac9bSJakub Kicinski 		jsonw_uint_field(json_wtr, "bytes", s->live[0].bytes);
131*124dac9bSJakub Kicinski 		jsonw_end_object(json_wtr);
132*124dac9bSJakub Kicinski 
133*124dac9bSJakub Kicinski 		if (s->alloc_fast || s->alloc_slow)
134*124dac9bSJakub Kicinski 			print_json_recycling_stats(s);
135*124dac9bSJakub Kicinski 
136*124dac9bSJakub Kicinski 		jsonw_end_object(json_wtr);
137*124dac9bSJakub Kicinski 	}
138*124dac9bSJakub Kicinski 
139*124dac9bSJakub Kicinski 	jsonw_end_array(json_wtr);
140*124dac9bSJakub Kicinski }
141*124dac9bSJakub Kicinski 
142*124dac9bSJakub Kicinski static void print_plain_stats(struct pp_stats_array *a)
143*124dac9bSJakub Kicinski {
144*124dac9bSJakub Kicinski 	for (unsigned int i = 0; i < a->i; i++) {
145*124dac9bSJakub Kicinski 		char ifname[IF_NAMESIZE];
146*124dac9bSJakub Kicinski 		struct pp_stat *s = &a->s[i];
147*124dac9bSJakub Kicinski 		const char *name;
148*124dac9bSJakub Kicinski 
149*124dac9bSJakub Kicinski 		if (!s->ifc) {
150*124dac9bSJakub Kicinski 			printf("<orphan>\t");
151*124dac9bSJakub Kicinski 		} else {
152*124dac9bSJakub Kicinski 			name = if_indextoname(s->ifc, ifname);
153*124dac9bSJakub Kicinski 			if (name)
154*124dac9bSJakub Kicinski 				printf("%8s", name);
155*124dac9bSJakub Kicinski 			printf("[%u]\t", s->ifc);
156*124dac9bSJakub Kicinski 		}
157*124dac9bSJakub Kicinski 
158*124dac9bSJakub Kicinski 		printf("page pools: %u (zombies: %u)\n",
159*124dac9bSJakub Kicinski 		       s->live[1].cnt, s->live[0].cnt);
160*124dac9bSJakub Kicinski 		printf("\t\trefs: %zu bytes: %zu (refs: %zu bytes: %zu)\n",
161*124dac9bSJakub Kicinski 		       s->live[1].refs, s->live[1].bytes,
162*124dac9bSJakub Kicinski 		       s->live[0].refs, s->live[0].bytes);
163*124dac9bSJakub Kicinski 
164*124dac9bSJakub Kicinski 		if (s->alloc_fast || s->alloc_slow) {
165*124dac9bSJakub Kicinski 			printf("\t\t");
166*124dac9bSJakub Kicinski 			print_plain_recycling_stats(s);
167*124dac9bSJakub Kicinski 			printf("\n");
168*124dac9bSJakub Kicinski 		}
169*124dac9bSJakub Kicinski 	}
170*124dac9bSJakub Kicinski }
171*124dac9bSJakub Kicinski 
172*124dac9bSJakub Kicinski static bool
173*124dac9bSJakub Kicinski find_pool_stat_in_list(struct netdev_page_pool_stats_get_list *pp_stats,
174*124dac9bSJakub Kicinski 		       __u64 pool_id, struct pp_stat *pstat)
175*124dac9bSJakub Kicinski {
176*124dac9bSJakub Kicinski 	ynl_dump_foreach(pp_stats, pp) {
177*124dac9bSJakub Kicinski 		if (!pp->_present.info || !pp->info._present.id)
178*124dac9bSJakub Kicinski 			continue;
179*124dac9bSJakub Kicinski 		if (pp->info.id != pool_id)
180*124dac9bSJakub Kicinski 			continue;
181*124dac9bSJakub Kicinski 
182*124dac9bSJakub Kicinski 		memset(pstat, 0, sizeof(*pstat));
183*124dac9bSJakub Kicinski 		if (pp->_present.alloc_fast)
184*124dac9bSJakub Kicinski 			pstat->alloc_fast = pp->alloc_fast;
185*124dac9bSJakub Kicinski 		if (pp->_present.alloc_refill)
186*124dac9bSJakub Kicinski 			pstat->alloc_fast += pp->alloc_refill;
187*124dac9bSJakub Kicinski 		if (pp->_present.alloc_slow)
188*124dac9bSJakub Kicinski 			pstat->alloc_slow = pp->alloc_slow;
189*124dac9bSJakub Kicinski 		if (pp->_present.recycle_ring)
190*124dac9bSJakub Kicinski 			pstat->recycle_ring = pp->recycle_ring;
191*124dac9bSJakub Kicinski 		if (pp->_present.recycle_cached)
192*124dac9bSJakub Kicinski 			pstat->recycle_cache = pp->recycle_cached;
193*124dac9bSJakub Kicinski 		return true;
194*124dac9bSJakub Kicinski 	}
195*124dac9bSJakub Kicinski 	return false;
196*124dac9bSJakub Kicinski }
197*124dac9bSJakub Kicinski 
198*124dac9bSJakub Kicinski static void
199*124dac9bSJakub Kicinski print_json_pool_list(struct netdev_page_pool_get_list *pools,
200*124dac9bSJakub Kicinski 		     struct netdev_page_pool_stats_get_list *pp_stats,
201*124dac9bSJakub Kicinski 		     bool zombies_only)
202*124dac9bSJakub Kicinski {
203*124dac9bSJakub Kicinski 	jsonw_start_array(json_wtr);
204*124dac9bSJakub Kicinski 
205*124dac9bSJakub Kicinski 	ynl_dump_foreach(pools, pp) {
206*124dac9bSJakub Kicinski 		char ifname[IF_NAMESIZE];
207*124dac9bSJakub Kicinski 		struct pp_stat pstat;
208*124dac9bSJakub Kicinski 		const char *name;
209*124dac9bSJakub Kicinski 
210*124dac9bSJakub Kicinski 		if (zombies_only && !pp->_present.detach_time)
211*124dac9bSJakub Kicinski 			continue;
212*124dac9bSJakub Kicinski 
213*124dac9bSJakub Kicinski 		jsonw_start_object(json_wtr);
214*124dac9bSJakub Kicinski 
215*124dac9bSJakub Kicinski 		jsonw_uint_field(json_wtr, "id", pp->id);
216*124dac9bSJakub Kicinski 
217*124dac9bSJakub Kicinski 		if (pp->_present.ifindex) {
218*124dac9bSJakub Kicinski 			name = if_indextoname(pp->ifindex, ifname);
219*124dac9bSJakub Kicinski 			if (name)
220*124dac9bSJakub Kicinski 				jsonw_string_field(json_wtr, "ifname", name);
221*124dac9bSJakub Kicinski 			jsonw_uint_field(json_wtr, "ifindex", pp->ifindex);
222*124dac9bSJakub Kicinski 		}
223*124dac9bSJakub Kicinski 
224*124dac9bSJakub Kicinski 		if (pp->_present.napi_id)
225*124dac9bSJakub Kicinski 			jsonw_uint_field(json_wtr, "napi_id", pp->napi_id);
226*124dac9bSJakub Kicinski 
227*124dac9bSJakub Kicinski 		if (pp->_present.inflight)
228*124dac9bSJakub Kicinski 			jsonw_uint_field(json_wtr, "refs", pp->inflight);
229*124dac9bSJakub Kicinski 
230*124dac9bSJakub Kicinski 		if (pp->_present.inflight_mem)
231*124dac9bSJakub Kicinski 			jsonw_uint_field(json_wtr, "bytes", pp->inflight_mem);
232*124dac9bSJakub Kicinski 
233*124dac9bSJakub Kicinski 		if (pp->_present.detach_time)
234*124dac9bSJakub Kicinski 			jsonw_uint_field(json_wtr, "detach_time", pp->detach_time);
235*124dac9bSJakub Kicinski 
236*124dac9bSJakub Kicinski 		if (pp->_present.dmabuf)
237*124dac9bSJakub Kicinski 			jsonw_uint_field(json_wtr, "dmabuf", pp->dmabuf);
238*124dac9bSJakub Kicinski 
239*124dac9bSJakub Kicinski 		if (find_pool_stat_in_list(pp_stats, pp->id, &pstat) &&
240*124dac9bSJakub Kicinski 		    (pstat.alloc_fast || pstat.alloc_slow))
241*124dac9bSJakub Kicinski 			print_json_recycling_stats(&pstat);
242*124dac9bSJakub Kicinski 
243*124dac9bSJakub Kicinski 		jsonw_end_object(json_wtr);
244*124dac9bSJakub Kicinski 	}
245*124dac9bSJakub Kicinski 
246*124dac9bSJakub Kicinski 	jsonw_end_array(json_wtr);
247*124dac9bSJakub Kicinski }
248*124dac9bSJakub Kicinski 
249*124dac9bSJakub Kicinski static void
250*124dac9bSJakub Kicinski print_plain_pool_list(struct netdev_page_pool_get_list *pools,
251*124dac9bSJakub Kicinski 		      struct netdev_page_pool_stats_get_list *pp_stats,
252*124dac9bSJakub Kicinski 		      bool zombies_only)
253*124dac9bSJakub Kicinski {
254*124dac9bSJakub Kicinski 	ynl_dump_foreach(pools, pp) {
255*124dac9bSJakub Kicinski 		char ifname[IF_NAMESIZE];
256*124dac9bSJakub Kicinski 		struct pp_stat pstat;
257*124dac9bSJakub Kicinski 		const char *name;
258*124dac9bSJakub Kicinski 
259*124dac9bSJakub Kicinski 		if (zombies_only && !pp->_present.detach_time)
260*124dac9bSJakub Kicinski 			continue;
261*124dac9bSJakub Kicinski 
262*124dac9bSJakub Kicinski 		printf("pool id: %llu", pp->id);
263*124dac9bSJakub Kicinski 
264*124dac9bSJakub Kicinski 		if (pp->_present.ifindex) {
265*124dac9bSJakub Kicinski 			name = if_indextoname(pp->ifindex, ifname);
266*124dac9bSJakub Kicinski 			if (name)
267*124dac9bSJakub Kicinski 				printf("  dev: %s", name);
268*124dac9bSJakub Kicinski 			printf("[%u]", pp->ifindex);
269*124dac9bSJakub Kicinski 		}
270*124dac9bSJakub Kicinski 
271*124dac9bSJakub Kicinski 		if (pp->_present.napi_id)
272*124dac9bSJakub Kicinski 			printf("  napi: %llu", pp->napi_id);
273*124dac9bSJakub Kicinski 
274*124dac9bSJakub Kicinski 		printf("\n");
275*124dac9bSJakub Kicinski 
276*124dac9bSJakub Kicinski 		if (pp->_present.inflight || pp->_present.inflight_mem) {
277*124dac9bSJakub Kicinski 			printf("  inflight:");
278*124dac9bSJakub Kicinski 			if (pp->_present.inflight)
279*124dac9bSJakub Kicinski 				printf(" %llu pages", pp->inflight);
280*124dac9bSJakub Kicinski 			if (pp->_present.inflight_mem)
281*124dac9bSJakub Kicinski 				printf(" %llu bytes", pp->inflight_mem);
282*124dac9bSJakub Kicinski 			printf("\n");
283*124dac9bSJakub Kicinski 		}
284*124dac9bSJakub Kicinski 
285*124dac9bSJakub Kicinski 		if (pp->_present.detach_time)
286*124dac9bSJakub Kicinski 			printf("  detached: %llu\n", pp->detach_time);
287*124dac9bSJakub Kicinski 
288*124dac9bSJakub Kicinski 		if (pp->_present.dmabuf)
289*124dac9bSJakub Kicinski 			printf("  dmabuf: %u\n", pp->dmabuf);
290*124dac9bSJakub Kicinski 
291*124dac9bSJakub Kicinski 		if (find_pool_stat_in_list(pp_stats, pp->id, &pstat) &&
292*124dac9bSJakub Kicinski 		    (pstat.alloc_fast || pstat.alloc_slow)) {
293*124dac9bSJakub Kicinski 			printf("  ");
294*124dac9bSJakub Kicinski 			print_plain_recycling_stats(&pstat);
295*124dac9bSJakub Kicinski 			printf("\n");
296*124dac9bSJakub Kicinski 		}
297*124dac9bSJakub Kicinski 	}
298*124dac9bSJakub Kicinski }
299*124dac9bSJakub Kicinski 
300*124dac9bSJakub Kicinski static void aggregate_device_stats(struct pp_stats_array *a,
301*124dac9bSJakub Kicinski 				   struct netdev_page_pool_get_list *pools,
302*124dac9bSJakub Kicinski 				   struct netdev_page_pool_stats_get_list *pp_stats)
303*124dac9bSJakub Kicinski {
304*124dac9bSJakub Kicinski 	ynl_dump_foreach(pools, pp) {
305*124dac9bSJakub Kicinski 		struct pp_stat *s = find_ifc(a, pp->ifindex);
306*124dac9bSJakub Kicinski 
307*124dac9bSJakub Kicinski 		count_pool(s, 1, pp);
308*124dac9bSJakub Kicinski 		if (pp->_present.detach_time)
309*124dac9bSJakub Kicinski 			count_pool(s, 0, pp);
310*124dac9bSJakub Kicinski 	}
311*124dac9bSJakub Kicinski 
312*124dac9bSJakub Kicinski 	ynl_dump_foreach(pp_stats, pp) {
313*124dac9bSJakub Kicinski 		struct pp_stat *s = find_ifc(a, pp->info.ifindex);
314*124dac9bSJakub Kicinski 
315*124dac9bSJakub Kicinski 		if (pp->_present.alloc_fast)
316*124dac9bSJakub Kicinski 			s->alloc_fast += pp->alloc_fast;
317*124dac9bSJakub Kicinski 		if (pp->_present.alloc_refill)
318*124dac9bSJakub Kicinski 			s->alloc_fast += pp->alloc_refill;
319*124dac9bSJakub Kicinski 		if (pp->_present.alloc_slow)
320*124dac9bSJakub Kicinski 			s->alloc_slow += pp->alloc_slow;
321*124dac9bSJakub Kicinski 		if (pp->_present.recycle_ring)
322*124dac9bSJakub Kicinski 			s->recycle_ring += pp->recycle_ring;
323*124dac9bSJakub Kicinski 		if (pp->_present.recycle_cached)
324*124dac9bSJakub Kicinski 			s->recycle_cache += pp->recycle_cached;
325*124dac9bSJakub Kicinski 	}
326*124dac9bSJakub Kicinski }
327*124dac9bSJakub Kicinski 
328*124dac9bSJakub Kicinski static int do_stats(int argc, char **argv)
329*124dac9bSJakub Kicinski {
330*124dac9bSJakub Kicinski 	struct netdev_page_pool_stats_get_list *pp_stats;
331*124dac9bSJakub Kicinski 	struct netdev_page_pool_get_list *pools;
332*124dac9bSJakub Kicinski 	enum {
333*124dac9bSJakub Kicinski 		GROUP_BY_DEVICE,
334*124dac9bSJakub Kicinski 		GROUP_BY_POOL,
335*124dac9bSJakub Kicinski 	} group_by = GROUP_BY_DEVICE;
336*124dac9bSJakub Kicinski 	bool zombies_only = false;
337*124dac9bSJakub Kicinski 	struct pp_stats_array a = {};
338*124dac9bSJakub Kicinski 	struct ynl_error yerr;
339*124dac9bSJakub Kicinski 	struct ynl_sock *ys;
340*124dac9bSJakub Kicinski 	int ret = 0;
341*124dac9bSJakub Kicinski 
342*124dac9bSJakub Kicinski 	/* Parse options */
343*124dac9bSJakub Kicinski 	while (argc > 0) {
344*124dac9bSJakub Kicinski 		if (is_prefix(*argv, "group-by")) {
345*124dac9bSJakub Kicinski 			NEXT_ARG();
346*124dac9bSJakub Kicinski 
347*124dac9bSJakub Kicinski 			if (!REQ_ARGS(1))
348*124dac9bSJakub Kicinski 				return -1;
349*124dac9bSJakub Kicinski 
350*124dac9bSJakub Kicinski 			if (is_prefix(*argv, "device")) {
351*124dac9bSJakub Kicinski 				group_by = GROUP_BY_DEVICE;
352*124dac9bSJakub Kicinski 			} else if (is_prefix(*argv, "pp") ||
353*124dac9bSJakub Kicinski 				   is_prefix(*argv, "page-pool") ||
354*124dac9bSJakub Kicinski 				   is_prefix(*argv, "none")) {
355*124dac9bSJakub Kicinski 				group_by = GROUP_BY_POOL;
356*124dac9bSJakub Kicinski 			} else {
357*124dac9bSJakub Kicinski 				p_err("invalid group-by value '%s'", *argv);
358*124dac9bSJakub Kicinski 				return -1;
359*124dac9bSJakub Kicinski 			}
360*124dac9bSJakub Kicinski 			NEXT_ARG();
361*124dac9bSJakub Kicinski 		} else if (is_prefix(*argv, "zombies")) {
362*124dac9bSJakub Kicinski 			zombies_only = true;
363*124dac9bSJakub Kicinski 			group_by = GROUP_BY_POOL;
364*124dac9bSJakub Kicinski 			NEXT_ARG();
365*124dac9bSJakub Kicinski 		} else {
366*124dac9bSJakub Kicinski 			p_err("unknown option '%s'", *argv);
367*124dac9bSJakub Kicinski 			return -1;
368*124dac9bSJakub Kicinski 		}
369*124dac9bSJakub Kicinski 	}
370*124dac9bSJakub Kicinski 
371*124dac9bSJakub Kicinski 	ys = ynl_sock_create(&ynl_netdev_family, &yerr);
372*124dac9bSJakub Kicinski 	if (!ys) {
373*124dac9bSJakub Kicinski 		p_err("YNL: %s", yerr.msg);
374*124dac9bSJakub Kicinski 		return -1;
375*124dac9bSJakub Kicinski 	}
376*124dac9bSJakub Kicinski 
377*124dac9bSJakub Kicinski 	pools = netdev_page_pool_get_dump(ys);
378*124dac9bSJakub Kicinski 	if (!pools) {
379*124dac9bSJakub Kicinski 		p_err("failed to get page pools: %s", ys->err.msg);
380*124dac9bSJakub Kicinski 		ret = -1;
381*124dac9bSJakub Kicinski 		goto exit_close;
382*124dac9bSJakub Kicinski 	}
383*124dac9bSJakub Kicinski 
384*124dac9bSJakub Kicinski 	pp_stats = netdev_page_pool_stats_get_dump(ys);
385*124dac9bSJakub Kicinski 	if (!pp_stats) {
386*124dac9bSJakub Kicinski 		p_err("failed to get page pool stats: %s", ys->err.msg);
387*124dac9bSJakub Kicinski 		ret = -1;
388*124dac9bSJakub Kicinski 		goto exit_free_pp_list;
389*124dac9bSJakub Kicinski 	}
390*124dac9bSJakub Kicinski 
391*124dac9bSJakub Kicinski 	/* If grouping by pool, print individual pools */
392*124dac9bSJakub Kicinski 	if (group_by == GROUP_BY_POOL) {
393*124dac9bSJakub Kicinski 		if (json_output)
394*124dac9bSJakub Kicinski 			print_json_pool_list(pools, pp_stats, zombies_only);
395*124dac9bSJakub Kicinski 		else
396*124dac9bSJakub Kicinski 			print_plain_pool_list(pools, pp_stats, zombies_only);
397*124dac9bSJakub Kicinski 	} else {
398*124dac9bSJakub Kicinski 		/* Aggregated stats mode (group-by device) */
399*124dac9bSJakub Kicinski 		a.max = 64;
400*124dac9bSJakub Kicinski 		a.s = calloc(a.max, sizeof(*a.s));
401*124dac9bSJakub Kicinski 		if (!a.s) {
402*124dac9bSJakub Kicinski 			p_err("failed to allocate stats array");
403*124dac9bSJakub Kicinski 			ret = -1;
404*124dac9bSJakub Kicinski 			goto exit_free_stats_list;
405*124dac9bSJakub Kicinski 		}
406*124dac9bSJakub Kicinski 
407*124dac9bSJakub Kicinski 		aggregate_device_stats(&a, pools, pp_stats);
408*124dac9bSJakub Kicinski 
409*124dac9bSJakub Kicinski 		if (json_output)
410*124dac9bSJakub Kicinski 			print_json_stats(&a);
411*124dac9bSJakub Kicinski 		else
412*124dac9bSJakub Kicinski 			print_plain_stats(&a);
413*124dac9bSJakub Kicinski 
414*124dac9bSJakub Kicinski 		free(a.s);
415*124dac9bSJakub Kicinski 	}
416*124dac9bSJakub Kicinski 
417*124dac9bSJakub Kicinski exit_free_stats_list:
418*124dac9bSJakub Kicinski 	netdev_page_pool_stats_get_list_free(pp_stats);
419*124dac9bSJakub Kicinski exit_free_pp_list:
420*124dac9bSJakub Kicinski 	netdev_page_pool_get_list_free(pools);
421*124dac9bSJakub Kicinski exit_close:
422*124dac9bSJakub Kicinski 	ynl_sock_destroy(ys);
423*124dac9bSJakub Kicinski 	return ret;
424*124dac9bSJakub Kicinski }
425*124dac9bSJakub Kicinski 
426*124dac9bSJakub Kicinski static int do_help(int argc __attribute__((unused)),
427*124dac9bSJakub Kicinski 		   char **argv __attribute__((unused)))
428*124dac9bSJakub Kicinski {
429*124dac9bSJakub Kicinski 	if (json_output) {
430*124dac9bSJakub Kicinski 		jsonw_null(json_wtr);
431*124dac9bSJakub Kicinski 		return 0;
432*124dac9bSJakub Kicinski 	}
433*124dac9bSJakub Kicinski 
434*124dac9bSJakub Kicinski 	fprintf(stderr,
435*124dac9bSJakub Kicinski 		"Usage: %s page-pool { COMMAND | help }\n"
436*124dac9bSJakub Kicinski 		"       %s page-pool stats [ OPTIONS ]\n"
437*124dac9bSJakub Kicinski 		"\n"
438*124dac9bSJakub Kicinski 		"       OPTIONS := { group-by { device | page-pool | none } | zombies }\n"
439*124dac9bSJakub Kicinski 		"\n"
440*124dac9bSJakub Kicinski 		"       stats                   - Display page pool statistics\n"
441*124dac9bSJakub Kicinski 		"       stats group-by device   - Group statistics by network device (default)\n"
442*124dac9bSJakub Kicinski 		"       stats group-by page-pool | pp | none\n"
443*124dac9bSJakub Kicinski 		"                               - Show individual page pool details (no grouping)\n"
444*124dac9bSJakub Kicinski 		"       stats zombies           - Show only zombie page pools (detached but with\n"
445*124dac9bSJakub Kicinski 		"                                 pages in flight). Implies group-by page-pool.\n"
446*124dac9bSJakub Kicinski 		"",
447*124dac9bSJakub Kicinski 		bin_name, bin_name);
448*124dac9bSJakub Kicinski 
449*124dac9bSJakub Kicinski 	return 0;
450*124dac9bSJakub Kicinski }
451*124dac9bSJakub Kicinski 
452*124dac9bSJakub Kicinski static const struct cmd page_pool_cmds[] = {
453*124dac9bSJakub Kicinski 	{ "help",	do_help },
454*124dac9bSJakub Kicinski 	{ "stats",	do_stats },
455*124dac9bSJakub Kicinski 	{ 0 }
456*124dac9bSJakub Kicinski };
457*124dac9bSJakub Kicinski 
458*124dac9bSJakub Kicinski int do_page_pool(int argc, char **argv)
459*124dac9bSJakub Kicinski {
460*124dac9bSJakub Kicinski 	return cmd_select(page_pool_cmds, argc, argv, do_help);
461*124dac9bSJakub Kicinski }
462