Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: page_pool: expose page pool stats via netlink

Dump the stats into netlink. More clever approaches
like dumping the stats per-CPU for each CPU individually
to see where the packets get consumed can be implemented
in the future.

A trimmed example from a real (but recently booted system):

$ ./cli.py --no-schema --spec netlink/specs/netdev.yaml \
--dump page-pool-stats-get
[{'info': {'id': 19, 'ifindex': 2},
'alloc-empty': 48,
'alloc-fast': 3024,
'alloc-refill': 0,
'alloc-slow': 48,
'alloc-slow-high-order': 0,
'alloc-waive': 0,
'recycle-cache-full': 0,
'recycle-cached': 0,
'recycle-released-refcnt': 0,
'recycle-ring': 0,
'recycle-ring-full': 0},
{'info': {'id': 18, 'ifindex': 2},
'alloc-empty': 66,
'alloc-fast': 11811,
'alloc-refill': 35,
'alloc-slow': 66,
'alloc-slow-high-order': 0,
'alloc-waive': 0,
'recycle-cache-full': 1145,
'recycle-cached': 6541,
'recycle-released-refcnt': 0,
'recycle-ring': 1275,
'recycle-ring-full': 0},
{'info': {'id': 17, 'ifindex': 2},
'alloc-empty': 73,
'alloc-fast': 62099,
'alloc-refill': 413,
...

Acked-by: Jesper Dangaard Brouer <hawk@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Jakub Kicinski and committed by
Paolo Abeni
d49010ad 69cb4952

+250 -9
+78
Documentation/netlink/specs/netdev.yaml
··· 139 139 "re-attached", they are just waiting to disappear. 140 140 Attribute is absent if Page Pool has not been detached, and 141 141 can still be used to allocate new memory. 142 + - 143 + name: page-pool-info 144 + subset-of: page-pool 145 + attributes: 146 + - 147 + name: id 148 + - 149 + name: ifindex 150 + - 151 + name: page-pool-stats 152 + doc: | 153 + Page pool statistics, see docs for struct page_pool_stats 154 + for information about individual statistics. 155 + attributes: 156 + - 157 + name: info 158 + doc: Page pool identifying information. 159 + type: nest 160 + nested-attributes: page-pool-info 161 + - 162 + name: alloc-fast 163 + type: uint 164 + value: 8 # reserve some attr ids in case we need more metadata later 165 + - 166 + name: alloc-slow 167 + type: uint 168 + - 169 + name: alloc-slow-high-order 170 + type: uint 171 + - 172 + name: alloc-empty 173 + type: uint 174 + - 175 + name: alloc-refill 176 + type: uint 177 + - 178 + name: alloc-waive 179 + type: uint 180 + - 181 + name: recycle-cached 182 + type: uint 183 + - 184 + name: recycle-cache-full 185 + type: uint 186 + - 187 + name: recycle-ring 188 + type: uint 189 + - 190 + name: recycle-ring-full 191 + type: uint 192 + - 193 + name: recycle-released-refcnt 194 + type: uint 142 195 143 196 operations: 144 197 list: ··· 265 212 notify: page-pool-get 266 213 mcgrp: page-pool 267 214 config-cond: page-pool 215 + - 216 + name: page-pool-stats-get 217 + doc: Get page pool statistics. 218 + attribute-set: page-pool-stats 219 + do: 220 + request: 221 + attributes: 222 + - info 223 + reply: &pp-stats-reply 224 + attributes: 225 + - info 226 + - alloc-fast 227 + - alloc-slow 228 + - alloc-slow-high-order 229 + - alloc-empty 230 + - alloc-refill 231 + - alloc-waive 232 + - recycle-cached 233 + - recycle-cache-full 234 + - recycle-ring 235 + - recycle-ring-full 236 + - recycle-released-refcnt 237 + dump: 238 + reply: *pp-stats-reply 239 + config-cond: page-pool-stats 268 240 269 241 mcast-groups: 270 242 list:
+8 -2
Documentation/networking/page_pool.rst
··· 41 41 | Fast cache | | ptr-ring cache | 42 42 +-----------------+ +------------------+ 43 43 44 + Monitoring 45 + ========== 46 + Information about page pools on the system can be accessed via the netdev 47 + genetlink family (see Documentation/netlink/specs/netdev.yaml). 48 + 44 49 API interface 45 50 ============= 46 51 The number of pools created **must** match the number of hardware queues ··· 112 107 It takes a pointer to a ``struct page_pool`` and a pointer to a struct 113 108 page_pool_stats allocated by the caller. 114 109 115 - The API will fill in the provided struct page_pool_stats with 116 - statistics about the page_pool. 110 + Older drivers expose page pool statistics via ethtool or debugfs. 111 + The same statistics are accessible via the netlink netdev family 112 + in a driver-independent fashion. 117 113 118 114 .. kernel-doc:: include/net/page_pool/types.h 119 115 :identifiers: struct page_pool_recycle_stats
+2 -6
include/net/page_pool/helpers.h
··· 55 55 #include <net/page_pool/types.h> 56 56 57 57 #ifdef CONFIG_PAGE_POOL_STATS 58 + /* Deprecated driver-facing API, use netlink instead */ 58 59 int page_pool_ethtool_stats_get_count(void); 59 60 u8 *page_pool_ethtool_stats_get_strings(u8 *data); 60 61 u64 *page_pool_ethtool_stats_get(u64 *data, void *stats); 61 62 62 - /* 63 - * Drivers that wish to harvest page pool stats and report them to users 64 - * (perhaps via ethtool, debugfs, or another mechanism) can allocate a 65 - * struct page_pool_stats call page_pool_get_stats to get stats for the specified pool. 66 - */ 67 - bool page_pool_get_stats(struct page_pool *pool, 63 + bool page_pool_get_stats(const struct page_pool *pool, 68 64 struct page_pool_stats *stats); 69 65 #else 70 66 static inline int page_pool_ethtool_stats_get_count(void)
+19
include/uapi/linux/netdev.h
··· 77 77 }; 78 78 79 79 enum { 80 + NETDEV_A_PAGE_POOL_STATS_INFO = 1, 81 + NETDEV_A_PAGE_POOL_STATS_ALLOC_FAST = 8, 82 + NETDEV_A_PAGE_POOL_STATS_ALLOC_SLOW, 83 + NETDEV_A_PAGE_POOL_STATS_ALLOC_SLOW_HIGH_ORDER, 84 + NETDEV_A_PAGE_POOL_STATS_ALLOC_EMPTY, 85 + NETDEV_A_PAGE_POOL_STATS_ALLOC_REFILL, 86 + NETDEV_A_PAGE_POOL_STATS_ALLOC_WAIVE, 87 + NETDEV_A_PAGE_POOL_STATS_RECYCLE_CACHED, 88 + NETDEV_A_PAGE_POOL_STATS_RECYCLE_CACHE_FULL, 89 + NETDEV_A_PAGE_POOL_STATS_RECYCLE_RING, 90 + NETDEV_A_PAGE_POOL_STATS_RECYCLE_RING_FULL, 91 + NETDEV_A_PAGE_POOL_STATS_RECYCLE_RELEASED_REFCNT, 92 + 93 + __NETDEV_A_PAGE_POOL_STATS_MAX, 94 + NETDEV_A_PAGE_POOL_STATS_MAX = (__NETDEV_A_PAGE_POOL_STATS_MAX - 1) 95 + }; 96 + 97 + enum { 80 98 NETDEV_CMD_DEV_GET = 1, 81 99 NETDEV_CMD_DEV_ADD_NTF, 82 100 NETDEV_CMD_DEV_DEL_NTF, ··· 103 85 NETDEV_CMD_PAGE_POOL_ADD_NTF, 104 86 NETDEV_CMD_PAGE_POOL_DEL_NTF, 105 87 NETDEV_CMD_PAGE_POOL_CHANGE_NTF, 88 + NETDEV_CMD_PAGE_POOL_STATS_GET, 106 89 107 90 __NETDEV_CMD_MAX, 108 91 NETDEV_CMD_MAX = (__NETDEV_CMD_MAX - 1)
+32
net/core/netdev-genl-gen.c
··· 16 16 .max = 4294967295ULL, 17 17 }; 18 18 19 + static const struct netlink_range_validation netdev_a_page_pool_ifindex_range = { 20 + .min = 1ULL, 21 + .max = 2147483647ULL, 22 + }; 23 + 24 + /* Common nested types */ 25 + const struct nla_policy netdev_page_pool_info_nl_policy[NETDEV_A_PAGE_POOL_IFINDEX + 1] = { 26 + [NETDEV_A_PAGE_POOL_ID] = NLA_POLICY_FULL_RANGE(NLA_UINT, &netdev_a_page_pool_id_range), 27 + [NETDEV_A_PAGE_POOL_IFINDEX] = NLA_POLICY_FULL_RANGE(NLA_U32, &netdev_a_page_pool_ifindex_range), 28 + }; 29 + 19 30 /* NETDEV_CMD_DEV_GET - do */ 20 31 static const struct nla_policy netdev_dev_get_nl_policy[NETDEV_A_DEV_IFINDEX + 1] = { 21 32 [NETDEV_A_DEV_IFINDEX] = NLA_POLICY_MIN(NLA_U32, 1), ··· 38 27 [NETDEV_A_PAGE_POOL_ID] = NLA_POLICY_FULL_RANGE(NLA_UINT, &netdev_a_page_pool_id_range), 39 28 }; 40 29 #endif /* CONFIG_PAGE_POOL */ 30 + 31 + /* NETDEV_CMD_PAGE_POOL_STATS_GET - do */ 32 + #ifdef CONFIG_PAGE_POOL_STATS 33 + static const struct nla_policy netdev_page_pool_stats_get_nl_policy[NETDEV_A_PAGE_POOL_STATS_INFO + 1] = { 34 + [NETDEV_A_PAGE_POOL_STATS_INFO] = NLA_POLICY_NESTED(netdev_page_pool_info_nl_policy), 35 + }; 36 + #endif /* CONFIG_PAGE_POOL_STATS */ 41 37 42 38 /* Ops table for netdev */ 43 39 static const struct genl_split_ops netdev_nl_ops[] = { ··· 74 56 .flags = GENL_CMD_CAP_DUMP, 75 57 }, 76 58 #endif /* CONFIG_PAGE_POOL */ 59 + #ifdef CONFIG_PAGE_POOL_STATS 60 + { 61 + .cmd = NETDEV_CMD_PAGE_POOL_STATS_GET, 62 + .doit = netdev_nl_page_pool_stats_get_doit, 63 + .policy = netdev_page_pool_stats_get_nl_policy, 64 + .maxattr = NETDEV_A_PAGE_POOL_STATS_INFO, 65 + .flags = GENL_CMD_CAP_DO, 66 + }, 67 + { 68 + .cmd = NETDEV_CMD_PAGE_POOL_STATS_GET, 69 + .dumpit = netdev_nl_page_pool_stats_get_dumpit, 70 + .flags = GENL_CMD_CAP_DUMP, 71 + }, 72 + #endif /* CONFIG_PAGE_POOL_STATS */ 77 73 }; 78 74 79 75 static const struct genl_multicast_group netdev_nl_mcgrps[] = {
+7
net/core/netdev-genl-gen.h
··· 11 11 12 12 #include <uapi/linux/netdev.h> 13 13 14 + /* Common nested types */ 15 + extern const struct nla_policy netdev_page_pool_info_nl_policy[NETDEV_A_PAGE_POOL_IFINDEX + 1]; 16 + 14 17 int netdev_nl_dev_get_doit(struct sk_buff *skb, struct genl_info *info); 15 18 int netdev_nl_dev_get_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 16 19 int netdev_nl_page_pool_get_doit(struct sk_buff *skb, struct genl_info *info); 17 20 int netdev_nl_page_pool_get_dumpit(struct sk_buff *skb, 18 21 struct netlink_callback *cb); 22 + int netdev_nl_page_pool_stats_get_doit(struct sk_buff *skb, 23 + struct genl_info *info); 24 + int netdev_nl_page_pool_stats_get_dumpit(struct sk_buff *skb, 25 + struct netlink_callback *cb); 19 26 20 27 enum { 21 28 NETDEV_NLGRP_MGMT,
+1 -1
net/core/page_pool.c
··· 71 71 * is passed to this API which is filled in. The caller can then report 72 72 * those stats to the user (perhaps via ethtool, debugfs, etc.). 73 73 */ 74 - bool page_pool_get_stats(struct page_pool *pool, 74 + bool page_pool_get_stats(const struct page_pool *pool, 75 75 struct page_pool_stats *stats) 76 76 { 77 77 int cpu = 0;
+103
net/core/page_pool_user.c
··· 5 5 #include <linux/xarray.h> 6 6 #include <net/net_debug.h> 7 7 #include <net/page_pool/types.h> 8 + #include <net/page_pool/helpers.h> 8 9 #include <net/sock.h> 9 10 10 11 #include "page_pool_priv.h" ··· 105 104 if (skb->len && err == -EMSGSIZE) 106 105 return skb->len; 107 106 return err; 107 + } 108 + 109 + static int 110 + page_pool_nl_stats_fill(struct sk_buff *rsp, const struct page_pool *pool, 111 + const struct genl_info *info) 112 + { 113 + #ifdef CONFIG_PAGE_POOL_STATS 114 + struct page_pool_stats stats = {}; 115 + struct nlattr *nest; 116 + void *hdr; 117 + 118 + if (!page_pool_get_stats(pool, &stats)) 119 + return 0; 120 + 121 + hdr = genlmsg_iput(rsp, info); 122 + if (!hdr) 123 + return -EMSGSIZE; 124 + 125 + nest = nla_nest_start(rsp, NETDEV_A_PAGE_POOL_STATS_INFO); 126 + 127 + if (nla_put_uint(rsp, NETDEV_A_PAGE_POOL_ID, pool->user.id) || 128 + (pool->slow.netdev->ifindex != LOOPBACK_IFINDEX && 129 + nla_put_u32(rsp, NETDEV_A_PAGE_POOL_IFINDEX, 130 + pool->slow.netdev->ifindex))) 131 + goto err_cancel_nest; 132 + 133 + nla_nest_end(rsp, nest); 134 + 135 + if (nla_put_uint(rsp, NETDEV_A_PAGE_POOL_STATS_ALLOC_FAST, 136 + stats.alloc_stats.fast) || 137 + nla_put_uint(rsp, NETDEV_A_PAGE_POOL_STATS_ALLOC_SLOW, 138 + stats.alloc_stats.slow) || 139 + nla_put_uint(rsp, NETDEV_A_PAGE_POOL_STATS_ALLOC_SLOW_HIGH_ORDER, 140 + stats.alloc_stats.slow_high_order) || 141 + nla_put_uint(rsp, NETDEV_A_PAGE_POOL_STATS_ALLOC_EMPTY, 142 + stats.alloc_stats.empty) || 143 + nla_put_uint(rsp, NETDEV_A_PAGE_POOL_STATS_ALLOC_REFILL, 144 + stats.alloc_stats.refill) || 145 + nla_put_uint(rsp, NETDEV_A_PAGE_POOL_STATS_ALLOC_WAIVE, 146 + stats.alloc_stats.waive) || 147 + nla_put_uint(rsp, NETDEV_A_PAGE_POOL_STATS_RECYCLE_CACHED, 148 + stats.recycle_stats.cached) || 149 + nla_put_uint(rsp, NETDEV_A_PAGE_POOL_STATS_RECYCLE_CACHE_FULL, 150 + stats.recycle_stats.cache_full) || 151 + nla_put_uint(rsp, NETDEV_A_PAGE_POOL_STATS_RECYCLE_RING, 152 + stats.recycle_stats.ring) || 153 + nla_put_uint(rsp, NETDEV_A_PAGE_POOL_STATS_RECYCLE_RING_FULL, 154 + stats.recycle_stats.ring_full) || 155 + nla_put_uint(rsp, NETDEV_A_PAGE_POOL_STATS_RECYCLE_RELEASED_REFCNT, 156 + stats.recycle_stats.released_refcnt)) 157 + goto err_cancel_msg; 158 + 159 + genlmsg_end(rsp, hdr); 160 + 161 + return 0; 162 + err_cancel_nest: 163 + nla_nest_cancel(rsp, nest); 164 + err_cancel_msg: 165 + genlmsg_cancel(rsp, hdr); 166 + return -EMSGSIZE; 167 + #else 168 + GENL_SET_ERR_MSG(info, "kernel built without CONFIG_PAGE_POOL_STATS"); 169 + return -EOPNOTSUPP; 170 + #endif 171 + } 172 + 173 + int netdev_nl_page_pool_stats_get_doit(struct sk_buff *skb, 174 + struct genl_info *info) 175 + { 176 + struct nlattr *tb[ARRAY_SIZE(netdev_page_pool_info_nl_policy)]; 177 + struct nlattr *nest; 178 + int err; 179 + u32 id; 180 + 181 + if (GENL_REQ_ATTR_CHECK(info, NETDEV_A_PAGE_POOL_STATS_INFO)) 182 + return -EINVAL; 183 + 184 + nest = info->attrs[NETDEV_A_PAGE_POOL_STATS_INFO]; 185 + err = nla_parse_nested(tb, ARRAY_SIZE(tb) - 1, nest, 186 + netdev_page_pool_info_nl_policy, 187 + info->extack); 188 + if (err) 189 + return err; 190 + 191 + if (NL_REQ_ATTR_CHECK(info->extack, nest, tb, NETDEV_A_PAGE_POOL_ID)) 192 + return -EINVAL; 193 + if (tb[NETDEV_A_PAGE_POOL_IFINDEX]) { 194 + NL_SET_ERR_MSG_ATTR(info->extack, 195 + tb[NETDEV_A_PAGE_POOL_IFINDEX], 196 + "selecting by ifindex not supported"); 197 + return -EINVAL; 198 + } 199 + 200 + id = nla_get_uint(tb[NETDEV_A_PAGE_POOL_ID]); 201 + 202 + return netdev_nl_page_pool_get_do(info, id, page_pool_nl_stats_fill); 203 + } 204 + 205 + int netdev_nl_page_pool_stats_get_dumpit(struct sk_buff *skb, 206 + struct netlink_callback *cb) 207 + { 208 + return netdev_nl_page_pool_get_dump(skb, cb, page_pool_nl_stats_fill); 108 209 } 109 210 110 211 static int