File Index
Files and directories
Sign in
FILE

qos_conf.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/netronome/nfp/flower/qos_conf.c C
  1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
  2/* Copyright (C) 2019 Netronome Systems, Inc. */
  3
  4#include <linux/hash.h>
  5#include <linux/hashtable.h>
  6#include <linux/jhash.h>
  7#include <linux/math64.h>
  8#include <linux/vmalloc.h>
  9#include <net/pkt_cls.h>
 10#include <net/pkt_sched.h>
 11
 12#include "cmsg.h"
 13#include "main.h"
 14#include "../nfp_port.h"
 15
 16#define NFP_FL_QOS_UPDATE		msecs_to_jiffies(1000)
 17#define NFP_FL_QOS_PPS  BIT(15)
 18#define NFP_FL_QOS_METER  BIT(10)
 19
 20struct nfp_police_cfg_head {
 21	__be32 flags_opts;
 22	union {
 23		__be32 meter_id;
 24		__be32 port;
 25	};
 26};
 27
 28enum NFP_FL_QOS_TYPES {
 29	NFP_FL_QOS_TYPE_BPS,
 30	NFP_FL_QOS_TYPE_PPS,
 31	NFP_FL_QOS_TYPE_MAX,
 32};
 33
 34/* Police cmsg for configuring a trTCM traffic conditioner (8W/32B)
 35 * See RFC 2698 for more details.
 36 * ----------------------------------------------------------------
 37 *    3                   2                   1
 38 *  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
 39 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 40 * |             Reserved          |p|         Reserved            |
 41 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 42 * |                          Port Ingress                         |
 43 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 44 * |                        Token Bucket Peak                      |
 45 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 46 * |                     Token Bucket Committed                    |
 47 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 48 * |                         Peak Burst Size                       |
 49 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 50 * |                      Committed Burst Size                     |
 51 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 52 * |                      Peak Information Rate                    |
 53 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 54 * |                    Committed Information Rate                 |
 55 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 56 * Word[0](FLag options):
 57 * [15] p(pps) 1 for pps, 0 for bps
 58 *
 59 * Meter control message
 60 *  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
 61 * +-------------------------------+-+---+-----+-+---------+-+---+-+
 62 * |            Reserved           |p| Y |TYPE |E|TSHFV    |P| PC|R|
 63 * +-------------------------------+-+---+-----+-+---------+-+---+-+
 64 * |                            meter ID                           |
 65 * +-------------------------------+-------------------------------+
 66 *
 67 */
 68struct nfp_police_config {
 69	struct nfp_police_cfg_head head;
 70	__be32 bkt_tkn_p;
 71	__be32 bkt_tkn_c;
 72	__be32 pbs;
 73	__be32 cbs;
 74	__be32 pir;
 75	__be32 cir;
 76};
 77
 78struct nfp_police_stats_reply {
 79	struct nfp_police_cfg_head head;
 80	__be64 pass_bytes;
 81	__be64 pass_pkts;
 82	__be64 drop_bytes;
 83	__be64 drop_pkts;
 84};
 85
 86int nfp_flower_offload_one_police(struct nfp_app *app, bool ingress,
 87				  bool pps, u32 id, u32 rate, u32 burst)
 88{
 89	struct nfp_police_config *config;
 90	struct sk_buff *skb;
 91
 92	skb = nfp_flower_cmsg_alloc(app, sizeof(struct nfp_police_config),
 93				    NFP_FLOWER_CMSG_TYPE_QOS_MOD, GFP_KERNEL);
 94	if (!skb)
 95		return -ENOMEM;
 96
 97	config = nfp_flower_cmsg_get_data(skb);
 98	memset(config, 0, sizeof(struct nfp_police_config));
 99	if (pps)
100		config->head.flags_opts |= cpu_to_be32(NFP_FL_QOS_PPS);
101	if (!ingress)
102		config->head.flags_opts |= cpu_to_be32(NFP_FL_QOS_METER);
103
104	if (ingress)
105		config->head.port = cpu_to_be32(id);
106	else
107		config->head.meter_id = cpu_to_be32(id);
108
109	config->bkt_tkn_p = cpu_to_be32(burst);
110	config->bkt_tkn_c = cpu_to_be32(burst);
111	config->pbs = cpu_to_be32(burst);
112	config->cbs = cpu_to_be32(burst);
113	config->pir = cpu_to_be32(rate);
114	config->cir = cpu_to_be32(rate);
115	nfp_ctrl_tx(app->ctrl, skb);
116
117	return 0;
118}
119
120static int nfp_policer_validate(const struct flow_action *action,
121				const struct flow_action_entry *act,
122				struct netlink_ext_ack *extack,
123				bool ingress)
124{
125	if (act->police.exceed.act_id != FLOW_ACTION_DROP) {
126		NL_SET_ERR_MSG_MOD(extack,
127				   "Offload not supported when exceed action is not drop");
128		return -EOPNOTSUPP;
129	}
130
131	if (ingress) {
132		if (act->police.notexceed.act_id != FLOW_ACTION_CONTINUE &&
133		    act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {
134			NL_SET_ERR_MSG_MOD(extack,
135					   "Offload not supported when conform action is not continue or ok");
136			return -EOPNOTSUPP;
137		}
138	} else {
139		if (act->police.notexceed.act_id != FLOW_ACTION_PIPE &&
140		    act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) {
141			NL_SET_ERR_MSG_MOD(extack,
142					   "Offload not supported when conform action is not pipe or ok");
143			return -EOPNOTSUPP;
144		}
145	}
146
147	if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT &&
148	    !flow_action_is_last_entry(action, act)) {
149		NL_SET_ERR_MSG_MOD(extack,
150				   "Offload not supported when conform action is ok, but action is not last");
151		return -EOPNOTSUPP;
152	}
153
154	if (act->police.peakrate_bytes_ps ||
155	    act->police.avrate || act->police.overhead) {
156		NL_SET_ERR_MSG_MOD(extack,
157				   "Offload not supported when peakrate/avrate/overhead is configured");
158		return -EOPNOTSUPP;
159	}
160
161	return 0;
162}
163
164static int
165nfp_flower_install_rate_limiter(struct nfp_app *app, struct net_device *netdev,
166				struct tc_cls_matchall_offload *flow,
167				struct netlink_ext_ack *extack)
168{
169	struct flow_action_entry *paction = &flow->rule->action.entries[0];
170	u32 action_num = flow->rule->action.num_entries;
171	struct nfp_flower_priv *fl_priv = app->priv;
172	struct flow_action_entry *action = NULL;
173	struct nfp_flower_repr_priv *repr_priv;
174	u32 netdev_port_id, i;
175	struct nfp_repr *repr;
176	bool pps_support;
177	u32 bps_num = 0;
178	u32 pps_num = 0;
179	u32 burst;
180	bool pps;
181	u64 rate;
182	int err;
183
184	if (!nfp_netdev_is_nfp_repr(netdev)) {
185		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload not supported on higher level port");
186		return -EOPNOTSUPP;
187	}
188	repr = netdev_priv(netdev);
189	repr_priv = repr->app_priv;
190	netdev_port_id = nfp_repr_get_port_id(netdev);
191	pps_support = !!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_PPS);
192
193	if (repr_priv->block_shared) {
194		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload not supported on shared blocks");
195		return -EOPNOTSUPP;
196	}
197
198	if (repr->port->type != NFP_PORT_VF_PORT) {
199		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload not supported on non-VF ports");
200		return -EOPNOTSUPP;
201	}
202
203	if (pps_support) {
204		if (action_num > 2 || action_num == 0) {
205			NL_SET_ERR_MSG_MOD(extack,
206					   "unsupported offload: qos rate limit offload only support action number 1 or 2");
207			return -EOPNOTSUPP;
208		}
209	} else {
210		if (!flow_offload_has_one_action(&flow->rule->action)) {
211			NL_SET_ERR_MSG_MOD(extack,
212					   "unsupported offload: qos rate limit offload requires a single action");
213			return -EOPNOTSUPP;
214		}
215	}
216
217	if (flow->common.prio != 1) {
218		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload requires highest priority");
219		return -EOPNOTSUPP;
220	}
221
222	for (i = 0 ; i < action_num; i++) {
223		action = paction + i;
224		if (action->id != FLOW_ACTION_POLICE) {
225			NL_SET_ERR_MSG_MOD(extack,
226					   "unsupported offload: qos rate limit offload requires police action");
227			return -EOPNOTSUPP;
228		}
229
230		err = nfp_policer_validate(&flow->rule->action, action, extack, true);
231		if (err)
232			return err;
233
234		if (action->police.rate_bytes_ps > 0) {
235			if (bps_num++) {
236				NL_SET_ERR_MSG_MOD(extack,
237						   "unsupported offload: qos rate limit offload only support one BPS action");
238				return -EOPNOTSUPP;
239			}
240		}
241		if (action->police.rate_pkt_ps > 0) {
242			if (!pps_support) {
243				NL_SET_ERR_MSG_MOD(extack,
244						   "unsupported offload: FW does not support PPS action");
245				return -EOPNOTSUPP;
246			}
247			if (pps_num++) {
248				NL_SET_ERR_MSG_MOD(extack,
249						   "unsupported offload: qos rate limit offload only support one PPS action");
250				return -EOPNOTSUPP;
251			}
252		}
253	}
254
255	for (i = 0 ; i < action_num; i++) {
256		/* Set QoS data for this interface */
257		action = paction + i;
258		if (action->police.rate_bytes_ps > 0) {
259			rate = action->police.rate_bytes_ps;
260			burst = action->police.burst;
261		} else if (action->police.rate_pkt_ps > 0) {
262			rate = action->police.rate_pkt_ps;
263			burst = action->police.burst_pkt;
264		} else {
265			NL_SET_ERR_MSG_MOD(extack,
266					   "unsupported offload: qos rate limit is not BPS or PPS");
267			continue;
268		}
269
270		if (rate != 0) {
271			pps = false;
272			if (action->police.rate_pkt_ps > 0)
273				pps = true;
274			nfp_flower_offload_one_police(repr->app, true,
275						      pps, netdev_port_id,
276						      rate, burst);
277		}
278	}
279	repr_priv->qos_table.netdev_port_id = netdev_port_id;
280	fl_priv->qos_rate_limiters++;
281	if (fl_priv->qos_rate_limiters == 1)
282		schedule_delayed_work(&fl_priv->qos_stats_work,
283				      NFP_FL_QOS_UPDATE);
284
285	return 0;
286}
287
288static int
289nfp_flower_remove_rate_limiter(struct nfp_app *app, struct net_device *netdev,
290			       struct tc_cls_matchall_offload *flow,
291			       struct netlink_ext_ack *extack)
292{
293	struct nfp_flower_priv *fl_priv = app->priv;
294	struct nfp_flower_repr_priv *repr_priv;
295	struct nfp_police_config *config;
296	u32 netdev_port_id, i;
297	struct nfp_repr *repr;
298	struct sk_buff *skb;
299	bool pps_support;
300
301	if (!nfp_netdev_is_nfp_repr(netdev)) {
302		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload not supported on higher level port");
303		return -EOPNOTSUPP;
304	}
305	repr = netdev_priv(netdev);
306
307	netdev_port_id = nfp_repr_get_port_id(netdev);
308	repr_priv = repr->app_priv;
309	pps_support = !!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_PPS);
310
311	if (!repr_priv->qos_table.netdev_port_id) {
312		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: cannot remove qos entry that does not exist");
313		return -EOPNOTSUPP;
314	}
315
316	memset(&repr_priv->qos_table, 0, sizeof(struct nfp_fl_qos));
317	fl_priv->qos_rate_limiters--;
318	if (!fl_priv->qos_rate_limiters)
319		cancel_delayed_work_sync(&fl_priv->qos_stats_work);
320	for (i = 0 ; i < NFP_FL_QOS_TYPE_MAX; i++) {
321		if (i == NFP_FL_QOS_TYPE_PPS && !pps_support)
322			break;
323		/* 0:bps 1:pps
324		 * Clear QoS data for this interface.
325		 * There is no need to check if a specific QOS_TYPE was
326		 * configured as the firmware handles clearing a QoS entry
327		 * safely, even if it wasn't explicitly added.
328		 */
329		skb = nfp_flower_cmsg_alloc(repr->app, sizeof(struct nfp_police_config),
330					    NFP_FLOWER_CMSG_TYPE_QOS_DEL, GFP_KERNEL);
331		if (!skb)
332			return -ENOMEM;
333
334		config = nfp_flower_cmsg_get_data(skb);
335		memset(config, 0, sizeof(struct nfp_police_config));
336		if (i == NFP_FL_QOS_TYPE_PPS)
337			config->head.flags_opts = cpu_to_be32(NFP_FL_QOS_PPS);
338		config->head.port = cpu_to_be32(netdev_port_id);
339		nfp_ctrl_tx(repr->app->ctrl, skb);
340	}
341
342	return 0;
343}
344
345void nfp_flower_stats_rlim_reply(struct nfp_app *app, struct sk_buff *skb)
346{
347	struct nfp_flower_priv *fl_priv = app->priv;
348	struct nfp_flower_repr_priv *repr_priv;
349	struct nfp_police_stats_reply *msg;
350	struct nfp_stat_pair *curr_stats;
351	struct nfp_stat_pair *prev_stats;
352	struct net_device *netdev;
353	struct nfp_repr *repr;
354	u32 netdev_port_id;
355
356	msg = nfp_flower_cmsg_get_data(skb);
357	if (be32_to_cpu(msg->head.flags_opts) & NFP_FL_QOS_METER)
358		return nfp_act_stats_reply(app, msg);
359
360	netdev_port_id = be32_to_cpu(msg->head.port);
361	rcu_read_lock();
362	netdev = nfp_app_dev_get(app, netdev_port_id, NULL);
363	if (!netdev)
364		goto exit_unlock_rcu;
365
366	repr = netdev_priv(netdev);
367	repr_priv = repr->app_priv;
368	curr_stats = &repr_priv->qos_table.curr_stats;
369	prev_stats = &repr_priv->qos_table.prev_stats;
370
371	spin_lock_bh(&fl_priv->qos_stats_lock);
372	curr_stats->pkts = be64_to_cpu(msg->pass_pkts) +
373			   be64_to_cpu(msg->drop_pkts);
374	curr_stats->bytes = be64_to_cpu(msg->pass_bytes) +
375			    be64_to_cpu(msg->drop_bytes);
376
377	if (!repr_priv->qos_table.last_update) {
378		prev_stats->pkts = curr_stats->pkts;
379		prev_stats->bytes = curr_stats->bytes;
380	}
381
382	repr_priv->qos_table.last_update = jiffies;
383	spin_unlock_bh(&fl_priv->qos_stats_lock);
384
385exit_unlock_rcu:
386	rcu_read_unlock();
387}
388
389static void
390nfp_flower_stats_rlim_request(struct nfp_flower_priv *fl_priv,
391			      u32 id, bool ingress)
392{
393	struct nfp_police_cfg_head *head;
394	struct sk_buff *skb;
395
396	skb = nfp_flower_cmsg_alloc(fl_priv->app,
397				    sizeof(struct nfp_police_cfg_head),
398				    NFP_FLOWER_CMSG_TYPE_QOS_STATS,
399				    GFP_ATOMIC);
400	if (!skb)
401		return;
402	head = nfp_flower_cmsg_get_data(skb);
403
404	memset(head, 0, sizeof(struct nfp_police_cfg_head));
405	if (ingress) {
406		head->port = cpu_to_be32(id);
407	} else {
408		head->flags_opts = cpu_to_be32(NFP_FL_QOS_METER);
409		head->meter_id = cpu_to_be32(id);
410	}
411
412	nfp_ctrl_tx(fl_priv->app->ctrl, skb);
413}
414
415static void
416nfp_flower_stats_rlim_request_all(struct nfp_flower_priv *fl_priv)
417{
418	struct nfp_reprs *repr_set;
419	int i;
420
421	rcu_read_lock();
422	repr_set = rcu_dereference(fl_priv->app->reprs[NFP_REPR_TYPE_VF]);
423	if (!repr_set)
424		goto exit_unlock_rcu;
425
426	for (i = 0; i < repr_set->num_reprs; i++) {
427		struct net_device *netdev;
428
429		netdev = rcu_dereference(repr_set->reprs[i]);
430		if (netdev) {
431			struct nfp_repr *priv = netdev_priv(netdev);
432			struct nfp_flower_repr_priv *repr_priv;
433			u32 netdev_port_id;
434
435			repr_priv = priv->app_priv;
436			netdev_port_id = repr_priv->qos_table.netdev_port_id;
437			if (!netdev_port_id)
438				continue;
439
440			nfp_flower_stats_rlim_request(fl_priv,
441						      netdev_port_id, true);
442		}
443	}
444
445exit_unlock_rcu:
446	rcu_read_unlock();
447}
448
449static void update_stats_cache(struct work_struct *work)
450{
451	struct delayed_work *delayed_work;
452	struct nfp_flower_priv *fl_priv;
453
454	delayed_work = to_delayed_work(work);
455	fl_priv = container_of(delayed_work, struct nfp_flower_priv,
456			       qos_stats_work);
457
458	nfp_flower_stats_rlim_request_all(fl_priv);
459	nfp_flower_stats_meter_request_all(fl_priv);
460
461	schedule_delayed_work(&fl_priv->qos_stats_work, NFP_FL_QOS_UPDATE);
462}
463
464static int
465nfp_flower_stats_rate_limiter(struct nfp_app *app, struct net_device *netdev,
466			      struct tc_cls_matchall_offload *flow,
467			      struct netlink_ext_ack *extack)
468{
469	struct nfp_flower_priv *fl_priv = app->priv;
470	struct nfp_flower_repr_priv *repr_priv;
471	struct nfp_stat_pair *curr_stats;
472	struct nfp_stat_pair *prev_stats;
473	u64 diff_bytes, diff_pkts;
474	struct nfp_repr *repr;
475
476	if (!nfp_netdev_is_nfp_repr(netdev)) {
477		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: qos rate limit offload not supported on higher level port");
478		return -EOPNOTSUPP;
479	}
480	repr = netdev_priv(netdev);
481
482	repr_priv = repr->app_priv;
483	if (!repr_priv->qos_table.netdev_port_id) {
484		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: cannot find qos entry for stats update");
485		return -EOPNOTSUPP;
486	}
487
488	spin_lock_bh(&fl_priv->qos_stats_lock);
489	curr_stats = &repr_priv->qos_table.curr_stats;
490	prev_stats = &repr_priv->qos_table.prev_stats;
491	diff_pkts = curr_stats->pkts - prev_stats->pkts;
492	diff_bytes = curr_stats->bytes - prev_stats->bytes;
493	prev_stats->pkts = curr_stats->pkts;
494	prev_stats->bytes = curr_stats->bytes;
495	spin_unlock_bh(&fl_priv->qos_stats_lock);
496
497	flow_stats_update(&flow->stats, diff_bytes, diff_pkts, 0,
498			  repr_priv->qos_table.last_update,
499			  FLOW_ACTION_HW_STATS_DELAYED);
500	return 0;
501}
502
503void nfp_flower_qos_init(struct nfp_app *app)
504{
505	struct nfp_flower_priv *fl_priv = app->priv;
506
507	spin_lock_init(&fl_priv->qos_stats_lock);
508	mutex_init(&fl_priv->meter_stats_lock);
509	nfp_init_meter_table(app);
510
511	INIT_DELAYED_WORK(&fl_priv->qos_stats_work, &update_stats_cache);
512}
513
514void nfp_flower_qos_cleanup(struct nfp_app *app)
515{
516	struct nfp_flower_priv *fl_priv = app->priv;
517
518	cancel_delayed_work_sync(&fl_priv->qos_stats_work);
519}
520
521int nfp_flower_setup_qos_offload(struct nfp_app *app, struct net_device *netdev,
522				 struct tc_cls_matchall_offload *flow)
523{
524	struct netlink_ext_ack *extack = flow->common.extack;
525	struct nfp_flower_priv *fl_priv = app->priv;
526	int ret;
527
528	if (!(fl_priv->flower_ext_feats & NFP_FL_FEATS_VF_RLIM)) {
529		NL_SET_ERR_MSG_MOD(extack, "unsupported offload: loaded firmware does not support qos rate limit offload");
530		return -EOPNOTSUPP;
531	}
532
533	mutex_lock(&fl_priv->nfp_fl_lock);
534	switch (flow->command) {
535	case TC_CLSMATCHALL_REPLACE:
536		ret = nfp_flower_install_rate_limiter(app, netdev, flow, extack);
537		break;
538	case TC_CLSMATCHALL_DESTROY:
539		ret = nfp_flower_remove_rate_limiter(app, netdev, flow, extack);
540		break;
541	case TC_CLSMATCHALL_STATS:
542		ret = nfp_flower_stats_rate_limiter(app, netdev, flow, extack);
543		break;
544	default:
545		ret = -EOPNOTSUPP;
546		break;
547	}
548	mutex_unlock(&fl_priv->nfp_fl_lock);
549
550	return ret;
551}
552
553/* Offload tc action, currently only for tc police */
554
555static const struct rhashtable_params stats_meter_table_params = {
556	.key_offset	= offsetof(struct nfp_meter_entry, meter_id),
557	.head_offset	= offsetof(struct nfp_meter_entry, ht_node),
558	.key_len	= sizeof(u32),
559};
560
561struct nfp_meter_entry *
562nfp_flower_search_meter_entry(struct nfp_app *app, u32 meter_id)
563{
564	struct nfp_flower_priv *priv = app->priv;
565
566	return rhashtable_lookup_fast(&priv->meter_table, &meter_id,
567				      stats_meter_table_params);
568}
569
570static struct nfp_meter_entry *
571nfp_flower_add_meter_entry(struct nfp_app *app, u32 meter_id)
572{
573	struct nfp_meter_entry *meter_entry = NULL;
574	struct nfp_flower_priv *priv = app->priv;
575
576	meter_entry = rhashtable_lookup_fast(&priv->meter_table,
577					     &meter_id,
578					     stats_meter_table_params);
579	if (meter_entry)
580		return meter_entry;
581
582	meter_entry = kzalloc(sizeof(*meter_entry), GFP_KERNEL);
583	if (!meter_entry)
584		return NULL;
585
586	meter_entry->meter_id = meter_id;
587	meter_entry->used = jiffies;
588	if (rhashtable_insert_fast(&priv->meter_table, &meter_entry->ht_node,
589				   stats_meter_table_params)) {
590		kfree(meter_entry);
591		return NULL;
592	}
593
594	priv->qos_rate_limiters++;
595	if (priv->qos_rate_limiters == 1)
596		schedule_delayed_work(&priv->qos_stats_work,
597				      NFP_FL_QOS_UPDATE);
598
599	return meter_entry;
600}
601
602static void nfp_flower_del_meter_entry(struct nfp_app *app, u32 meter_id)
603{
604	struct nfp_meter_entry *meter_entry = NULL;
605	struct nfp_flower_priv *priv = app->priv;
606
607	meter_entry = rhashtable_lookup_fast(&priv->meter_table, &meter_id,
608					     stats_meter_table_params);
609	if (!meter_entry)
610		return;
611
612	rhashtable_remove_fast(&priv->meter_table,
613			       &meter_entry->ht_node,
614			       stats_meter_table_params);
615	kfree(meter_entry);
616	priv->qos_rate_limiters--;
617	if (!priv->qos_rate_limiters)
618		cancel_delayed_work_sync(&priv->qos_stats_work);
619}
620
621int nfp_flower_setup_meter_entry(struct nfp_app *app,
622				 const struct flow_action_entry *action,
623				 enum nfp_meter_op op,
624				 u32 meter_id)
625{
626	struct nfp_flower_priv *fl_priv = app->priv;
627	struct nfp_meter_entry *meter_entry = NULL;
628	int err = 0;
629
630	mutex_lock(&fl_priv->meter_stats_lock);
631
632	switch (op) {
633	case NFP_METER_DEL:
634		nfp_flower_del_meter_entry(app, meter_id);
635		goto exit_unlock;
636	case NFP_METER_ADD:
637		meter_entry = nfp_flower_add_meter_entry(app, meter_id);
638		break;
639	default:
640		err = -EOPNOTSUPP;
641		goto exit_unlock;
642	}
643
644	if (!meter_entry) {
645		err = -ENOMEM;
646		goto exit_unlock;
647	}
648
649	if (action->police.rate_bytes_ps > 0) {
650		meter_entry->bps = true;
651		meter_entry->rate = action->police.rate_bytes_ps;
652		meter_entry->burst = action->police.burst;
653	} else {
654		meter_entry->bps = false;
655		meter_entry->rate = action->police.rate_pkt_ps;
656		meter_entry->burst = action->police.burst_pkt;
657	}
658
659exit_unlock:
660	mutex_unlock(&fl_priv->meter_stats_lock);
661	return err;
662}
663
664int nfp_init_meter_table(struct nfp_app *app)
665{
666	struct nfp_flower_priv *priv = app->priv;
667
668	return rhashtable_init(&priv->meter_table, &stats_meter_table_params);
669}
670
671void
672nfp_flower_stats_meter_request_all(struct nfp_flower_priv *fl_priv)
673{
674	struct nfp_meter_entry *meter_entry = NULL;
675	struct rhashtable_iter iter;
676
677	mutex_lock(&fl_priv->meter_stats_lock);
678	rhashtable_walk_enter(&fl_priv->meter_table, &iter);
679	rhashtable_walk_start(&iter);
680
681	while ((meter_entry = rhashtable_walk_next(&iter)) != NULL) {
682		if (IS_ERR(meter_entry))
683			continue;
684		nfp_flower_stats_rlim_request(fl_priv,
685					      meter_entry->meter_id, false);
686	}
687
688	rhashtable_walk_stop(&iter);
689	rhashtable_walk_exit(&iter);
690	mutex_unlock(&fl_priv->meter_stats_lock);
691}
692
693static int
694nfp_act_install_actions(struct nfp_app *app, struct flow_offload_action *fl_act,
695			struct netlink_ext_ack *extack)
696{
697	struct flow_action_entry *paction = &fl_act->action.entries[0];
698	u32 action_num = fl_act->action.num_entries;
699	struct nfp_flower_priv *fl_priv = app->priv;
700	struct flow_action_entry *action = NULL;
701	u32 burst, i, meter_id;
702	bool pps_support, pps;
703	bool add = false;
704	u64 rate;
705	int err;
706
707	pps_support = !!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_PPS);
708
709	for (i = 0 ; i < action_num; i++) {
710		/* Set qos associate data for this interface */
711		action = paction + i;
712		if (action->id != FLOW_ACTION_POLICE) {
713			NL_SET_ERR_MSG_MOD(extack,
714					   "unsupported offload: qos rate limit offload requires police action");
715			continue;
716		}
717
718		err = nfp_policer_validate(&fl_act->action, action, extack, false);
719		if (err)
720			return err;
721
722		if (action->police.rate_bytes_ps > 0) {
723			rate = action->police.rate_bytes_ps;
724			burst = action->police.burst;
725		} else if (action->police.rate_pkt_ps > 0 && pps_support) {
726			rate = action->police.rate_pkt_ps;
727			burst = action->police.burst_pkt;
728		} else {
729			NL_SET_ERR_MSG_MOD(extack,
730					   "unsupported offload: unsupported qos rate limit");
731			continue;
732		}
733
734		if (rate != 0) {
735			meter_id = action->hw_index;
736			if (nfp_flower_setup_meter_entry(app, action, NFP_METER_ADD, meter_id))
737				continue;
738
739			pps = false;
740			if (action->police.rate_pkt_ps > 0)
741				pps = true;
742			nfp_flower_offload_one_police(app, false, pps, meter_id,
743						      rate, burst);
744			add = true;
745		}
746	}
747
748	return add ? 0 : -EOPNOTSUPP;
749}
750
751static int
752nfp_act_remove_actions(struct nfp_app *app, struct flow_offload_action *fl_act,
753		       struct netlink_ext_ack *extack)
754{
755	struct nfp_meter_entry *meter_entry = NULL;
756	struct nfp_police_config *config;
757	struct sk_buff *skb;
758	u32 meter_id;
759	bool pps;
760
761	/* Delete qos associate data for this interface */
762	if (fl_act->id != FLOW_ACTION_POLICE) {
763		NL_SET_ERR_MSG_MOD(extack,
764				   "unsupported offload: qos rate limit offload requires police action");
765		return -EOPNOTSUPP;
766	}
767
768	meter_id = fl_act->index;
769	meter_entry = nfp_flower_search_meter_entry(app, meter_id);
770	if (!meter_entry) {
771		NL_SET_ERR_MSG_MOD(extack,
772				   "no meter entry when delete the action index.");
773		return -ENOENT;
774	}
775	pps = !meter_entry->bps;
776
777	skb = nfp_flower_cmsg_alloc(app, sizeof(struct nfp_police_config),
778				    NFP_FLOWER_CMSG_TYPE_QOS_DEL, GFP_KERNEL);
779	if (!skb)
780		return -ENOMEM;
781
782	config = nfp_flower_cmsg_get_data(skb);
783	memset(config, 0, sizeof(struct nfp_police_config));
784	config->head.flags_opts = cpu_to_be32(NFP_FL_QOS_METER);
785	config->head.meter_id = cpu_to_be32(meter_id);
786	if (pps)
787		config->head.flags_opts |= cpu_to_be32(NFP_FL_QOS_PPS);
788
789	nfp_ctrl_tx(app->ctrl, skb);
790	nfp_flower_setup_meter_entry(app, NULL, NFP_METER_DEL, meter_id);
791
792	return 0;
793}
794
795void
796nfp_act_stats_reply(struct nfp_app *app, void *pmsg)
797{
798	struct nfp_flower_priv *fl_priv = app->priv;
799	struct nfp_meter_entry *meter_entry = NULL;
800	struct nfp_police_stats_reply *msg = pmsg;
801	u32 meter_id;
802
803	meter_id = be32_to_cpu(msg->head.meter_id);
804	mutex_lock(&fl_priv->meter_stats_lock);
805
806	meter_entry = nfp_flower_search_meter_entry(app, meter_id);
807	if (!meter_entry)
808		goto exit_unlock;
809
810	meter_entry->stats.curr.pkts = be64_to_cpu(msg->pass_pkts) +
811				       be64_to_cpu(msg->drop_pkts);
812	meter_entry->stats.curr.bytes = be64_to_cpu(msg->pass_bytes) +
813					be64_to_cpu(msg->drop_bytes);
814	meter_entry->stats.curr.drops = be64_to_cpu(msg->drop_pkts);
815	if (!meter_entry->stats.update) {
816		meter_entry->stats.prev.pkts = meter_entry->stats.curr.pkts;
817		meter_entry->stats.prev.bytes = meter_entry->stats.curr.bytes;
818		meter_entry->stats.prev.drops = meter_entry->stats.curr.drops;
819	}
820
821	meter_entry->stats.update = jiffies;
822
823exit_unlock:
824	mutex_unlock(&fl_priv->meter_stats_lock);
825}
826
827static int
828nfp_act_stats_actions(struct nfp_app *app, struct flow_offload_action *fl_act,
829		      struct netlink_ext_ack *extack)
830{
831	struct nfp_flower_priv *fl_priv = app->priv;
832	struct nfp_meter_entry *meter_entry = NULL;
833	u64 diff_bytes, diff_pkts, diff_drops;
834	int err = 0;
835
836	if (fl_act->id != FLOW_ACTION_POLICE) {
837		NL_SET_ERR_MSG_MOD(extack,
838				   "unsupported offload: qos rate limit offload requires police action");
839		return -EOPNOTSUPP;
840	}
841
842	mutex_lock(&fl_priv->meter_stats_lock);
843	meter_entry = nfp_flower_search_meter_entry(app, fl_act->index);
844	if (!meter_entry) {
845		err = -ENOENT;
846		goto exit_unlock;
847	}
848	diff_pkts = meter_entry->stats.curr.pkts > meter_entry->stats.prev.pkts ?
849		    meter_entry->stats.curr.pkts - meter_entry->stats.prev.pkts : 0;
850	diff_bytes = meter_entry->stats.curr.bytes > meter_entry->stats.prev.bytes ?
851		     meter_entry->stats.curr.bytes - meter_entry->stats.prev.bytes : 0;
852	diff_drops = meter_entry->stats.curr.drops > meter_entry->stats.prev.drops ?
853		     meter_entry->stats.curr.drops - meter_entry->stats.prev.drops : 0;
854
855	flow_stats_update(&fl_act->stats, diff_bytes, diff_pkts, diff_drops,
856			  meter_entry->stats.update,
857			  FLOW_ACTION_HW_STATS_DELAYED);
858
859	meter_entry->stats.prev.pkts = meter_entry->stats.curr.pkts;
860	meter_entry->stats.prev.bytes = meter_entry->stats.curr.bytes;
861	meter_entry->stats.prev.drops = meter_entry->stats.curr.drops;
862
863exit_unlock:
864	mutex_unlock(&fl_priv->meter_stats_lock);
865	return err;
866}
867
868int nfp_setup_tc_act_offload(struct nfp_app *app,
869			     struct flow_offload_action *fl_act)
870{
871	struct netlink_ext_ack *extack = fl_act->extack;
872	struct nfp_flower_priv *fl_priv = app->priv;
873
874	if (!(fl_priv->flower_ext_feats & NFP_FL_FEATS_QOS_METER))
875		return -EOPNOTSUPP;
876
877	switch (fl_act->command) {
878	case FLOW_ACT_REPLACE:
879		return nfp_act_install_actions(app, fl_act, extack);
880	case FLOW_ACT_DESTROY:
881		return nfp_act_remove_actions(app, fl_act, extack);
882	case FLOW_ACT_STATS:
883		return nfp_act_stats_actions(app, fl_act, extack);
884	default:
885		return -EOPNOTSUPP;
886	}
887}
~/ Read-only file preview.