Skip to content

Commit 885531e

Browse files
committed
net: nexthop: Expose nexthop group HW stats to user space
JIRA: https://issues.redhat.com/browse/RHEL-59118 commit 5072ae0 Author: Ido Schimmel <idosch@nvidia.com> Date: Wed Mar 6 13:49:21 2024 +0100 net: nexthop: Expose nexthop group HW stats to user space Add netlink support for reading NH group hardware stats. Stats collection is done through a new notifier, NEXTHOP_EVENT_HW_STATS_REPORT_DELTA. Drivers that implement HW counters for a given NH group are thereby asked to collect the stats and report back to core by calling nh_grp_hw_stats_report_delta(). This is similar to what netdevice L3 stats do. Besides exposing number of packets that passed in the HW datapath, also include information on whether any driver actually realizes the counters. The core can tell based on whether it got any _report_delta() reports from the drivers. This allows enabling the statistics at the group at any time, with drivers opting into supporting them. This is also in line with what netdevice L3 stats are doing. So as not to waste time and space, tie the collection and reporting of HW stats with a new op flag, NHA_OP_FLAG_DUMP_HW_STATS. Co-developed-by: Petr Machata <petrm@nvidia.com> Signed-off-by: Petr Machata <petrm@nvidia.com> Signed-off-by: Ido Schimmel <idosch@nvidia.com> Reviewed-by: Kees Cook <keescook@chromium.org> # For the __counted_by bits Reviewed-by: David Ahern <dsahern@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Ivan Vecera <ivecera@redhat.com>
1 parent 43d32cc commit 885531e

File tree

3 files changed

+149
-8
lines changed

3 files changed

+149
-8
lines changed

include/net/nexthop.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ struct nh_grp_entry {
124124

125125
struct list_head nh_list;
126126
struct nexthop *nh_parent; /* nexthop of group with this entry */
127+
u64 packets_hw;
127128
};
128129

