Commit 26ff98d7 authored by Baowen Zheng's avatar Baowen Zheng Committed by Jakub Kicinski
Browse files

nfp: add hash table to store meter table



Add a hash table to store meter table.

This meter table will also be used by flower action.

Signed-off-by: default avatarBaowen Zheng <baowen.zheng@corigine.com>
Signed-off-by: default avatarLouis Peens <louis.peens@corigine.com>
Signed-off-by: default avatarSimon Horman <simon.horman@corigine.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 59080da0
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
@@ -194,6 +194,8 @@ struct nfp_fl_internal_ports {
 * @qos_stats_work:	Workqueue for qos stats processing
 * @qos_rate_limiters:	Current active qos rate limiters
 * @qos_stats_lock:	Lock on qos stats updates
 * @meter_stats_lock:   Lock on meter stats updates
 * @meter_table:	Hash table used to store the meter table
 * @pre_tun_rule_cnt:	Number of pre-tunnel rules offloaded
 * @merge_table:	Hash table to store merged flows
 * @ct_zone_table:	Hash table used to store the different zones
@@ -231,6 +233,8 @@ struct nfp_flower_priv {
	struct delayed_work qos_stats_work;
	unsigned int qos_rate_limiters;
	spinlock_t qos_stats_lock; /* Protect the qos stats */
	struct mutex meter_stats_lock; /* Protect the meter stats */
	struct rhashtable meter_table;
	int pre_tun_rule_cnt;
	struct rhashtable merge_table;
	struct rhashtable ct_zone_table;
@@ -377,6 +381,31 @@ struct nfp_fl_stats_frame {
	__be64 stats_cookie;
};

struct nfp_meter_stats_entry {
	u64 pkts;
	u64 bytes;
	u64 drops;
};

struct nfp_meter_entry {
	struct rhash_head ht_node;
	u32 meter_id;
	bool bps;
	u32 rate;
	u32 burst;
	u64 used;
	struct nfp_meter_stats {
		u64 update;
		struct nfp_meter_stats_entry curr;
		struct nfp_meter_stats_entry prev;
	} stats;
};

enum nfp_meter_op {
	NFP_METER_ADD,
	NFP_METER_DEL,
};

static inline bool
nfp_flower_internal_port_can_offload(struct nfp_app *app,
				     struct net_device *netdev)
@@ -575,6 +604,12 @@ nfp_flower_update_merge_stats(struct nfp_app *app,

int nfp_setup_tc_act_offload(struct nfp_app *app,
			     struct flow_offload_action *fl_act);
int nfp_init_meter_table(struct nfp_app *app);

int nfp_flower_offload_one_police(struct nfp_app *app, bool ingress,
				  bool pps, u32 id, u32 rate, u32 burst);
int nfp_flower_setup_meter_entry(struct nfp_app *app,
				 const struct flow_action_entry *action,
				 enum nfp_meter_op op,
				 u32 meter_id);
#endif
+141 −1
Original line number Diff line number Diff line
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2019 Netronome Systems, Inc. */

#include <linux/hash.h>
#include <linux/hashtable.h>
#include <linux/jhash.h>
#include <linux/math64.h>
#include <linux/vmalloc.h>
#include <net/pkt_cls.h>
#include <net/pkt_sched.h>

@@ -440,6 +444,9 @@ void nfp_flower_qos_init(struct nfp_app *app)
	struct nfp_flower_priv *fl_priv = app->priv;

	spin_lock_init(&fl_priv->qos_stats_lock);
	mutex_init(&fl_priv->meter_stats_lock);
	nfp_init_meter_table(app);

	INIT_DELAYED_WORK(&fl_priv->qos_stats_work, &update_stats_cache);
}

@@ -478,6 +485,122 @@ int nfp_flower_setup_qos_offload(struct nfp_app *app, struct net_device *netdev,

/* offload tc action, currently only for tc police */

static const struct rhashtable_params stats_meter_table_params = {
	.key_offset	= offsetof(struct nfp_meter_entry, meter_id),
	.head_offset	= offsetof(struct nfp_meter_entry, ht_node),
	.key_len	= sizeof(u32),
};

static struct nfp_meter_entry *
nfp_flower_search_meter_entry(struct nfp_app *app, u32 meter_id)
{
	struct nfp_flower_priv *priv = app->priv;

	return rhashtable_lookup_fast(&priv->meter_table, &meter_id,
				      stats_meter_table_params);
}

static struct nfp_meter_entry *
nfp_flower_add_meter_entry(struct nfp_app *app, u32 meter_id)
{
	struct nfp_meter_entry *meter_entry = NULL;
	struct nfp_flower_priv *priv = app->priv;

	meter_entry = rhashtable_lookup_fast(&priv->meter_table,
					     &meter_id,
					     stats_meter_table_params);
	if (meter_entry)
		return meter_entry;

	meter_entry = kzalloc(sizeof(*meter_entry), GFP_KERNEL);
	if (!meter_entry)
		return NULL;

	meter_entry->meter_id = meter_id;
	meter_entry->used = jiffies;
	if (rhashtable_insert_fast(&priv->meter_table, &meter_entry->ht_node,
				   stats_meter_table_params)) {
		kfree(meter_entry);
		return NULL;
	}

	priv->qos_rate_limiters++;
	if (priv->qos_rate_limiters == 1)
		schedule_delayed_work(&priv->qos_stats_work,
				      NFP_FL_QOS_UPDATE);

	return meter_entry;
}

static void nfp_flower_del_meter_entry(struct nfp_app *app, u32 meter_id)
{
	struct nfp_meter_entry *meter_entry = NULL;
	struct nfp_flower_priv *priv = app->priv;

	meter_entry = rhashtable_lookup_fast(&priv->meter_table, &meter_id,
					     stats_meter_table_params);
	if (!meter_entry)
		return;

	rhashtable_remove_fast(&priv->meter_table,
			       &meter_entry->ht_node,
			       stats_meter_table_params);
	kfree(meter_entry);
	priv->qos_rate_limiters--;
	if (!priv->qos_rate_limiters)
		cancel_delayed_work_sync(&priv->qos_stats_work);
}

int nfp_flower_setup_meter_entry(struct nfp_app *app,
				 const struct flow_action_entry *action,
				 enum nfp_meter_op op,
				 u32 meter_id)
{
	struct nfp_flower_priv *fl_priv = app->priv;
	struct nfp_meter_entry *meter_entry = NULL;
	int err = 0;

	mutex_lock(&fl_priv->meter_stats_lock);

	switch (op) {
	case NFP_METER_DEL:
		nfp_flower_del_meter_entry(app, meter_id);
		goto exit_unlock;
	case NFP_METER_ADD:
		meter_entry = nfp_flower_add_meter_entry(app, meter_id);
		break;
	default:
		err = -EOPNOTSUPP;
		goto exit_unlock;
	}

	if (!meter_entry) {
		err = -ENOMEM;
		goto exit_unlock;
	}

	if (action->police.rate_bytes_ps > 0) {
		meter_entry->bps = true;
		meter_entry->rate = action->police.rate_bytes_ps;
		meter_entry->burst = action->police.burst;
	} else {
		meter_entry->bps = false;
		meter_entry->rate = action->police.rate_pkt_ps;
		meter_entry->burst = action->police.burst_pkt;
	}

exit_unlock:
	mutex_unlock(&fl_priv->meter_stats_lock);
	return err;
}

int nfp_init_meter_table(struct nfp_app *app)
{
	struct nfp_flower_priv *priv = app->priv;

	return rhashtable_init(&priv->meter_table, &stats_meter_table_params);
}

static int
nfp_act_install_actions(struct nfp_app *app, struct flow_offload_action *fl_act,
			struct netlink_ext_ack *extack)
@@ -514,10 +637,13 @@ nfp_act_install_actions(struct nfp_app *app, struct flow_offload_action *fl_act,
		}

		if (rate != 0) {
			meter_id = action->hw_index;
			if (nfp_flower_setup_meter_entry(app, action, NFP_METER_ADD, meter_id))
				continue;

			pps = false;
			if (action->police.rate_pkt_ps > 0)
				pps = true;
			meter_id = action->hw_index;
			nfp_flower_offload_one_police(app, false, pps, meter_id,
						      rate, burst);
			add = true;
@@ -531,9 +657,11 @@ static int
nfp_act_remove_actions(struct nfp_app *app, struct flow_offload_action *fl_act,
		       struct netlink_ext_ack *extack)
{
	struct nfp_meter_entry *meter_entry = NULL;
	struct nfp_police_config *config;
	struct sk_buff *skb;
	u32 meter_id;
	bool pps;

	/*delete qos associate data for this interface */
	if (fl_act->id != FLOW_ACTION_POLICE) {
@@ -543,6 +671,14 @@ nfp_act_remove_actions(struct nfp_app *app, struct flow_offload_action *fl_act,
	}

	meter_id = fl_act->index;
	meter_entry = nfp_flower_search_meter_entry(app, meter_id);
	if (!meter_entry) {
		NL_SET_ERR_MSG_MOD(extack,
				   "no meter entry when delete the action index.\n");
		return -ENOENT;
	}
	pps = !meter_entry->bps;

	skb = nfp_flower_cmsg_alloc(app, sizeof(struct nfp_police_config),
				    NFP_FLOWER_CMSG_TYPE_QOS_DEL, GFP_KERNEL);
	if (!skb)
@@ -552,7 +688,11 @@ nfp_act_remove_actions(struct nfp_app *app, struct flow_offload_action *fl_act,
	memset(config, 0, sizeof(struct nfp_police_config));
	config->head.flags_opts = cpu_to_be32(NFP_FL_QOS_METER);
	config->head.meter_id = cpu_to_be32(meter_id);
	if (pps)
		config->head.flags_opts |= cpu_to_be32(NFP_FL_QOS_PPS);

	nfp_ctrl_tx(app->ctrl, skb);
	nfp_flower_setup_meter_entry(app, NULL, NFP_METER_DEL, meter_id);

	return 0;
}