xref: /openbmc/linux/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c (revision e8c127b0576660da9195504fe8393fe9da3de9ce)
1b66d035eSPieter Jansen van Vuuren // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2b66d035eSPieter Jansen van Vuuren /* Copyright (C) 2019 Netronome Systems, Inc. */
3b66d035eSPieter Jansen van Vuuren 
426ff98d7SBaowen Zheng #include <linux/hash.h>
526ff98d7SBaowen Zheng #include <linux/hashtable.h>
626ff98d7SBaowen Zheng #include <linux/jhash.h>
749cbef13SPieter Jansen van Vuuren #include <linux/math64.h>
826ff98d7SBaowen Zheng #include <linux/vmalloc.h>
9b66d035eSPieter Jansen van Vuuren #include <net/pkt_cls.h>
1049cbef13SPieter Jansen van Vuuren #include <net/pkt_sched.h>
11b66d035eSPieter Jansen van Vuuren 
12b66d035eSPieter Jansen van Vuuren #include "cmsg.h"
13b66d035eSPieter Jansen van Vuuren #include "main.h"
1449cbef13SPieter Jansen van Vuuren #include "../nfp_port.h"
1549cbef13SPieter Jansen van Vuuren 
165fb5c395SPieter Jansen van Vuuren #define NFP_FL_QOS_UPDATE		msecs_to_jiffies(1000)
17631a44edSPeng Zhang #define NFP_FL_QOS_PPS  BIT(15)
18bbab5f93SBaowen Zheng #define NFP_FL_QOS_METER  BIT(10)
195fb5c395SPieter Jansen van Vuuren 
2049cbef13SPieter Jansen van Vuuren struct nfp_police_cfg_head {
2149cbef13SPieter Jansen van Vuuren 	__be32 flags_opts;
22bbab5f93SBaowen Zheng 	union {
23bbab5f93SBaowen Zheng 		__be32 meter_id;
2449cbef13SPieter Jansen van Vuuren 		__be32 port;
2549cbef13SPieter Jansen van Vuuren 	};
26bbab5f93SBaowen Zheng };
2749cbef13SPieter Jansen van Vuuren 
28631a44edSPeng Zhang enum NFP_FL_QOS_TYPES {
29631a44edSPeng Zhang 	NFP_FL_QOS_TYPE_BPS,
30631a44edSPeng Zhang 	NFP_FL_QOS_TYPE_PPS,
31631a44edSPeng Zhang 	NFP_FL_QOS_TYPE_MAX,
32631a44edSPeng Zhang };
33631a44edSPeng Zhang 
3449cbef13SPieter Jansen van Vuuren /* Police cmsg for configuring a trTCM traffic conditioner (8W/32B)
3549cbef13SPieter Jansen van Vuuren  * See RFC 2698 for more details.
3649cbef13SPieter Jansen van Vuuren  * ----------------------------------------------------------------
3749cbef13SPieter Jansen van Vuuren  *    3                   2                   1
3849cbef13SPieter Jansen van Vuuren  *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
3949cbef13SPieter Jansen van Vuuren  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
40631a44edSPeng Zhang  * |             Reserved          |p|         Reserved            |
4149cbef13SPieter Jansen van Vuuren  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4249cbef13SPieter Jansen van Vuuren  * |                          Port Ingress                         |
4349cbef13SPieter Jansen van Vuuren  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4449cbef13SPieter Jansen van Vuuren  * |                        Token Bucket Peak                      |
4549cbef13SPieter Jansen van Vuuren  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4649cbef13SPieter Jansen van Vuuren  * |                     Token Bucket Committed                    |
4749cbef13SPieter Jansen van Vuuren  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4849cbef13SPieter Jansen van Vuuren  * |                         Peak Burst Size                       |
4949cbef13SPieter Jansen van Vuuren  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
5049cbef13SPieter Jansen van Vuuren  * |                      Committed Burst Size                     |
5149cbef13SPieter Jansen van Vuuren  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
5249cbef13SPieter Jansen van Vuuren  * |                      Peak Information Rate                    |
5349cbef13SPieter Jansen van Vuuren  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
5449cbef13SPieter Jansen van Vuuren  * |                    Committed Information Rate                 |
5549cbef13SPieter Jansen van Vuuren  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
56631a44edSPeng Zhang  * Word[0](FLag options):
57631a44edSPeng Zhang  * [15] p(pps) 1 for pps, 0 for bps
58631a44edSPeng Zhang  *
59bbab5f93SBaowen Zheng  * Meter control message
60bbab5f93SBaowen Zheng  *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
61bbab5f93SBaowen Zheng  * +-------------------------------+-+---+-----+-+---------+-+---+-+
62bbab5f93SBaowen Zheng  * |            Reserved           |p| Y |TYPE |E|TSHFV    |P| PC|R|
63bbab5f93SBaowen Zheng  * +-------------------------------+-+---+-----+-+---------+-+---+-+
64bbab5f93SBaowen Zheng  * |                            meter ID                           |
65bbab5f93SBaowen Zheng  * +-------------------------------+-------------------------------+
66bbab5f93SBaowen Zheng  *
6749cbef13SPieter Jansen van Vuuren  */
6849cbef13SPieter Jansen van Vuuren struct nfp_police_config {
6949cbef13SPieter Jansen van Vuuren 	struct nfp_police_cfg_head head;
7049cbef13SPieter Jansen van Vuuren 	__be32 bkt_tkn_p;
7149cbef13SPieter Jansen van Vuuren 	__be32 bkt_tkn_c;
7249cbef13SPieter Jansen van Vuuren 	__be32 pbs;
7349cbef13SPieter Jansen van Vuuren 	__be32 cbs;
7449cbef13SPieter Jansen van Vuuren 	__be32 pir;
7549cbef13SPieter Jansen van Vuuren 	__be32 cir;
7649cbef13SPieter Jansen van Vuuren };
7749cbef13SPieter Jansen van Vuuren 
785fb5c395SPieter Jansen van Vuuren struct nfp_police_stats_reply {
795fb5c395SPieter Jansen van Vuuren 	struct nfp_police_cfg_head head;
805fb5c395SPieter Jansen van Vuuren 	__be64 pass_bytes;
815fb5c395SPieter Jansen van Vuuren 	__be64 pass_pkts;
825fb5c395SPieter Jansen van Vuuren 	__be64 drop_bytes;
835fb5c395SPieter Jansen van Vuuren 	__be64 drop_pkts;
845fb5c395SPieter Jansen van Vuuren };
855fb5c395SPieter Jansen van Vuuren 
nfp_flower_offload_one_police(struct nfp_app * app,bool ingress,bool pps,u32 id,u32 rate,u32 burst)86bbab5f93SBaowen Zheng int nfp_flower_offload_one_police(struct nfp_app *app, bool ingress,
87bbab5f93SBaowen Zheng 				  bool pps, u32 id, u32 rate, u32 burst)
88bbab5f93SBaowen Zheng {
89bbab5f93SBaowen Zheng 	struct nfp_police_config *config;
90bbab5f93SBaowen Zheng 	struct sk_buff *skb;
91bbab5f93SBaowen Zheng 
92bbab5f93SBaowen Zheng 	skb = nfp_flower_cmsg_alloc(app, sizeof(struct nfp_police_config),
93bbab5f93SBaowen Zheng 				    NFP_FLOWER_CMSG_TYPE_QOS_MOD, GFP_KERNEL);
94bbab5f93SBaowen Zheng 	if (!skb)
95bbab5f93SBaowen Zheng 		return -ENOMEM;
96bbab5f93SBaowen Zheng 
97bbab5f93SBaowen Zheng 	config = nfp_flower_cmsg_get_data(skb);
98bbab5f93SBaowen Zheng 	memset(config, 0, sizeof(struct nfp_police_config));
99bbab5f93SBaowen Zheng 	if (pps)
100bbab5f93SBaowen Zheng 		config->head.flags_opts |= cpu_to_be32(NFP_FL_QOS_PPS);
101bbab5f93SBaowen Zheng 	if (!ingress)
102bbab5f93SBaowen Zheng 		config->head.flags_opts |= cpu_to_be32(NFP_FL_QOS_METER);
103bbab5f93SBaowen Zheng 
104bbab5f93SBaowen Zheng 	if (ingress)
105bbab5f93SBaowen Zheng 		config->head.port = cpu_to_be32(id);
106bbab5f93SBaowen Zheng 	else
107bbab5f93SBaowen Zheng 		config->head.meter_id = cpu_to_be32(id);
108bbab5f93SBaowen Zheng 
109bbab5f93SBaowen Zheng 	config->bkt_tkn_p = cpu_to_be32(burst);
110bbab5f93SBaowen Zheng 	config->bkt_tkn_c = cpu_to_be32(burst);
111bbab5f93SBaowen Zheng 	config->pbs = cpu_to_be32(burst);
112bbab5f93SBaowen Zheng 	config->cbs = cpu_to_be32(burst);
113bbab5f93SBaowen Zheng 	config->pir = cpu_to_be32(rate);
114bbab5f93SBaowen Zheng 	config->cir = cpu_to_be32(rate);
115bbab5f93SBaowen Zheng 	nfp_ctrl_tx(app->ctrl, skb);
116bbab5f93SBaowen Zheng 
117bbab5f93SBaowen Zheng 	return 0;
118bbab5f93SBaowen Zheng }
119bbab5f93SBaowen Zheng 
nfp_policer_validate(const struct flow_action * action,const struct flow_action_entry * act,struct netlink_ext_ack * extack,bool ingress)120d97b4b10SJianbo Liu static int nfp_policer_validate(const struct flow_action *action,
121d97b4b10SJianbo Liu 				const struct flow_action_entry *act,
1229f1a948fSZiyang Chen 				struct netlink_ext_ack *extack,
1239f1a948fSZiyang Chen 				bool ingress)
124d97b4b10SJianbo Liu {
125d97b4b10SJianbo Liu 	if (act->police.exceed.act_id != FLOW_ACTION_DROP) {
126d97b4b10SJianbo Liu 		NL_SET_ERR_MSG_MOD(extack,
127d97b4b10SJianbo Liu 				   "Offload not supported when exceed action is not drop");
128d97b4b10SJianbo Liu 		return -EOPNOTSUPP;
129d97b4b10SJianbo Liu 	}
130d97b4b10SJianbo Liu 
1319f1a948fSZiyang Chen 	if (ingress) {
132ebe5555cSTianyu Yuan 		if (act->police.notexceed.act_id != FLOW_ACTION_CONTINUE &&
133d97b4b10SJianbo Liu 		    act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {
134d97b4b10SJianbo Liu 			NL_SET_ERR_MSG_MOD(extack,
1359f1a948fSZiyang Chen 					   "Offload not supported when conform action is not continue or ok");
136d97b4b10SJianbo Liu 			return -EOPNOTSUPP;
137d97b4b10SJianbo Liu 		}
1389f1a948fSZiyang Chen 	} else {
1399f1a948fSZiyang Chen 		if (act->police.notexceed.act_id != FLOW_ACTION_PIPE &&
1409f1a948fSZiyang Chen 		    act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {
1419f1a948fSZiyang Chen 			NL_SET_ERR_MSG_MOD(extack,
1429f1a948fSZiyang Chen 					   "Offload not supported when conform action is not pipe or ok");
1439f1a948fSZiyang Chen 			return -EOPNOTSUPP;
1449f1a948fSZiyang Chen 		}
1459f1a948fSZiyang Chen 	}
146d97b4b10SJianbo Liu 
147d97b4b10SJianbo Liu 	if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT &&
148d97b4b10SJianbo Liu 	    !flow_action_is_last_entry(action, act)) {
149d97b4b10SJianbo Liu 		NL_SET_ERR_MSG_MOD(extack,
150d97b4b10SJianbo Liu 				   "Offload not supported when conform action is ok, but action is not last");
151d97b4b10SJianbo Liu 		return -EOPNOTSUPP;
152d97b4b10SJianbo Liu 	}
153d97b4b10SJianbo Liu 
154d97b4b10SJianbo Liu 	if (act->police.peakrate_bytes_ps ||
155d97b4b10SJianbo Liu 	    act->police.avrate || act->police.overhead) {
156d97b4b10SJianbo Liu 		NL_SET_ERR_MSG_MOD(extack,
157d97b4b10SJianbo Liu 				   "Offload not supported when peakrate/avrate/overhead is configured");
158d97b4b10SJianbo Liu 		return -EOPNOTSUPP;
159d97b4b10SJianbo Liu 	}
160d97b4b10SJianbo Liu 
161d97b4b10SJianbo Liu 	return 0;
162d97b4b10SJianbo Liu }
163d97b4b10SJianbo Liu 
16449cbef13SPieter Jansen van Vuuren static int
nfp_flower_install_rate_limiter(struct nfp_app * app,struct net_device * netdev,struct tc_cls_matchall_offload * flow,struct netlink_ext_ack * extack)16549cbef13SPieter Jansen van Vuuren nfp_flower_install_rate_limiter(struct nfp_app *app, struct net_device *netdev,
16649cbef13SPieter Jansen van Vuuren 				struct tc_cls_matchall_offload *flow,
16749cbef13SPieter Jansen van Vuuren 				struct netlink_ext_ack *extack)
16849cbef13SPieter Jansen van Vuuren {
169631a44edSPeng Zhang 	struct flow_action_entry *paction = &flow->rule->action.entries[0];
170631a44edSPeng Zhang 	u32 action_num = flow->rule->action.num_entries;
1715fb5c395SPieter Jansen van Vuuren 	struct nfp_flower_priv *fl_priv = app->priv;
172631a44edSPeng Zhang 	struct flow_action_entry *action = NULL;
17349cbef13SPieter Jansen van Vuuren 	struct nfp_flower_repr_priv *repr_priv;
174631a44edSPeng Zhang 	u32 netdev_port_id, i;
17549cbef13SPieter Jansen van Vuuren 	struct nfp_repr *repr;
176631a44edSPeng Zhang 	bool pps_support;
177631a44edSPeng Zhang 	u32 bps_num = 0;
178631a44edSPeng Zhang 	u32 pps_num = 0;
1795f035af7SPo Liu 	u32 burst;
180bbab5f93SBaowen Zheng 	bool pps;
1815f035af7SPo Liu 	u64 rate;
182d97b4b10SJianbo Liu 	int err;
18349cbef13SPieter Jansen van Vuuren 
18449cbef13SPieter Jansen van Vuuren 	if (!nfp_netdev_is_nfp_repr(netdev)) {
18549cbef13SPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload not supported on higher level port");
18649cbef13SPieter Jansen van Vuuren 		return -EOPNOTSUPP;
18749cbef13SPieter Jansen van Vuuren 	}
18849cbef13SPieter Jansen van Vuuren 	repr = netdev_priv(netdev);
189d6787147SPieter Jansen van Vuuren 	repr_priv = repr->app_priv;
190631a44edSPeng Zhang 	netdev_port_id = nfp_repr_get_port_id(netdev);
191631a44edSPeng Zhang 	pps_support = !!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_PPS);
19249cbef13SPieter Jansen van Vuuren 
193d6787147SPieter Jansen van Vuuren 	if (repr_priv->block_shared) {
19449cbef13SPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload not supported on shared blocks");
19549cbef13SPieter Jansen van Vuuren 		return -EOPNOTSUPP;
19649cbef13SPieter Jansen van Vuuren 	}
19749cbef13SPieter Jansen van Vuuren 
19849cbef13SPieter Jansen van Vuuren 	if (repr->port->type != NFP_PORT_VF_PORT) {
19949cbef13SPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload not supported on non-VF ports");
20049cbef13SPieter Jansen van Vuuren 		return -EOPNOTSUPP;
20149cbef13SPieter Jansen van Vuuren 	}
20249cbef13SPieter Jansen van Vuuren 
203631a44edSPeng Zhang 	if (pps_support) {
204631a44edSPeng Zhang 		if (action_num > 2 || action_num == 0) {
205631a44edSPeng Zhang 			NL_SET_ERR_MSG_MOD(extack,
206631a44edSPeng Zhang 					   "unsupported offload: qos rate limit offload only support action number 1 or 2");
20749cbef13SPieter Jansen van Vuuren 			return -EOPNOTSUPP;
20849cbef13SPieter Jansen van Vuuren 		}
209631a44edSPeng Zhang 	} else {
210631a44edSPeng Zhang 		if (!flow_offload_has_one_action(&flow->rule->action)) {
211631a44edSPeng Zhang 			NL_SET_ERR_MSG_MOD(extack,
212631a44edSPeng Zhang 					   "unsupported offload: qos rate limit offload requires a single action");
213631a44edSPeng Zhang 			return -EOPNOTSUPP;
214631a44edSPeng Zhang 		}
215631a44edSPeng Zhang 	}
21649cbef13SPieter Jansen van Vuuren 
217ef01adaeSPablo Neira Ayuso 	if (flow->common.prio != 1) {
21849cbef13SPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload requires highest priority");
21949cbef13SPieter Jansen van Vuuren 		return -EOPNOTSUPP;
22049cbef13SPieter Jansen van Vuuren 	}
22149cbef13SPieter Jansen van Vuuren 
222631a44edSPeng Zhang 	for (i = 0 ; i < action_num; i++) {
223631a44edSPeng Zhang 		action = paction + i;
22449cbef13SPieter Jansen van Vuuren 		if (action->id != FLOW_ACTION_POLICE) {
225631a44edSPeng Zhang 			NL_SET_ERR_MSG_MOD(extack,
226631a44edSPeng Zhang 					   "unsupported offload: qos rate limit offload requires police action");
22749cbef13SPieter Jansen van Vuuren 			return -EOPNOTSUPP;
22849cbef13SPieter Jansen van Vuuren 		}
229d97b4b10SJianbo Liu 
2309f1a948fSZiyang Chen 		err = nfp_policer_validate(&flow->rule->action, action, extack, true);
231d97b4b10SJianbo Liu 		if (err)
232d97b4b10SJianbo Liu 			return err;
233d97b4b10SJianbo Liu 
234631a44edSPeng Zhang 		if (action->police.rate_bytes_ps > 0) {
235631a44edSPeng Zhang 			if (bps_num++) {
236631a44edSPeng Zhang 				NL_SET_ERR_MSG_MOD(extack,
237631a44edSPeng Zhang 						   "unsupported offload: qos rate limit offload only support one BPS action");
2386a56e199SBaowen Zheng 				return -EOPNOTSUPP;
2396a56e199SBaowen Zheng 			}
240631a44edSPeng Zhang 		}
241631a44edSPeng Zhang 		if (action->police.rate_pkt_ps > 0) {
242631a44edSPeng Zhang 			if (!pps_support) {
243631a44edSPeng Zhang 				NL_SET_ERR_MSG_MOD(extack,
244631a44edSPeng Zhang 						   "unsupported offload: FW does not support PPS action");
245631a44edSPeng Zhang 				return -EOPNOTSUPP;
246631a44edSPeng Zhang 			}
247631a44edSPeng Zhang 			if (pps_num++) {
248631a44edSPeng Zhang 				NL_SET_ERR_MSG_MOD(extack,
249631a44edSPeng Zhang 						   "unsupported offload: qos rate limit offload only support one PPS action");
250631a44edSPeng Zhang 				return -EOPNOTSUPP;
251631a44edSPeng Zhang 			}
252631a44edSPeng Zhang 		}
253631a44edSPeng Zhang 	}
2546a56e199SBaowen Zheng 
255631a44edSPeng Zhang 	for (i = 0 ; i < action_num; i++) {
256631a44edSPeng Zhang 		/* Set QoS data for this interface */
257631a44edSPeng Zhang 		action = paction + i;
258631a44edSPeng Zhang 		if (action->police.rate_bytes_ps > 0) {
25949cbef13SPieter Jansen van Vuuren 			rate = action->police.rate_bytes_ps;
2605f035af7SPo Liu 			burst = action->police.burst;
261631a44edSPeng Zhang 		} else if (action->police.rate_pkt_ps > 0) {
262631a44edSPeng Zhang 			rate = action->police.rate_pkt_ps;
263631a44edSPeng Zhang 			burst = action->police.burst_pkt;
264631a44edSPeng Zhang 		} else {
265631a44edSPeng Zhang 			NL_SET_ERR_MSG_MOD(extack,
266631a44edSPeng Zhang 					   "unsupported offload: qos rate limit is not BPS or PPS");
267631a44edSPeng Zhang 			continue;
268631a44edSPeng Zhang 		}
26949cbef13SPieter Jansen van Vuuren 
270631a44edSPeng Zhang 		if (rate != 0) {
271bbab5f93SBaowen Zheng 			pps = false;
272631a44edSPeng Zhang 			if (action->police.rate_pkt_ps > 0)
273bbab5f93SBaowen Zheng 				pps = true;
274bbab5f93SBaowen Zheng 			nfp_flower_offload_one_police(repr->app, true,
275bbab5f93SBaowen Zheng 						      pps, netdev_port_id,
276bbab5f93SBaowen Zheng 						      rate, burst);
277631a44edSPeng Zhang 		}
278631a44edSPeng Zhang 	}
27949cbef13SPieter Jansen van Vuuren 	repr_priv->qos_table.netdev_port_id = netdev_port_id;
2805fb5c395SPieter Jansen van Vuuren 	fl_priv->qos_rate_limiters++;
2815fb5c395SPieter Jansen van Vuuren 	if (fl_priv->qos_rate_limiters == 1)
2825fb5c395SPieter Jansen van Vuuren 		schedule_delayed_work(&fl_priv->qos_stats_work,
2835fb5c395SPieter Jansen van Vuuren 				      NFP_FL_QOS_UPDATE);
28449cbef13SPieter Jansen van Vuuren 
28549cbef13SPieter Jansen van Vuuren 	return 0;
28649cbef13SPieter Jansen van Vuuren }
28749cbef13SPieter Jansen van Vuuren 
28849cbef13SPieter Jansen van Vuuren static int
nfp_flower_remove_rate_limiter(struct nfp_app * app,struct net_device * netdev,struct tc_cls_matchall_offload * flow,struct netlink_ext_ack * extack)28949cbef13SPieter Jansen van Vuuren nfp_flower_remove_rate_limiter(struct nfp_app *app, struct net_device *netdev,
29049cbef13SPieter Jansen van Vuuren 			       struct tc_cls_matchall_offload *flow,
29149cbef13SPieter Jansen van Vuuren 			       struct netlink_ext_ack *extack)
29249cbef13SPieter Jansen van Vuuren {
2935fb5c395SPieter Jansen van Vuuren 	struct nfp_flower_priv *fl_priv = app->priv;
29449cbef13SPieter Jansen van Vuuren 	struct nfp_flower_repr_priv *repr_priv;
29549cbef13SPieter Jansen van Vuuren 	struct nfp_police_config *config;
296631a44edSPeng Zhang 	u32 netdev_port_id, i;
29749cbef13SPieter Jansen van Vuuren 	struct nfp_repr *repr;
29849cbef13SPieter Jansen van Vuuren 	struct sk_buff *skb;
299631a44edSPeng Zhang 	bool pps_support;
30049cbef13SPieter Jansen van Vuuren 
30149cbef13SPieter Jansen van Vuuren 	if (!nfp_netdev_is_nfp_repr(netdev)) {
30249cbef13SPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload not supported on higher level port");
30349cbef13SPieter Jansen van Vuuren 		return -EOPNOTSUPP;
30449cbef13SPieter Jansen van Vuuren 	}
30549cbef13SPieter Jansen van Vuuren 	repr = netdev_priv(netdev);
30649cbef13SPieter Jansen van Vuuren 
30749cbef13SPieter Jansen van Vuuren 	netdev_port_id = nfp_repr_get_port_id(netdev);
30849cbef13SPieter Jansen van Vuuren 	repr_priv = repr->app_priv;
309631a44edSPeng Zhang 	pps_support = !!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_PPS);
31049cbef13SPieter Jansen van Vuuren 
31149cbef13SPieter Jansen van Vuuren 	if (!repr_priv->qos_table.netdev_port_id) {
31249cbef13SPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: cannot remove qos entry that does not exist");
31349cbef13SPieter Jansen van Vuuren 		return -EOPNOTSUPP;
31449cbef13SPieter Jansen van Vuuren 	}
31549cbef13SPieter Jansen van Vuuren 
316631a44edSPeng Zhang 	memset(&repr_priv->qos_table, 0, sizeof(struct nfp_fl_qos));
317631a44edSPeng Zhang 	fl_priv->qos_rate_limiters--;
318631a44edSPeng Zhang 	if (!fl_priv->qos_rate_limiters)
319631a44edSPeng Zhang 		cancel_delayed_work_sync(&fl_priv->qos_stats_work);
320631a44edSPeng Zhang 	for (i = 0 ; i < NFP_FL_QOS_TYPE_MAX; i++) {
321631a44edSPeng Zhang 		if (i == NFP_FL_QOS_TYPE_PPS && !pps_support)
322631a44edSPeng Zhang 			break;
323631a44edSPeng Zhang 		/* 0:bps 1:pps
324631a44edSPeng Zhang 		 * Clear QoS data for this interface.
325631a44edSPeng Zhang 		 * There is no need to check if a specific QOS_TYPE was
326631a44edSPeng Zhang 		 * configured as the firmware handles clearing a QoS entry
327631a44edSPeng Zhang 		 * safely, even if it wasn't explicitly added.
328631a44edSPeng Zhang 		 */
32949cbef13SPieter Jansen van Vuuren 		skb = nfp_flower_cmsg_alloc(repr->app, sizeof(struct nfp_police_config),
33049cbef13SPieter Jansen van Vuuren 					    NFP_FLOWER_CMSG_TYPE_QOS_DEL, GFP_KERNEL);
33149cbef13SPieter Jansen van Vuuren 		if (!skb)
33249cbef13SPieter Jansen van Vuuren 			return -ENOMEM;
33349cbef13SPieter Jansen van Vuuren 
33449cbef13SPieter Jansen van Vuuren 		config = nfp_flower_cmsg_get_data(skb);
33549cbef13SPieter Jansen van Vuuren 		memset(config, 0, sizeof(struct nfp_police_config));
336631a44edSPeng Zhang 		if (i == NFP_FL_QOS_TYPE_PPS)
337631a44edSPeng Zhang 			config->head.flags_opts = cpu_to_be32(NFP_FL_QOS_PPS);
33849cbef13SPieter Jansen van Vuuren 		config->head.port = cpu_to_be32(netdev_port_id);
33949cbef13SPieter Jansen van Vuuren 		nfp_ctrl_tx(repr->app->ctrl, skb);
340631a44edSPeng Zhang 	}
34149cbef13SPieter Jansen van Vuuren 
34249cbef13SPieter Jansen van Vuuren 	return 0;
34349cbef13SPieter Jansen van Vuuren }
344b66d035eSPieter Jansen van Vuuren 
nfp_flower_stats_rlim_reply(struct nfp_app * app,struct sk_buff * skb)3455fb5c395SPieter Jansen van Vuuren void nfp_flower_stats_rlim_reply(struct nfp_app *app, struct sk_buff *skb)
3465fb5c395SPieter Jansen van Vuuren {
3475fb5c395SPieter Jansen van Vuuren 	struct nfp_flower_priv *fl_priv = app->priv;
3485fb5c395SPieter Jansen van Vuuren 	struct nfp_flower_repr_priv *repr_priv;
3495fb5c395SPieter Jansen van Vuuren 	struct nfp_police_stats_reply *msg;
3505fb5c395SPieter Jansen van Vuuren 	struct nfp_stat_pair *curr_stats;
3515fb5c395SPieter Jansen van Vuuren 	struct nfp_stat_pair *prev_stats;
3525fb5c395SPieter Jansen van Vuuren 	struct net_device *netdev;
3535fb5c395SPieter Jansen van Vuuren 	struct nfp_repr *repr;
3545fb5c395SPieter Jansen van Vuuren 	u32 netdev_port_id;
3555fb5c395SPieter Jansen van Vuuren 
3565fb5c395SPieter Jansen van Vuuren 	msg = nfp_flower_cmsg_get_data(skb);
357776178a5SBaowen Zheng 	if (be32_to_cpu(msg->head.flags_opts) & NFP_FL_QOS_METER)
358776178a5SBaowen Zheng 		return nfp_act_stats_reply(app, msg);
359776178a5SBaowen Zheng 
3605fb5c395SPieter Jansen van Vuuren 	netdev_port_id = be32_to_cpu(msg->head.port);
3615fb5c395SPieter Jansen van Vuuren 	rcu_read_lock();
3625fb5c395SPieter Jansen van Vuuren 	netdev = nfp_app_dev_get(app, netdev_port_id, NULL);
3635fb5c395SPieter Jansen van Vuuren 	if (!netdev)
3645fb5c395SPieter Jansen van Vuuren 		goto exit_unlock_rcu;
3655fb5c395SPieter Jansen van Vuuren 
3665fb5c395SPieter Jansen van Vuuren 	repr = netdev_priv(netdev);
3675fb5c395SPieter Jansen van Vuuren 	repr_priv = repr->app_priv;
3685fb5c395SPieter Jansen van Vuuren 	curr_stats = &repr_priv->qos_table.curr_stats;
3695fb5c395SPieter Jansen van Vuuren 	prev_stats = &repr_priv->qos_table.prev_stats;
3705fb5c395SPieter Jansen van Vuuren 
3715fb5c395SPieter Jansen van Vuuren 	spin_lock_bh(&fl_priv->qos_stats_lock);
3725fb5c395SPieter Jansen van Vuuren 	curr_stats->pkts = be64_to_cpu(msg->pass_pkts) +
3735fb5c395SPieter Jansen van Vuuren 			   be64_to_cpu(msg->drop_pkts);
3745fb5c395SPieter Jansen van Vuuren 	curr_stats->bytes = be64_to_cpu(msg->pass_bytes) +
3755fb5c395SPieter Jansen van Vuuren 			    be64_to_cpu(msg->drop_bytes);
3765fb5c395SPieter Jansen van Vuuren 
3775fb5c395SPieter Jansen van Vuuren 	if (!repr_priv->qos_table.last_update) {
3785fb5c395SPieter Jansen van Vuuren 		prev_stats->pkts = curr_stats->pkts;
3795fb5c395SPieter Jansen van Vuuren 		prev_stats->bytes = curr_stats->bytes;
3805fb5c395SPieter Jansen van Vuuren 	}
3815fb5c395SPieter Jansen van Vuuren 
3825fb5c395SPieter Jansen van Vuuren 	repr_priv->qos_table.last_update = jiffies;
3835fb5c395SPieter Jansen van Vuuren 	spin_unlock_bh(&fl_priv->qos_stats_lock);
3845fb5c395SPieter Jansen van Vuuren 
3855fb5c395SPieter Jansen van Vuuren exit_unlock_rcu:
3865fb5c395SPieter Jansen van Vuuren 	rcu_read_unlock();
3875fb5c395SPieter Jansen van Vuuren }
3885fb5c395SPieter Jansen van Vuuren 
3895fb5c395SPieter Jansen van Vuuren static void
nfp_flower_stats_rlim_request(struct nfp_flower_priv * fl_priv,u32 id,bool ingress)3905fb5c395SPieter Jansen van Vuuren nfp_flower_stats_rlim_request(struct nfp_flower_priv *fl_priv,
391776178a5SBaowen Zheng 			      u32 id, bool ingress)
3925fb5c395SPieter Jansen van Vuuren {
3935fb5c395SPieter Jansen van Vuuren 	struct nfp_police_cfg_head *head;
3945fb5c395SPieter Jansen van Vuuren 	struct sk_buff *skb;
3955fb5c395SPieter Jansen van Vuuren 
3965fb5c395SPieter Jansen van Vuuren 	skb = nfp_flower_cmsg_alloc(fl_priv->app,
3975fb5c395SPieter Jansen van Vuuren 				    sizeof(struct nfp_police_cfg_head),
3985fb5c395SPieter Jansen van Vuuren 				    NFP_FLOWER_CMSG_TYPE_QOS_STATS,
3995fb5c395SPieter Jansen van Vuuren 				    GFP_ATOMIC);
4005fb5c395SPieter Jansen van Vuuren 	if (!skb)
4015fb5c395SPieter Jansen van Vuuren 		return;
4025fb5c395SPieter Jansen van Vuuren 	head = nfp_flower_cmsg_get_data(skb);
403776178a5SBaowen Zheng 
4045fb5c395SPieter Jansen van Vuuren 	memset(head, 0, sizeof(struct nfp_police_cfg_head));
405776178a5SBaowen Zheng 	if (ingress) {
406776178a5SBaowen Zheng 		head->port = cpu_to_be32(id);
407776178a5SBaowen Zheng 	} else {
408776178a5SBaowen Zheng 		head->flags_opts = cpu_to_be32(NFP_FL_QOS_METER);
409776178a5SBaowen Zheng 		head->meter_id = cpu_to_be32(id);
410776178a5SBaowen Zheng 	}
4115fb5c395SPieter Jansen van Vuuren 
4125fb5c395SPieter Jansen van Vuuren 	nfp_ctrl_tx(fl_priv->app->ctrl, skb);
4135fb5c395SPieter Jansen van Vuuren }
4145fb5c395SPieter Jansen van Vuuren 
4155fb5c395SPieter Jansen van Vuuren static void
nfp_flower_stats_rlim_request_all(struct nfp_flower_priv * fl_priv)4165fb5c395SPieter Jansen van Vuuren nfp_flower_stats_rlim_request_all(struct nfp_flower_priv *fl_priv)
4175fb5c395SPieter Jansen van Vuuren {
4185fb5c395SPieter Jansen van Vuuren 	struct nfp_reprs *repr_set;
4195fb5c395SPieter Jansen van Vuuren 	int i;
4205fb5c395SPieter Jansen van Vuuren 
4215fb5c395SPieter Jansen van Vuuren 	rcu_read_lock();
4225fb5c395SPieter Jansen van Vuuren 	repr_set = rcu_dereference(fl_priv->app->reprs[NFP_REPR_TYPE_VF]);
4235fb5c395SPieter Jansen van Vuuren 	if (!repr_set)
4245fb5c395SPieter Jansen van Vuuren 		goto exit_unlock_rcu;
4255fb5c395SPieter Jansen van Vuuren 
4265fb5c395SPieter Jansen van Vuuren 	for (i = 0; i < repr_set->num_reprs; i++) {
4275fb5c395SPieter Jansen van Vuuren 		struct net_device *netdev;
4285fb5c395SPieter Jansen van Vuuren 
4295fb5c395SPieter Jansen van Vuuren 		netdev = rcu_dereference(repr_set->reprs[i]);
4305fb5c395SPieter Jansen van Vuuren 		if (netdev) {
4315fb5c395SPieter Jansen van Vuuren 			struct nfp_repr *priv = netdev_priv(netdev);
4325fb5c395SPieter Jansen van Vuuren 			struct nfp_flower_repr_priv *repr_priv;
4335fb5c395SPieter Jansen van Vuuren 			u32 netdev_port_id;
4345fb5c395SPieter Jansen van Vuuren 
4355fb5c395SPieter Jansen van Vuuren 			repr_priv = priv->app_priv;
4365fb5c395SPieter Jansen van Vuuren 			netdev_port_id = repr_priv->qos_table.netdev_port_id;
4375fb5c395SPieter Jansen van Vuuren 			if (!netdev_port_id)
4385fb5c395SPieter Jansen van Vuuren 				continue;
4395fb5c395SPieter Jansen van Vuuren 
440776178a5SBaowen Zheng 			nfp_flower_stats_rlim_request(fl_priv,
441776178a5SBaowen Zheng 						      netdev_port_id, true);
4425fb5c395SPieter Jansen van Vuuren 		}
4435fb5c395SPieter Jansen van Vuuren 	}
4445fb5c395SPieter Jansen van Vuuren 
4455fb5c395SPieter Jansen van Vuuren exit_unlock_rcu:
4465fb5c395SPieter Jansen van Vuuren 	rcu_read_unlock();
4475fb5c395SPieter Jansen van Vuuren }
4485fb5c395SPieter Jansen van Vuuren 
update_stats_cache(struct work_struct * work)4495fb5c395SPieter Jansen van Vuuren static void update_stats_cache(struct work_struct *work)
4505fb5c395SPieter Jansen van Vuuren {
4515fb5c395SPieter Jansen van Vuuren 	struct delayed_work *delayed_work;
4525fb5c395SPieter Jansen van Vuuren 	struct nfp_flower_priv *fl_priv;
4535fb5c395SPieter Jansen van Vuuren 
4545fb5c395SPieter Jansen van Vuuren 	delayed_work = to_delayed_work(work);
4555fb5c395SPieter Jansen van Vuuren 	fl_priv = container_of(delayed_work, struct nfp_flower_priv,
4565fb5c395SPieter Jansen van Vuuren 			       qos_stats_work);
4575fb5c395SPieter Jansen van Vuuren 
4585fb5c395SPieter Jansen van Vuuren 	nfp_flower_stats_rlim_request_all(fl_priv);
459776178a5SBaowen Zheng 	nfp_flower_stats_meter_request_all(fl_priv);
460776178a5SBaowen Zheng 
4615fb5c395SPieter Jansen van Vuuren 	schedule_delayed_work(&fl_priv->qos_stats_work, NFP_FL_QOS_UPDATE);
4625fb5c395SPieter Jansen van Vuuren }
4635fb5c395SPieter Jansen van Vuuren 
4645fb5c395SPieter Jansen van Vuuren static int
nfp_flower_stats_rate_limiter(struct nfp_app * app,struct net_device * netdev,struct tc_cls_matchall_offload * flow,struct netlink_ext_ack * extack)4655fb5c395SPieter Jansen van Vuuren nfp_flower_stats_rate_limiter(struct nfp_app *app, struct net_device *netdev,
4665fb5c395SPieter Jansen van Vuuren 			      struct tc_cls_matchall_offload *flow,
4675fb5c395SPieter Jansen van Vuuren 			      struct netlink_ext_ack *extack)
4685fb5c395SPieter Jansen van Vuuren {
4695fb5c395SPieter Jansen van Vuuren 	struct nfp_flower_priv *fl_priv = app->priv;
4705fb5c395SPieter Jansen van Vuuren 	struct nfp_flower_repr_priv *repr_priv;
4715fb5c395SPieter Jansen van Vuuren 	struct nfp_stat_pair *curr_stats;
4725fb5c395SPieter Jansen van Vuuren 	struct nfp_stat_pair *prev_stats;
4735fb5c395SPieter Jansen van Vuuren 	u64 diff_bytes, diff_pkts;
4745fb5c395SPieter Jansen van Vuuren 	struct nfp_repr *repr;
4755fb5c395SPieter Jansen van Vuuren 
4765fb5c395SPieter Jansen van Vuuren 	if (!nfp_netdev_is_nfp_repr(netdev)) {
4775fb5c395SPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload not supported on higher level port");
4785fb5c395SPieter Jansen van Vuuren 		return -EOPNOTSUPP;
4795fb5c395SPieter Jansen van Vuuren 	}
4805fb5c395SPieter Jansen van Vuuren 	repr = netdev_priv(netdev);
4815fb5c395SPieter Jansen van Vuuren 
4825fb5c395SPieter Jansen van Vuuren 	repr_priv = repr->app_priv;
4835fb5c395SPieter Jansen van Vuuren 	if (!repr_priv->qos_table.netdev_port_id) {
4845fb5c395SPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: cannot find qos entry for stats update");
4855fb5c395SPieter Jansen van Vuuren 		return -EOPNOTSUPP;
4865fb5c395SPieter Jansen van Vuuren 	}
4875fb5c395SPieter Jansen van Vuuren 
4885fb5c395SPieter Jansen van Vuuren 	spin_lock_bh(&fl_priv->qos_stats_lock);
4895fb5c395SPieter Jansen van Vuuren 	curr_stats = &repr_priv->qos_table.curr_stats;
4905fb5c395SPieter Jansen van Vuuren 	prev_stats = &repr_priv->qos_table.prev_stats;
4915fb5c395SPieter Jansen van Vuuren 	diff_pkts = curr_stats->pkts - prev_stats->pkts;
4925fb5c395SPieter Jansen van Vuuren 	diff_bytes = curr_stats->bytes - prev_stats->bytes;
4935fb5c395SPieter Jansen van Vuuren 	prev_stats->pkts = curr_stats->pkts;
4945fb5c395SPieter Jansen van Vuuren 	prev_stats->bytes = curr_stats->bytes;
4955fb5c395SPieter Jansen van Vuuren 	spin_unlock_bh(&fl_priv->qos_stats_lock);
4965fb5c395SPieter Jansen van Vuuren 
4974b61d3e8SPo Liu 	flow_stats_update(&flow->stats, diff_bytes, diff_pkts, 0,
49893a129ebSJiri Pirko 			  repr_priv->qos_table.last_update,
49993a129ebSJiri Pirko 			  FLOW_ACTION_HW_STATS_DELAYED);
5005fb5c395SPieter Jansen van Vuuren 	return 0;
5015fb5c395SPieter Jansen van Vuuren }
5025fb5c395SPieter Jansen van Vuuren 
nfp_flower_qos_init(struct nfp_app * app)5035fb5c395SPieter Jansen van Vuuren void nfp_flower_qos_init(struct nfp_app *app)
5045fb5c395SPieter Jansen van Vuuren {
5055fb5c395SPieter Jansen van Vuuren 	struct nfp_flower_priv *fl_priv = app->priv;
5065fb5c395SPieter Jansen van Vuuren 
5075fb5c395SPieter Jansen van Vuuren 	spin_lock_init(&fl_priv->qos_stats_lock);
50826ff98d7SBaowen Zheng 	mutex_init(&fl_priv->meter_stats_lock);
50926ff98d7SBaowen Zheng 	nfp_init_meter_table(app);
51026ff98d7SBaowen Zheng 
5115fb5c395SPieter Jansen van Vuuren 	INIT_DELAYED_WORK(&fl_priv->qos_stats_work, &update_stats_cache);
5125fb5c395SPieter Jansen van Vuuren }
5135fb5c395SPieter Jansen van Vuuren 
nfp_flower_qos_cleanup(struct nfp_app * app)5145fb5c395SPieter Jansen van Vuuren void nfp_flower_qos_cleanup(struct nfp_app *app)
5155fb5c395SPieter Jansen van Vuuren {
5165fb5c395SPieter Jansen van Vuuren 	struct nfp_flower_priv *fl_priv = app->priv;
5175fb5c395SPieter Jansen van Vuuren 
5185fb5c395SPieter Jansen van Vuuren 	cancel_delayed_work_sync(&fl_priv->qos_stats_work);
5195fb5c395SPieter Jansen van Vuuren }
5205fb5c395SPieter Jansen van Vuuren 
nfp_flower_setup_qos_offload(struct nfp_app * app,struct net_device * netdev,struct tc_cls_matchall_offload * flow)521b66d035eSPieter Jansen van Vuuren int nfp_flower_setup_qos_offload(struct nfp_app *app, struct net_device *netdev,
522b66d035eSPieter Jansen van Vuuren 				 struct tc_cls_matchall_offload *flow)
523b66d035eSPieter Jansen van Vuuren {
524b66d035eSPieter Jansen van Vuuren 	struct netlink_ext_ack *extack = flow->common.extack;
525b66d035eSPieter Jansen van Vuuren 	struct nfp_flower_priv *fl_priv = app->priv;
526*14690995SYanguo Li 	int ret;
527b66d035eSPieter Jansen van Vuuren 
528b66d035eSPieter Jansen van Vuuren 	if (!(fl_priv->flower_ext_feats & NFP_FL_FEATS_VF_RLIM)) {
529b66d035eSPieter Jansen van Vuuren 		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: loaded firmware does not support qos rate limit offload");
530b66d035eSPieter Jansen van Vuuren 		return -EOPNOTSUPP;
531b66d035eSPieter Jansen van Vuuren 	}
532b66d035eSPieter Jansen van Vuuren 
533*14690995SYanguo Li 	mutex_lock(&fl_priv->nfp_fl_lock);
53449cbef13SPieter Jansen van Vuuren 	switch (flow->command) {
53549cbef13SPieter Jansen van Vuuren 	case TC_CLSMATCHALL_REPLACE:
536*14690995SYanguo Li 		ret = nfp_flower_install_rate_limiter(app, netdev, flow, extack);
537*14690995SYanguo Li 		break;
53849cbef13SPieter Jansen van Vuuren 	case TC_CLSMATCHALL_DESTROY:
539*14690995SYanguo Li 		ret = nfp_flower_remove_rate_limiter(app, netdev, flow, extack);
540*14690995SYanguo Li 		break;
5415fb5c395SPieter Jansen van Vuuren 	case TC_CLSMATCHALL_STATS:
542*14690995SYanguo Li 		ret = nfp_flower_stats_rate_limiter(app, netdev, flow, extack);
543*14690995SYanguo Li 		break;
54449cbef13SPieter Jansen van Vuuren 	default:
545*14690995SYanguo Li 		ret = -EOPNOTSUPP;
546*14690995SYanguo Li 		break;
547b66d035eSPieter Jansen van Vuuren 	}
548*14690995SYanguo Li 	mutex_unlock(&fl_priv->nfp_fl_lock);
549*14690995SYanguo Li 
550*14690995SYanguo Li 	return ret;
55149cbef13SPieter Jansen van Vuuren }
55259080da0SBaowen Zheng 
5539bacb93bSWalter Heymans /* Offload tc action, currently only for tc police */
55459080da0SBaowen Zheng 
55526ff98d7SBaowen Zheng static const struct rhashtable_params stats_meter_table_params = {
55626ff98d7SBaowen Zheng 	.key_offset	= offsetof(struct nfp_meter_entry, meter_id),
55726ff98d7SBaowen Zheng 	.head_offset	= offsetof(struct nfp_meter_entry, ht_node),
55826ff98d7SBaowen Zheng 	.key_len	= sizeof(u32),
55926ff98d7SBaowen Zheng };
56026ff98d7SBaowen Zheng 
561147747ecSBaowen Zheng struct nfp_meter_entry *
nfp_flower_search_meter_entry(struct nfp_app * app,u32 meter_id)56226ff98d7SBaowen Zheng nfp_flower_search_meter_entry(struct nfp_app *app, u32 meter_id)
56326ff98d7SBaowen Zheng {
56426ff98d7SBaowen Zheng 	struct nfp_flower_priv *priv = app->priv;
56526ff98d7SBaowen Zheng 
56626ff98d7SBaowen Zheng 	return rhashtable_lookup_fast(&priv->meter_table, &meter_id,
56726ff98d7SBaowen Zheng 				      stats_meter_table_params);
56826ff98d7SBaowen Zheng }
56926ff98d7SBaowen Zheng 
57026ff98d7SBaowen Zheng static struct nfp_meter_entry *
nfp_flower_add_meter_entry(struct nfp_app * app,u32 meter_id)57126ff98d7SBaowen Zheng nfp_flower_add_meter_entry(struct nfp_app *app, u32 meter_id)
57226ff98d7SBaowen Zheng {
57326ff98d7SBaowen Zheng 	struct nfp_meter_entry *meter_entry = NULL;
57426ff98d7SBaowen Zheng 	struct nfp_flower_priv *priv = app->priv;
57526ff98d7SBaowen Zheng 
57626ff98d7SBaowen Zheng 	meter_entry = rhashtable_lookup_fast(&priv->meter_table,
57726ff98d7SBaowen Zheng 					     &meter_id,
57826ff98d7SBaowen Zheng 					     stats_meter_table_params);
57926ff98d7SBaowen Zheng 	if (meter_entry)
58026ff98d7SBaowen Zheng 		return meter_entry;
58126ff98d7SBaowen Zheng 
58226ff98d7SBaowen Zheng 	meter_entry = kzalloc(sizeof(*meter_entry), GFP_KERNEL);
58326ff98d7SBaowen Zheng 	if (!meter_entry)
58426ff98d7SBaowen Zheng 		return NULL;
58526ff98d7SBaowen Zheng 
58626ff98d7SBaowen Zheng 	meter_entry->meter_id = meter_id;
58726ff98d7SBaowen Zheng 	meter_entry->used = jiffies;
58826ff98d7SBaowen Zheng 	if (rhashtable_insert_fast(&priv->meter_table, &meter_entry->ht_node,
58926ff98d7SBaowen Zheng 				   stats_meter_table_params)) {
59026ff98d7SBaowen Zheng 		kfree(meter_entry);
59126ff98d7SBaowen Zheng 		return NULL;
59226ff98d7SBaowen Zheng 	}
59326ff98d7SBaowen Zheng 
59426ff98d7SBaowen Zheng 	priv->qos_rate_limiters++;
59526ff98d7SBaowen Zheng 	if (priv->qos_rate_limiters == 1)
59626ff98d7SBaowen Zheng 		schedule_delayed_work(&priv->qos_stats_work,
59726ff98d7SBaowen Zheng 				      NFP_FL_QOS_UPDATE);
59826ff98d7SBaowen Zheng 
59926ff98d7SBaowen Zheng 	return meter_entry;
60026ff98d7SBaowen Zheng }
60126ff98d7SBaowen Zheng 
nfp_flower_del_meter_entry(struct nfp_app * app,u32 meter_id)60226ff98d7SBaowen Zheng static void nfp_flower_del_meter_entry(struct nfp_app *app, u32 meter_id)
60326ff98d7SBaowen Zheng {
60426ff98d7SBaowen Zheng 	struct nfp_meter_entry *meter_entry = NULL;
60526ff98d7SBaowen Zheng 	struct nfp_flower_priv *priv = app->priv;
60626ff98d7SBaowen Zheng 
60726ff98d7SBaowen Zheng 	meter_entry = rhashtable_lookup_fast(&priv->meter_table, &meter_id,
60826ff98d7SBaowen Zheng 					     stats_meter_table_params);
60926ff98d7SBaowen Zheng 	if (!meter_entry)
61026ff98d7SBaowen Zheng 		return;
61126ff98d7SBaowen Zheng 
61226ff98d7SBaowen Zheng 	rhashtable_remove_fast(&priv->meter_table,
61326ff98d7SBaowen Zheng 			       &meter_entry->ht_node,
61426ff98d7SBaowen Zheng 			       stats_meter_table_params);
61526ff98d7SBaowen Zheng 	kfree(meter_entry);
61626ff98d7SBaowen Zheng 	priv->qos_rate_limiters--;
61726ff98d7SBaowen Zheng 	if (!priv->qos_rate_limiters)
61826ff98d7SBaowen Zheng 		cancel_delayed_work_sync(&priv->qos_stats_work);
61926ff98d7SBaowen Zheng }
62026ff98d7SBaowen Zheng 
nfp_flower_setup_meter_entry(struct nfp_app * app,const struct flow_action_entry * action,enum nfp_meter_op op,u32 meter_id)62126ff98d7SBaowen Zheng int nfp_flower_setup_meter_entry(struct nfp_app *app,
62226ff98d7SBaowen Zheng 				 const struct flow_action_entry *action,
62326ff98d7SBaowen Zheng 				 enum nfp_meter_op op,
62426ff98d7SBaowen Zheng 				 u32 meter_id)
62526ff98d7SBaowen Zheng {
62626ff98d7SBaowen Zheng 	struct nfp_flower_priv *fl_priv = app->priv;
62726ff98d7SBaowen Zheng 	struct nfp_meter_entry *meter_entry = NULL;
62826ff98d7SBaowen Zheng 	int err = 0;
62926ff98d7SBaowen Zheng 
63026ff98d7SBaowen Zheng 	mutex_lock(&fl_priv->meter_stats_lock);
63126ff98d7SBaowen Zheng 
63226ff98d7SBaowen Zheng 	switch (op) {
63326ff98d7SBaowen Zheng 	case NFP_METER_DEL:
63426ff98d7SBaowen Zheng 		nfp_flower_del_meter_entry(app, meter_id);
63526ff98d7SBaowen Zheng 		goto exit_unlock;
63626ff98d7SBaowen Zheng 	case NFP_METER_ADD:
63726ff98d7SBaowen Zheng 		meter_entry = nfp_flower_add_meter_entry(app, meter_id);
63826ff98d7SBaowen Zheng 		break;
63926ff98d7SBaowen Zheng 	default:
64026ff98d7SBaowen Zheng 		err = -EOPNOTSUPP;
64126ff98d7SBaowen Zheng 		goto exit_unlock;
64226ff98d7SBaowen Zheng 	}
64326ff98d7SBaowen Zheng 
64426ff98d7SBaowen Zheng 	if (!meter_entry) {
64526ff98d7SBaowen Zheng 		err = -ENOMEM;
64626ff98d7SBaowen Zheng 		goto exit_unlock;
64726ff98d7SBaowen Zheng 	}
64826ff98d7SBaowen Zheng 
64926ff98d7SBaowen Zheng 	if (action->police.rate_bytes_ps > 0) {
65026ff98d7SBaowen Zheng 		meter_entry->bps = true;
65126ff98d7SBaowen Zheng 		meter_entry->rate = action->police.rate_bytes_ps;
65226ff98d7SBaowen Zheng 		meter_entry->burst = action->police.burst;
65326ff98d7SBaowen Zheng 	} else {
65426ff98d7SBaowen Zheng 		meter_entry->bps = false;
65526ff98d7SBaowen Zheng 		meter_entry->rate = action->police.rate_pkt_ps;
65626ff98d7SBaowen Zheng 		meter_entry->burst = action->police.burst_pkt;
65726ff98d7SBaowen Zheng 	}
65826ff98d7SBaowen Zheng 
65926ff98d7SBaowen Zheng exit_unlock:
66026ff98d7SBaowen Zheng 	mutex_unlock(&fl_priv->meter_stats_lock);
66126ff98d7SBaowen Zheng 	return err;
66226ff98d7SBaowen Zheng }
66326ff98d7SBaowen Zheng 
nfp_init_meter_table(struct nfp_app * app)66426ff98d7SBaowen Zheng int nfp_init_meter_table(struct nfp_app *app)
66526ff98d7SBaowen Zheng {
66626ff98d7SBaowen Zheng 	struct nfp_flower_priv *priv = app->priv;
66726ff98d7SBaowen Zheng 
66826ff98d7SBaowen Zheng 	return rhashtable_init(&priv->meter_table, &stats_meter_table_params);
66926ff98d7SBaowen Zheng }
67026ff98d7SBaowen Zheng 
671776178a5SBaowen Zheng void
nfp_flower_stats_meter_request_all(struct nfp_flower_priv * fl_priv)672776178a5SBaowen Zheng nfp_flower_stats_meter_request_all(struct nfp_flower_priv *fl_priv)
673776178a5SBaowen Zheng {
674776178a5SBaowen Zheng 	struct nfp_meter_entry *meter_entry = NULL;
675776178a5SBaowen Zheng 	struct rhashtable_iter iter;
676776178a5SBaowen Zheng 
677776178a5SBaowen Zheng 	mutex_lock(&fl_priv->meter_stats_lock);
678776178a5SBaowen Zheng 	rhashtable_walk_enter(&fl_priv->meter_table, &iter);
679776178a5SBaowen Zheng 	rhashtable_walk_start(&iter);
680776178a5SBaowen Zheng 
681776178a5SBaowen Zheng 	while ((meter_entry = rhashtable_walk_next(&iter)) != NULL) {
682776178a5SBaowen Zheng 		if (IS_ERR(meter_entry))
683776178a5SBaowen Zheng 			continue;
684776178a5SBaowen Zheng 		nfp_flower_stats_rlim_request(fl_priv,
685776178a5SBaowen Zheng 					      meter_entry->meter_id, false);
686776178a5SBaowen Zheng 	}
687776178a5SBaowen Zheng 
688776178a5SBaowen Zheng 	rhashtable_walk_stop(&iter);
689776178a5SBaowen Zheng 	rhashtable_walk_exit(&iter);
690776178a5SBaowen Zheng 	mutex_unlock(&fl_priv->meter_stats_lock);
691776178a5SBaowen Zheng }
692776178a5SBaowen Zheng 
69359080da0SBaowen Zheng static int
nfp_act_install_actions(struct nfp_app * app,struct flow_offload_action * fl_act,struct netlink_ext_ack * extack)69459080da0SBaowen Zheng nfp_act_install_actions(struct nfp_app *app, struct flow_offload_action *fl_act,
69559080da0SBaowen Zheng 			struct netlink_ext_ack *extack)
69659080da0SBaowen Zheng {
69759080da0SBaowen Zheng 	struct flow_action_entry *paction = &fl_act->action.entries[0];
69859080da0SBaowen Zheng 	u32 action_num = fl_act->action.num_entries;
69959080da0SBaowen Zheng 	struct nfp_flower_priv *fl_priv = app->priv;
70059080da0SBaowen Zheng 	struct flow_action_entry *action = NULL;
70159080da0SBaowen Zheng 	u32 burst, i, meter_id;
70259080da0SBaowen Zheng 	bool pps_support, pps;
70359080da0SBaowen Zheng 	bool add = false;
70459080da0SBaowen Zheng 	u64 rate;
7059f1a948fSZiyang Chen 	int err;
70659080da0SBaowen Zheng 
70759080da0SBaowen Zheng 	pps_support = !!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_PPS);
70859080da0SBaowen Zheng 
70959080da0SBaowen Zheng 	for (i = 0 ; i < action_num; i++) {
7109bacb93bSWalter Heymans 		/* Set qos associate data for this interface */
71159080da0SBaowen Zheng 		action = paction + i;
71259080da0SBaowen Zheng 		if (action->id != FLOW_ACTION_POLICE) {
71359080da0SBaowen Zheng 			NL_SET_ERR_MSG_MOD(extack,
71459080da0SBaowen Zheng 					   "unsupported offload: qos rate limit offload requires police action");
71559080da0SBaowen Zheng 			continue;
71659080da0SBaowen Zheng 		}
7179f1a948fSZiyang Chen 
7189f1a948fSZiyang Chen 		err = nfp_policer_validate(&fl_act->action, action, extack, false);
7199f1a948fSZiyang Chen 		if (err)
7209f1a948fSZiyang Chen 			return err;
7219f1a948fSZiyang Chen 
72259080da0SBaowen Zheng 		if (action->police.rate_bytes_ps > 0) {
72359080da0SBaowen Zheng 			rate = action->police.rate_bytes_ps;
72459080da0SBaowen Zheng 			burst = action->police.burst;
72559080da0SBaowen Zheng 		} else if (action->police.rate_pkt_ps > 0 && pps_support) {
72659080da0SBaowen Zheng 			rate = action->police.rate_pkt_ps;
72759080da0SBaowen Zheng 			burst = action->police.burst_pkt;
72859080da0SBaowen Zheng 		} else {
72959080da0SBaowen Zheng 			NL_SET_ERR_MSG_MOD(extack,
73059080da0SBaowen Zheng 					   "unsupported offload: unsupported qos rate limit");
73159080da0SBaowen Zheng 			continue;
73259080da0SBaowen Zheng 		}
73359080da0SBaowen Zheng 
73459080da0SBaowen Zheng 		if (rate != 0) {
73526ff98d7SBaowen Zheng 			meter_id = action->hw_index;
73626ff98d7SBaowen Zheng 			if (nfp_flower_setup_meter_entry(app, action, NFP_METER_ADD, meter_id))
73726ff98d7SBaowen Zheng 				continue;
73826ff98d7SBaowen Zheng 
73959080da0SBaowen Zheng 			pps = false;
74059080da0SBaowen Zheng 			if (action->police.rate_pkt_ps > 0)
74159080da0SBaowen Zheng 				pps = true;
74259080da0SBaowen Zheng 			nfp_flower_offload_one_police(app, false, pps, meter_id,
74359080da0SBaowen Zheng 						      rate, burst);
74459080da0SBaowen Zheng 			add = true;
74559080da0SBaowen Zheng 		}
74659080da0SBaowen Zheng 	}
74759080da0SBaowen Zheng 
74859080da0SBaowen Zheng 	return add ? 0 : -EOPNOTSUPP;
74959080da0SBaowen Zheng }
75059080da0SBaowen Zheng 
75159080da0SBaowen Zheng static int
nfp_act_remove_actions(struct nfp_app * app,struct flow_offload_action * fl_act,struct netlink_ext_ack * extack)75259080da0SBaowen Zheng nfp_act_remove_actions(struct nfp_app *app, struct flow_offload_action *fl_act,
75359080da0SBaowen Zheng 		       struct netlink_ext_ack *extack)
75459080da0SBaowen Zheng {
75526ff98d7SBaowen Zheng 	struct nfp_meter_entry *meter_entry = NULL;
75659080da0SBaowen Zheng 	struct nfp_police_config *config;
75759080da0SBaowen Zheng 	struct sk_buff *skb;
75859080da0SBaowen Zheng 	u32 meter_id;
75926ff98d7SBaowen Zheng 	bool pps;
76059080da0SBaowen Zheng 
7619bacb93bSWalter Heymans 	/* Delete qos associate data for this interface */
76259080da0SBaowen Zheng 	if (fl_act->id != FLOW_ACTION_POLICE) {
76359080da0SBaowen Zheng 		NL_SET_ERR_MSG_MOD(extack,
76459080da0SBaowen Zheng 				   "unsupported offload: qos rate limit offload requires police action");
76559080da0SBaowen Zheng 		return -EOPNOTSUPP;
76659080da0SBaowen Zheng 	}
76759080da0SBaowen Zheng 
76859080da0SBaowen Zheng 	meter_id = fl_act->index;
76926ff98d7SBaowen Zheng 	meter_entry = nfp_flower_search_meter_entry(app, meter_id);
77026ff98d7SBaowen Zheng 	if (!meter_entry) {
77126ff98d7SBaowen Zheng 		NL_SET_ERR_MSG_MOD(extack,
772323d51caSWan Jiabing 				   "no meter entry when delete the action index.");
77326ff98d7SBaowen Zheng 		return -ENOENT;
77426ff98d7SBaowen Zheng 	}
77526ff98d7SBaowen Zheng 	pps = !meter_entry->bps;
77626ff98d7SBaowen Zheng 
77759080da0SBaowen Zheng 	skb = nfp_flower_cmsg_alloc(app, sizeof(struct nfp_police_config),
77859080da0SBaowen Zheng 				    NFP_FLOWER_CMSG_TYPE_QOS_DEL, GFP_KERNEL);
77959080da0SBaowen Zheng 	if (!skb)
78059080da0SBaowen Zheng 		return -ENOMEM;
78159080da0SBaowen Zheng 
78259080da0SBaowen Zheng 	config = nfp_flower_cmsg_get_data(skb);
78359080da0SBaowen Zheng 	memset(config, 0, sizeof(struct nfp_police_config));
78459080da0SBaowen Zheng 	config->head.flags_opts = cpu_to_be32(NFP_FL_QOS_METER);
78559080da0SBaowen Zheng 	config->head.meter_id = cpu_to_be32(meter_id);
78626ff98d7SBaowen Zheng 	if (pps)
78726ff98d7SBaowen Zheng 		config->head.flags_opts |= cpu_to_be32(NFP_FL_QOS_PPS);
78826ff98d7SBaowen Zheng 
78959080da0SBaowen Zheng 	nfp_ctrl_tx(app->ctrl, skb);
79026ff98d7SBaowen Zheng 	nfp_flower_setup_meter_entry(app, NULL, NFP_METER_DEL, meter_id);
79159080da0SBaowen Zheng 
79259080da0SBaowen Zheng 	return 0;
79359080da0SBaowen Zheng }
79459080da0SBaowen Zheng 
795776178a5SBaowen Zheng void
nfp_act_stats_reply(struct nfp_app * app,void * pmsg)796776178a5SBaowen Zheng nfp_act_stats_reply(struct nfp_app *app, void *pmsg)
797776178a5SBaowen Zheng {
798776178a5SBaowen Zheng 	struct nfp_flower_priv *fl_priv = app->priv;
799776178a5SBaowen Zheng 	struct nfp_meter_entry *meter_entry = NULL;
800776178a5SBaowen Zheng 	struct nfp_police_stats_reply *msg = pmsg;
801776178a5SBaowen Zheng 	u32 meter_id;
802776178a5SBaowen Zheng 
803776178a5SBaowen Zheng 	meter_id = be32_to_cpu(msg->head.meter_id);
804776178a5SBaowen Zheng 	mutex_lock(&fl_priv->meter_stats_lock);
805776178a5SBaowen Zheng 
806776178a5SBaowen Zheng 	meter_entry = nfp_flower_search_meter_entry(app, meter_id);
807776178a5SBaowen Zheng 	if (!meter_entry)
808776178a5SBaowen Zheng 		goto exit_unlock;
809776178a5SBaowen Zheng 
810776178a5SBaowen Zheng 	meter_entry->stats.curr.pkts = be64_to_cpu(msg->pass_pkts) +
811776178a5SBaowen Zheng 				       be64_to_cpu(msg->drop_pkts);
812776178a5SBaowen Zheng 	meter_entry->stats.curr.bytes = be64_to_cpu(msg->pass_bytes) +
813776178a5SBaowen Zheng 					be64_to_cpu(msg->drop_bytes);
814776178a5SBaowen Zheng 	meter_entry->stats.curr.drops = be64_to_cpu(msg->drop_pkts);
815776178a5SBaowen Zheng 	if (!meter_entry->stats.update) {
816776178a5SBaowen Zheng 		meter_entry->stats.prev.pkts = meter_entry->stats.curr.pkts;
817776178a5SBaowen Zheng 		meter_entry->stats.prev.bytes = meter_entry->stats.curr.bytes;
818776178a5SBaowen Zheng 		meter_entry->stats.prev.drops = meter_entry->stats.curr.drops;
819776178a5SBaowen Zheng 	}
820776178a5SBaowen Zheng 
821776178a5SBaowen Zheng 	meter_entry->stats.update = jiffies;
822776178a5SBaowen Zheng 
823776178a5SBaowen Zheng exit_unlock:
824776178a5SBaowen Zheng 	mutex_unlock(&fl_priv->meter_stats_lock);
825776178a5SBaowen Zheng }
826776178a5SBaowen Zheng 
827776178a5SBaowen Zheng static int
nfp_act_stats_actions(struct nfp_app * app,struct flow_offload_action * fl_act,struct netlink_ext_ack * extack)828776178a5SBaowen Zheng nfp_act_stats_actions(struct nfp_app *app, struct flow_offload_action *fl_act,
829776178a5SBaowen Zheng 		      struct netlink_ext_ack *extack)
830776178a5SBaowen Zheng {
831776178a5SBaowen Zheng 	struct nfp_flower_priv *fl_priv = app->priv;
832776178a5SBaowen Zheng 	struct nfp_meter_entry *meter_entry = NULL;
833776178a5SBaowen Zheng 	u64 diff_bytes, diff_pkts, diff_drops;
834776178a5SBaowen Zheng 	int err = 0;
835776178a5SBaowen Zheng 
836776178a5SBaowen Zheng 	if (fl_act->id != FLOW_ACTION_POLICE) {
837776178a5SBaowen Zheng 		NL_SET_ERR_MSG_MOD(extack,
838776178a5SBaowen Zheng 				   "unsupported offload: qos rate limit offload requires police action");
839776178a5SBaowen Zheng 		return -EOPNOTSUPP;
840776178a5SBaowen Zheng 	}
841776178a5SBaowen Zheng 
842776178a5SBaowen Zheng 	mutex_lock(&fl_priv->meter_stats_lock);
843776178a5SBaowen Zheng 	meter_entry = nfp_flower_search_meter_entry(app, fl_act->index);
844776178a5SBaowen Zheng 	if (!meter_entry) {
845776178a5SBaowen Zheng 		err = -ENOENT;
846776178a5SBaowen Zheng 		goto exit_unlock;
847776178a5SBaowen Zheng 	}
848776178a5SBaowen Zheng 	diff_pkts = meter_entry->stats.curr.pkts > meter_entry->stats.prev.pkts ?
849776178a5SBaowen Zheng 		    meter_entry->stats.curr.pkts - meter_entry->stats.prev.pkts : 0;
850776178a5SBaowen Zheng 	diff_bytes = meter_entry->stats.curr.bytes > meter_entry->stats.prev.bytes ?
851776178a5SBaowen Zheng 		     meter_entry->stats.curr.bytes - meter_entry->stats.prev.bytes : 0;
852776178a5SBaowen Zheng 	diff_drops = meter_entry->stats.curr.drops > meter_entry->stats.prev.drops ?
853776178a5SBaowen Zheng 		     meter_entry->stats.curr.drops - meter_entry->stats.prev.drops : 0;
854776178a5SBaowen Zheng 
855776178a5SBaowen Zheng 	flow_stats_update(&fl_act->stats, diff_bytes, diff_pkts, diff_drops,
856776178a5SBaowen Zheng 			  meter_entry->stats.update,
857776178a5SBaowen Zheng 			  FLOW_ACTION_HW_STATS_DELAYED);
858776178a5SBaowen Zheng 
859776178a5SBaowen Zheng 	meter_entry->stats.prev.pkts = meter_entry->stats.curr.pkts;
860776178a5SBaowen Zheng 	meter_entry->stats.prev.bytes = meter_entry->stats.curr.bytes;
861776178a5SBaowen Zheng 	meter_entry->stats.prev.drops = meter_entry->stats.curr.drops;
862776178a5SBaowen Zheng 
863776178a5SBaowen Zheng exit_unlock:
864776178a5SBaowen Zheng 	mutex_unlock(&fl_priv->meter_stats_lock);
865776178a5SBaowen Zheng 	return err;
866776178a5SBaowen Zheng }
867776178a5SBaowen Zheng 
nfp_setup_tc_act_offload(struct nfp_app * app,struct flow_offload_action * fl_act)86859080da0SBaowen Zheng int nfp_setup_tc_act_offload(struct nfp_app *app,
86959080da0SBaowen Zheng 			     struct flow_offload_action *fl_act)
87059080da0SBaowen Zheng {
87159080da0SBaowen Zheng 	struct netlink_ext_ack *extack = fl_act->extack;
87259080da0SBaowen Zheng 	struct nfp_flower_priv *fl_priv = app->priv;
87359080da0SBaowen Zheng 
87459080da0SBaowen Zheng 	if (!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_METER))
87559080da0SBaowen Zheng 		return -EOPNOTSUPP;
87659080da0SBaowen Zheng 
87759080da0SBaowen Zheng 	switch (fl_act->command) {
87859080da0SBaowen Zheng 	case FLOW_ACT_REPLACE:
87959080da0SBaowen Zheng 		return nfp_act_install_actions(app, fl_act, extack);
88059080da0SBaowen Zheng 	case FLOW_ACT_DESTROY:
88159080da0SBaowen Zheng 		return nfp_act_remove_actions(app, fl_act, extack);
882776178a5SBaowen Zheng 	case FLOW_ACT_STATS:
883776178a5SBaowen Zheng 		return nfp_act_stats_actions(app, fl_act, extack);
88459080da0SBaowen Zheng 	default:
88559080da0SBaowen Zheng 		return -EOPNOTSUPP;
88659080da0SBaowen Zheng 	}
88759080da0SBaowen Zheng }
888