1 The gen_stats facility will add a header for the toplevel nlattr of type
2 TCA_STATS2 that contains all stats added by qdisc callbacks. A reference
3 to this header is stored in the gnet_dump struct, and when all the
4 per-qdisc callbacks have finished adding their stats, the length of the
5 containing header will be adjusted to the right value.
7 However, on architectures that need padding (i.e., that don't set
8 CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS), the padding nlattr is added
9 before the stats, which means that the stored pointer will point to the
10 padding, and so when the header is fixed up, the result is just a very
11 big padding nlattr. Because most qdiscs also supply the legacy TCA_STATS
12 struct, this problem has been mostly invisible, but we exposed it with
13 the netlink attribute-based statistics in CAKE.
15 Fix the issue by fixing up the stored pointer if it points to a padding
18 Tested-by: Pete Heist <pete@heistp.net>
19 Tested-by: Kevin Darbyshire-Bryant <kevin@darbyshire-bryant.me.uk>
20 Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
22 net/core/gen_stats.c | 16 ++++++++++++++--
23 1 file changed, 14 insertions(+), 2 deletions(-)
25 --- a/net/core/gen_stats.c
26 +++ b/net/core/gen_stats.c
27 @@ -77,8 +77,20 @@ gnet_stats_start_copy_compat(struct sk_b
32 - return gnet_stats_copy(d, type, NULL, 0, padattr);
34 + int ret = gnet_stats_copy(d, type, NULL, 0, padattr);
36 + /* The initial attribute added in gnet_stats_copy() may be
37 + * preceded by a padding attribute, in which case d->tail will
38 + * end up pointing at the padding instead of the real attribute.
39 + * Fix this so gnet_stats_finish_copy() adjusts the length of
40 + * the right attribute.
42 + if (ret == 0 && d->tail->nla_type == padattr)
43 + d->tail = (struct nlattr *)((char *)d->tail +
44 + NLA_ALIGN(d->tail->nla_len));