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