kernel: gen_stats: Fix netlink stats dumping in the presence of padding
[openwrt/openwrt.git] / target / linux / generic / pending-4.9 / 900-gen_stats-fix-netlink-stats-padding.patch
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.
6
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.
14
15 Fix the issue by fixing up the stored pointer if it points to a padding
16 nlattr.
17
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>
21 ---
22 net/core/gen_stats.c | 16 ++++++++++++++--
23 1 file changed, 14 insertions(+), 2 deletions(-)
24
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
28 d->lock = lock;
29 spin_lock_bh(lock);
30 }
31 - if (d->tail)
32 - return gnet_stats_copy(d, type, NULL, 0, padattr);
33 + if (d->tail) {
34 + int ret = gnet_stats_copy(d, type, NULL, 0, padattr);
35 +
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.
41 + */
42 + if (ret == 0 && d->tail->nla_type == padattr)
43 + d->tail = (struct nlattr *)((char *)d->tail +
44 + NLA_ALIGN(d->tail->nla_len));
45 + return ret;
46 + }
47
48 return 0;
49 }