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