@@ -47,7 +47,7 @@ mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en_tc.o en/rep/tc.o en/rep/neigh.o \
en/tc_tun_vxlan.o en/tc_tun_gre.o en/tc_tun_geneve.o \
en/tc_tun_mplsoudp.o diag/en_tc_tracepoint.o \
en/tc/post_act.o en/tc/int_port.o en/tc/meter.o \
- en/tc/post_meter.o
+ en/tc/post_meter.o en/tc/act_stats.o
mlx5_core-$(CONFIG_MLX5_CLS_ACT) += en/tc/act/act.o en/tc/act/drop.o en/tc/act/trap.o \
en/tc/act/accept.o en/tc/act/mark.o en/tc/act/goto.o \
new file mode 100644
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+// Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+#include <linux/rhashtable.h>
+#include <net/flow_offload.h>
+#include "en/tc_priv.h"
+#include "act_stats.h"
+#include "en/fs.h"
+
+struct mlx5e_tc_act_stats_handle {
+ struct rhashtable ht;
+ spinlock_t ht_lock; /* protects hashtable */
+};
+
+struct mlx5e_tc_act_stats {
+ unsigned long tc_act_cookie;
+
+ struct mlx5_fc *counter;
+ u64 lastpackets;
+ u64 lastbytes;
+
+ struct rhash_head hash;
+ struct rcu_head rcu_head;
+};
+
+static const struct rhashtable_params act_counters_ht_params = {
+ .head_offset = offsetof(struct mlx5e_tc_act_stats, hash),
+ .key_offset = 0,
+ .key_len = offsetof(struct mlx5e_tc_act_stats, counter),
+ .automatic_shrinking = true,
+};
+
+struct mlx5e_tc_act_stats_handle *
+mlx5e_tc_act_stats_create(void)
+{
+ struct mlx5e_tc_act_stats_handle *handle;
+ int err;
+
+ handle = kvzalloc(sizeof(*handle), GFP_KERNEL);
+ if (IS_ERR(handle))
+ return ERR_PTR(-ENOMEM);
+
+ err = rhashtable_init(&handle->ht, &act_counters_ht_params);
+ if (err)
+ goto err;
+
+ spin_lock_init(&handle->ht_lock);
+ return handle;
+err:
+ kvfree(handle);
+ return ERR_PTR(err);
+}
+
+void mlx5e_tc_act_stats_free(struct mlx5e_tc_act_stats_handle *handle)
+{
+ rhashtable_destroy(&handle->ht);
+ kvfree(handle);
+}
+
+static int
+mlx5e_tc_act_stats_add(struct mlx5e_tc_act_stats_handle *handle,
+ unsigned long act_cookie,
+ struct mlx5_fc *counter)
+{
+ struct mlx5e_tc_act_stats *act_stats, *old_act_stats;
+ struct rhashtable *ht = &handle->ht;
+ int err = 0;
+
+ act_stats = kvzalloc(sizeof(*act_stats), GFP_KERNEL);
+ if (!act_stats)
+ return -ENOMEM;
+
+ act_stats->tc_act_cookie = act_cookie;
+ act_stats->counter = counter;
+
+ rcu_read_lock();
+ old_act_stats = rhashtable_lookup_get_insert_fast(ht,
+ &act_stats->hash,
+ act_counters_ht_params);
+ if (IS_ERR(old_act_stats)) {
+ err = PTR_ERR(old_act_stats);
+ goto err_hash_insert;
+ } else if (old_act_stats) {
+ err = -EEXIST;
+ goto err_hash_insert;
+ }
+ rcu_read_unlock();
+
+ return 0;
+
+err_hash_insert:
+ rcu_read_unlock();
+ kvfree(act_stats);
+ return err;
+}
+
+void
+mlx5e_tc_act_stats_del_flow(struct mlx5e_tc_act_stats_handle *handle,
+ struct mlx5e_tc_flow *flow)
+{
+ struct mlx5_flow_attr *attr;
+ struct mlx5e_tc_act_stats *act_stats;
+ int i;
+
+ list_for_each_entry(attr, &flow->attrs, list) {
+ for (i = 0; i < attr->tc_act_cookies_count; i++) {
+ struct rhashtable *ht = &handle->ht;
+
+ spin_lock(&handle->ht_lock);
+ act_stats = rhashtable_lookup_fast(ht,
+ &attr->tc_act_cookies[i],
+ act_counters_ht_params);
+ if (act_stats &&
+ rhashtable_remove_fast(ht, &act_stats->hash,
+ act_counters_ht_params) == 0)
+ kvfree_rcu(act_stats, rcu_head);
+
+ spin_unlock(&handle->ht_lock);
+ }
+ }
+}
+
+int
+mlx5e_tc_act_stats_add_flow(struct mlx5e_tc_act_stats_handle *handle,
+ struct mlx5e_tc_flow *flow)
+{
+ struct mlx5_fc *curr_counter = NULL;
+ unsigned long last_cookie = 0;
+ struct mlx5_flow_attr *attr;
+ int err;
+ int i;
+
+ list_for_each_entry(attr, &flow->attrs, list) {
+ if (attr->counter)
+ curr_counter = attr->counter;
+
+ for (i = 0; i < attr->tc_act_cookies_count; i++) {
+ /* jump over identical ids (e.g. pedit)*/
+ if (last_cookie == attr->tc_act_cookies[i])
+ continue;
+
+ err = mlx5e_tc_act_stats_add(handle, attr->tc_act_cookies[i], curr_counter);
+ if (err)
+ goto out_err;
+ last_cookie = attr->tc_act_cookies[i];
+ }
+ }
+
+ return 0;
+out_err:
+ mlx5e_tc_act_stats_del_flow(handle, flow);
+ return err;
+}
new file mode 100644
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
+
+#ifndef __MLX5_EN_ACT_STATS_H__
+#define __MLX5_EN_ACT_STATS_H__
+
+#include <net/flow_offload.h>
+#include "en/tc_priv.h"
+
+struct mlx5e_tc_act_stats_handle;
+
+struct mlx5e_tc_act_stats_handle *mlx5e_tc_act_stats_create(void);
+void mlx5e_tc_act_stats_free(struct mlx5e_tc_act_stats_handle *handle);
+
+int
+mlx5e_tc_act_stats_add_flow(struct mlx5e_tc_act_stats_handle *handle,
+ struct mlx5e_tc_flow *flow);
+
+void
+mlx5e_tc_act_stats_del_flow(struct mlx5e_tc_act_stats_handle *handle,
+ struct mlx5e_tc_flow *flow);
+
+#endif /* __MLX5_EN_ACT_STATS_H__ */
@@ -100,6 +100,9 @@ struct mlx5_rep_uplink_priv {
struct mlx5e_tc_int_port_priv *int_port_priv;
struct mlx5e_flow_meters *flow_meters;
+
+ /* tc action stats */
+ struct mlx5e_tc_act_stats_handle *action_stats_handle;
};
struct mlx5e_rep_priv {
@@ -45,6 +45,7 @@
#include <net/bonding.h>
#include "en.h"
#include "en/tc/post_act.h"
+#include "en/tc/act_stats.h"
#include "en_rep.h"
#include "en/rep/tc.h"
#include "en/rep/neigh.h"
@@ -101,6 +102,9 @@ struct mlx5e_tc_table {
struct mapping_ctx *mapping;
struct mlx5e_hairpin_params hairpin_params;
struct dentry *dfs_root;
+
+ /* tc action stats */
+ struct mlx5e_tc_act_stats_handle *action_stats_handle;
};
struct mlx5e_tc_attr_to_reg_mapping mlx5e_tc_attr_to_reg_mappings[] = {
@@ -286,6 +290,24 @@ struct mlx5_fs_chains *mlx5e_nic_chains(struct mlx5e_tc_table *tc)
return err;
}
+static struct mlx5e_tc_act_stats_handle *
+get_act_stats_handle(struct mlx5e_priv *priv)
+{
+ struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs);
+ struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
+ struct mlx5_rep_uplink_priv *uplink_priv;
+ struct mlx5e_rep_priv *uplink_rpriv;
+
+ if (is_mdev_switchdev_mode(priv->mdev)) {
+ uplink_rpriv = mlx5_eswitch_get_uplink_priv(esw, REP_ETH);
+ uplink_priv = &uplink_rpriv->uplink_priv;
+
+ return uplink_priv->action_stats_handle;
+ }
+
+ return tc->action_stats_handle;
+}
+
struct mlx5e_tc_int_port_priv *
mlx5e_get_int_port_priv(struct mlx5e_priv *priv)
{
@@ -2026,6 +2048,10 @@ int mlx5e_tc_query_route_vport(struct net_device *out_dev, struct net_device *ro
if (err)
goto err_out;
+ err = mlx5e_tc_act_stats_add_flow(get_act_stats_handle(priv), flow);
+ if (err)
+ goto err_out;
+
/* we get here if one of the following takes place:
* (1) there's no error
* (2) there's an encap action and we don't have valid neigh
@@ -2120,6 +2146,8 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv,
if (flow_flag_test(flow, L3_TO_L2_DECAP))
mlx5e_detach_decap(priv, flow);
+ mlx5e_tc_act_stats_del_flow(get_act_stats_handle(priv), flow);
+
free_flow_post_acts(flow);
free_branch_attr(flow, attr->branch_true);
free_branch_attr(flow, attr->branch_false);
@@ -5331,8 +5359,16 @@ int mlx5e_tc_nic_init(struct mlx5e_priv *priv)
mlx5e_tc_debugfs_init(tc, mlx5e_fs_get_debugfs_root(priv->fs));
+ tc->action_stats_handle = mlx5e_tc_act_stats_create();
+ if (IS_ERR(tc->action_stats_handle))
+ goto err_act_stats;
+
return 0;
+err_act_stats:
+ unregister_netdevice_notifier_dev_net(priv->netdev,
+ &tc->netdevice_nb,
+ &tc->netdevice_nn);
err_reg:
mlx5_tc_ct_clean(tc->ct);
mlx5e_tc_post_act_destroy(tc->post_act);
@@ -5382,6 +5418,7 @@ void mlx5e_tc_nic_cleanup(struct mlx5e_priv *priv)
mapping_destroy(tc->mapping);
mlx5_chains_destroy(tc->chains);
mlx5e_tc_nic_destroy_miss_table(priv);
+ mlx5e_tc_act_stats_free(tc->action_stats_handle);
}
int mlx5e_tc_ht_init(struct rhashtable *tc_ht)
@@ -5458,8 +5495,14 @@ int mlx5e_tc_esw_init(struct mlx5_rep_uplink_priv *uplink_priv)
goto err_register_fib_notifier;
}
+ uplink_priv->action_stats_handle = mlx5e_tc_act_stats_create();
+ if (IS_ERR(uplink_priv->action_stats_handle))
+ goto err_action_counter;
+
return 0;
+err_action_counter:
+ mlx5e_tc_tun_cleanup(uplink_priv->encap);
err_register_fib_notifier:
mapping_destroy(uplink_priv->tunnel_enc_opts_mapping);
err_enc_opts_mapping:
@@ -5486,6 +5529,7 @@ void mlx5e_tc_esw_cleanup(struct mlx5_rep_uplink_priv *uplink_priv)
mlx5_tc_ct_clean(uplink_priv->ct_priv);
mlx5e_flow_meters_cleanup(uplink_priv->flow_meters);
mlx5e_tc_post_act_destroy(uplink_priv->post_act);
+ mlx5e_tc_act_stats_free(uplink_priv->action_stats_handle);
}
int mlx5e_tc_num_filters(struct mlx5e_priv *priv, unsigned long flags)