File Index
Files and directories
Sign in
FILE

page-pool.c

File preview with syntax highlighting.

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