129130
struct nh_group {
@@ -171,13 +172,15 @@ enum nexthop_event_type {
171172
NEXTHOP_EVENT_REPLACE,
172173
NEXTHOP_EVENT_RES_TABLE_PRE_REPLACE,
173174
NEXTHOP_EVENT_BUCKET_REPLACE,
175+
NEXTHOP_EVENT_HW_STATS_REPORT_DELTA,
174176
};
175177

176178
enum nh_notifier_info_type {
177179
NH_NOTIFIER_INFO_TYPE_SINGLE,
178180
NH_NOTIFIER_INFO_TYPE_GRP,
179181
NH_NOTIFIER_INFO_TYPE_RES_TABLE,
180182
NH_NOTIFIER_INFO_TYPE_RES_BUCKET,
183+
NH_NOTIFIER_INFO_TYPE_GRP_HW_STATS,
181184
};
182185

183186
struct nh_notifier_single_info {
@@ -219,6 +222,17 @@ struct nh_notifier_res_table_info {
219222
struct nh_notifier_single_info nhs[] __counted_by(num_nh_buckets);
220223
};
221224

225+
struct nh_notifier_grp_hw_stats_entry_info {
226+
u32 id;
227+
u64 packets;
228+
};
229+
230+
struct nh_notifier_grp_hw_stats_info {
231+
u16 num_nh;
232+
bool hw_stats_used;
233+
struct nh_notifier_grp_hw_stats_entry_info stats[] __counted_by(num_nh);
234+
};
235+
222236
struct nh_notifier_info {
223237
struct net *net;
224238
struct netlink_ext_ack *extack;
@@ -229,6 +243,7 @@ struct nh_notifier_info {
229243
struct nh_notifier_grp_info *nh_grp;
230244
struct nh_notifier_res_table_info *nh_res_table;
231245
struct nh_notifier_res_bucket_info *nh_res_bucket;
246+
struct nh_notifier_grp_hw_stats_info *nh_grp_hw_stats;
232247
};
233248
};
234249

@@ -241,6 +256,9 @@ void nexthop_bucket_set_hw_flags(struct net *net, u32 id, u16 bucket_index,
241256
bool offload, bool trap);
242257
void nexthop_res_grp_activity_update(struct net *net, u32 id, u16 num_buckets,
243258
unsigned long *activity);
259+
void nh_grp_hw_stats_report_delta(struct nh_notifier_grp_hw_stats_info *info,
260+
unsigned int nh_idx,
261+
u64 delta_packets);
244262

245263
/* caller is holding rcu or rtnl; no reference taken to nexthop */
246264
struct nexthop *nexthop_find_by_id(struct net *net, u32 id);

include/uapi/linux/nexthop.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ enum {
3131
#define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1)
3232

3333
#define NHA_OP_FLAG_DUMP_STATS BIT(0)
34+
#define NHA_OP_FLAG_DUMP_HW_STATS BIT(1)
3435

3536
enum {
3637
NHA_UNSPEC,
@@ -71,6 +72,9 @@ enum {
7172
/* u32; nexthop hardware stats enable */
7273
NHA_HW_STATS_ENABLE,
7374

75+
/* u32; read-only; whether any driver collects HW stats */
76+
NHA_HW_STATS_USED,
77+
7478
__NHA_MAX,
7579
};
7680

@@ -132,6 +136,11 @@ enum {
132136
/* uint; number of packets forwarded via the nexthop group entry */
133137
NHA_GROUP_STATS_ENTRY_PACKETS,
134138

139+
/* uint; number of packets forwarded via the nexthop group entry in
140+
* hardware
141+
*/
142+
NHA_GROUP_STATS_ENTRY_PACKETS_HW,
143+
135144
__NHA_GROUP_STATS_ENTRY_MAX,
136145
};
137146

net/ipv4/nexthop.c

Lines changed: 122 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ static void remove_nexthop(struct net *net, struct nexthop *nh,
2626
#define NH_DEV_HASHBITS 8
2727
#define NH_DEV_HASHSIZE (1U << NH_DEV_HASHBITS)
2828

29-
#define NHA_OP_FLAGS_DUMP_ALL (NHA_OP_FLAG_DUMP_STATS)
29+
#define NHA_OP_FLAGS_DUMP_ALL (NHA_OP_FLAG_DUMP_STATS | \
30+
NHA_OP_FLAG_DUMP_HW_STATS)
3031

3132
static const struct nla_policy rtm_nh_policy_new[] = {
3233
[NHA_ID] = { .type = NLA_U32 },
@@ -700,8 +701,95 @@ static void nh_grp_entry_stats_read(struct nh_grp_entry *nhge,
700701
}
701702
}
702703

704+
static int nh_notifier_grp_hw_stats_init(struct nh_notifier_info *info,
705+
const struct nexthop *nh)
706+
{
707+
struct nh_group *nhg;
708+
int i;
709+
710+
ASSERT_RTNL();
711+
nhg = rtnl_dereference(nh->nh_grp);
712+
713+
info->id = nh->id;
714+
info->type = NH_NOTIFIER_INFO_TYPE_GRP_HW_STATS;
715+
info->nh_grp_hw_stats = kzalloc(struct_size(info->nh_grp_hw_stats,
716+
stats, nhg->num_nh),
717+
GFP_KERNEL);
718+
if (!info->nh_grp_hw_stats)
719+
return -ENOMEM;
720+
721+
info->nh_grp_hw_stats->num_nh = nhg->num_nh;
722+
for (i = 0; i < nhg->num_nh; i++) {
723+
struct nh_grp_entry *nhge = &nhg->nh_entries[i];
724+
725+
info->nh_grp_hw_stats->stats[i].id = nhge->nh->id;
726+
}
727+
728+
return 0;
729+
}
730+
731+
static void nh_notifier_grp_hw_stats_fini(struct nh_notifier_info *info)
732+
{
733+
kfree(info->nh_grp_hw_stats);
734+
}
735+
736+
void nh_grp_hw_stats_report_delta(struct nh_notifier_grp_hw_stats_info *info,
737+
unsigned int nh_idx,
738+
u64 delta_packets)
739+
{
740+
info->hw_stats_used = true;
741+
info->stats[nh_idx].packets += delta_packets;
742+
}
743+
EXPORT_SYMBOL(nh_grp_hw_stats_report_delta);
744+
745+
static void nh_grp_hw_stats_apply_update(struct nexthop *nh,
746+
struct nh_notifier_info *info)
747+
{
748+
struct nh_group *nhg;
749+
int i;
750+
751+
ASSERT_RTNL();
752+
nhg = rtnl_dereference(nh->nh_grp);
753+
754+
for (i = 0; i < nhg->num_nh; i++) {
755+
struct nh_grp_entry *nhge = &nhg->nh_entries[i];
756+
757+
nhge->packets_hw += info->nh_grp_hw_stats->stats[i].packets;
758+
}
759+
}
760+
761+
static int nh_grp_hw_stats_update(struct nexthop *nh, bool *hw_stats_used)
762+
{
763+
struct nh_notifier_info info = {
764+
.net = nh->net,
765+
};
766+
struct net *net = nh->net;
767+
int err;
768+
769+
if (nexthop_notifiers_is_empty(net))
770+
return 0;
771+
772+
err = nh_notifier_grp_hw_stats_init(&info, nh);
773+
if (err)
774+
return err;
775+
776+
err = blocking_notifier_call_chain(&net->nexthop.notifier_chain,
777+
NEXTHOP_EVENT_HW_STATS_REPORT_DELTA,
778+
&info);
779+
780+
/* Cache whatever we got, even if there was an error, otherwise the
781+
* successful stats retrievals would get lost.
782+
*/
783+
nh_grp_hw_stats_apply_update(nh, &info);
784+
*hw_stats_used = info.nh_grp_hw_stats->hw_stats_used;
785+
786+
nh_notifier_grp_hw_stats_fini(&info);
787+
return notifier_to_errno(err);
788+
}
789+
703790
static int nla_put_nh_group_stats_entry(struct sk_buff *skb,
704-
struct nh_grp_entry *nhge)
791+
struct nh_grp_entry *nhge,
792+
u32 op_flags)
705793
{
706794
struct nlattr *nest;
707795
u64 packets;
@@ -713,7 +801,13 @@ static int nla_put_nh_group_stats_entry(struct sk_buff *skb,
713801
return -EMSGSIZE;
714802

715803
if (nla_put_u32(skb, NHA_GROUP_STATS_ENTRY_ID, nhge->nh->id) ||
716-
nla_put_uint(skb, NHA_GROUP_STATS_ENTRY_PACKETS, packets))
804+
nla_put_uint(skb, NHA_GROUP_STATS_ENTRY_PACKETS,
805+
packets + nhge->packets_hw))
806+
goto nla_put_failure;
807+
808+
if (op_flags & NHA_OP_FLAG_DUMP_HW_STATS &&
809+
nla_put_uint(skb, NHA_GROUP_STATS_ENTRY_PACKETS_HW,
810+
nhge->packets_hw))
717811
goto nla_put_failure;
718812

719813
nla_nest_end(skb, nest);
@@ -724,26 +818,46 @@ static int nla_put_nh_group_stats_entry(struct sk_buff *skb,
724818
return -EMSGSIZE;
725819
}
726820

727-
static int nla_put_nh_group_stats(struct sk_buff *skb, struct nexthop *nh)
821+
static int nla_put_nh_group_stats(struct sk_buff *skb, struct nexthop *nh,
822+
u32 op_flags)
728823
{
729824
struct nh_group *nhg = rtnl_dereference(nh->nh_grp);
730825
struct nlattr *nest;
826+
bool hw_stats_used;
827+
int err;
731828
int i;
732829

830+
if (nla_put_u32(skb, NHA_HW_STATS_ENABLE, nhg->hw_stats))
831+
goto err_out;
832+
833+
if (op_flags & NHA_OP_FLAG_DUMP_HW_STATS &&
834+
nhg->hw_stats) {
835+
err = nh_grp_hw_stats_update(nh, &hw_stats_used);
836+
if (err)
837+
goto out;
838+
839+
if (nla_put_u32(skb, NHA_HW_STATS_USED, hw_stats_used))
840+
goto err_out;
841+
}
842+
733843
nest = nla_nest_start(skb, NHA_GROUP_STATS);
734844
if (!nest)
735-
return -EMSGSIZE;
845+
goto err_out;
736846

737847
for (i = 0; i < nhg->num_nh; i++)
738-
if (nla_put_nh_group_stats_entry(skb, &nhg->nh_entries[i]))
848+
if (nla_put_nh_group_stats_entry(skb, &nhg->nh_entries[i],
849+
op_flags))
739850
goto cancel_out;
740851

741852
nla_nest_end(skb, nest);
742853
return 0;
743854

744855
cancel_out:
745856
nla_nest_cancel(skb, nest);
746-
return -EMSGSIZE;
857+
err_out:
858+
err = -EMSGSIZE;
859+
out:
860+
return err;
747861
}
748862

749863
static int nla_put_nh_group(struct sk_buff *skb, struct nexthop *nh,
@@ -780,7 +894,7 @@ static int nla_put_nh_group(struct sk_buff *skb, struct nexthop *nh,
780894

781895
if (op_flags & NHA_OP_FLAG_DUMP_STATS &&
782896
(nla_put_u32(skb, NHA_HW_STATS_ENABLE, nhg->hw_stats) ||
783-
nla_put_nh_group_stats(skb, nh)))
897+
nla_put_nh_group_stats(skb, nh, op_flags)))
784898
goto nla_put_failure;
785899

786900
return 0;

0 commit comments

Comments
 (0)