xref: /linux/drivers/net/ethernet/netronome/nfp/abm/cls.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
1174ab544SJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2174ab544SJakub Kicinski /* Copyright (C) 2018 Netronome Systems, Inc. */
3174ab544SJakub Kicinski 
4174ab544SJakub Kicinski #include <linux/bitfield.h>
5174ab544SJakub Kicinski #include <net/pkt_cls.h>
6174ab544SJakub Kicinski 
7174ab544SJakub Kicinski #include "../nfpcore/nfp_cpp.h"
8174ab544SJakub Kicinski #include "../nfp_app.h"
9174ab544SJakub Kicinski #include "../nfp_net_repr.h"
10174ab544SJakub Kicinski #include "main.h"
11174ab544SJakub Kicinski 
12174ab544SJakub Kicinski struct nfp_abm_u32_match {
13174ab544SJakub Kicinski 	u32 handle;
14174ab544SJakub Kicinski 	u32 band;
15174ab544SJakub Kicinski 	u8 mask;
16174ab544SJakub Kicinski 	u8 val;
17174ab544SJakub Kicinski 	struct list_head list;
18174ab544SJakub Kicinski };
19174ab544SJakub Kicinski 
20174ab544SJakub Kicinski static bool
nfp_abm_u32_check_knode(struct nfp_abm * abm,struct tc_cls_u32_knode * knode,__be16 proto,struct netlink_ext_ack * extack)21174ab544SJakub Kicinski nfp_abm_u32_check_knode(struct nfp_abm *abm, struct tc_cls_u32_knode *knode,
22174ab544SJakub Kicinski 			__be16 proto, struct netlink_ext_ack *extack)
23174ab544SJakub Kicinski {
24174ab544SJakub Kicinski 	struct tc_u32_key *k;
25174ab544SJakub Kicinski 	unsigned int tos_off;
26174ab544SJakub Kicinski 
27174ab544SJakub Kicinski 	if (knode->exts && tcf_exts_has_actions(knode->exts)) {
28174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "action offload not supported");
29174ab544SJakub Kicinski 		return false;
30174ab544SJakub Kicinski 	}
31174ab544SJakub Kicinski 	if (knode->link_handle) {
32174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "linking not supported");
33174ab544SJakub Kicinski 		return false;
34174ab544SJakub Kicinski 	}
35174ab544SJakub Kicinski 	if (knode->sel->flags != TC_U32_TERMINAL) {
36174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack,
37174ab544SJakub Kicinski 				   "flags must be equal to TC_U32_TERMINAL");
38174ab544SJakub Kicinski 		return false;
39174ab544SJakub Kicinski 	}
40174ab544SJakub Kicinski 	if (knode->sel->off || knode->sel->offshift || knode->sel->offmask ||
41174ab544SJakub Kicinski 	    knode->sel->offoff || knode->fshift) {
42d003d772SColin Ian King 		NL_SET_ERR_MSG_MOD(extack, "variable offsetting not supported");
43174ab544SJakub Kicinski 		return false;
44174ab544SJakub Kicinski 	}
45174ab544SJakub Kicinski 	if (knode->sel->hoff || knode->sel->hmask) {
46174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "hashing not supported");
47174ab544SJakub Kicinski 		return false;
48174ab544SJakub Kicinski 	}
49174ab544SJakub Kicinski 	if (knode->val || knode->mask) {
50174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "matching on mark not supported");
51174ab544SJakub Kicinski 		return false;
52174ab544SJakub Kicinski 	}
53174ab544SJakub Kicinski 	if (knode->res && knode->res->class) {
54174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "setting non-0 class not supported");
55174ab544SJakub Kicinski 		return false;
56174ab544SJakub Kicinski 	}
57174ab544SJakub Kicinski 	if (knode->res && knode->res->classid >= abm->num_bands) {
58174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack,
59174ab544SJakub Kicinski 				   "classid higher than number of bands");
60174ab544SJakub Kicinski 		return false;
61174ab544SJakub Kicinski 	}
62174ab544SJakub Kicinski 	if (knode->sel->nkeys != 1) {
63174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "exactly one key required");
64174ab544SJakub Kicinski 		return false;
65174ab544SJakub Kicinski 	}
66174ab544SJakub Kicinski 
67174ab544SJakub Kicinski 	switch (proto) {
68174ab544SJakub Kicinski 	case htons(ETH_P_IP):
69174ab544SJakub Kicinski 		tos_off = 16;
70174ab544SJakub Kicinski 		break;
71174ab544SJakub Kicinski 	case htons(ETH_P_IPV6):
72174ab544SJakub Kicinski 		tos_off = 20;
73174ab544SJakub Kicinski 		break;
74174ab544SJakub Kicinski 	default:
75174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "only IP and IPv6 supported as filter protocol");
76174ab544SJakub Kicinski 		return false;
77174ab544SJakub Kicinski 	}
78174ab544SJakub Kicinski 
79174ab544SJakub Kicinski 	k = &knode->sel->keys[0];
80174ab544SJakub Kicinski 	if (k->offmask) {
81d003d772SColin Ian King 		NL_SET_ERR_MSG_MOD(extack, "offset mask - variable offsetting not supported");
82174ab544SJakub Kicinski 		return false;
83174ab544SJakub Kicinski 	}
84174ab544SJakub Kicinski 	if (k->off) {
85174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "only DSCP fields can be matched");
86174ab544SJakub Kicinski 		return false;
87174ab544SJakub Kicinski 	}
88174ab544SJakub Kicinski 	if (k->val & ~k->mask) {
89174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "mask does not cover the key");
90174ab544SJakub Kicinski 		return false;
91174ab544SJakub Kicinski 	}
92174ab544SJakub Kicinski 	if (be32_to_cpu(k->mask) >> tos_off & ~abm->dscp_mask) {
93174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(extack, "only high DSCP class selector bits can be used");
94174ab544SJakub Kicinski 		nfp_err(abm->app->cpp,
95174ab544SJakub Kicinski 			"u32 offload: requested mask %x FW can support only %x\n",
96174ab544SJakub Kicinski 			be32_to_cpu(k->mask) >> tos_off, abm->dscp_mask);
97174ab544SJakub Kicinski 		return false;
98174ab544SJakub Kicinski 	}
99174ab544SJakub Kicinski 
100174ab544SJakub Kicinski 	return true;
101174ab544SJakub Kicinski }
102174ab544SJakub Kicinski 
103174ab544SJakub Kicinski /* This filter list -> map conversion is O(n * m), we expect single digit or
104174ab544SJakub Kicinski  * low double digit number of prios and likewise for the filters.  Also u32
105174ab544SJakub Kicinski  * doesn't report stats, so it's really only setup time cost.
106174ab544SJakub Kicinski  */
107174ab544SJakub Kicinski static unsigned int
nfp_abm_find_band_for_prio(struct nfp_abm_link * alink,unsigned int prio)108174ab544SJakub Kicinski nfp_abm_find_band_for_prio(struct nfp_abm_link *alink, unsigned int prio)
109174ab544SJakub Kicinski {
110174ab544SJakub Kicinski 	struct nfp_abm_u32_match *iter;
111174ab544SJakub Kicinski 
112174ab544SJakub Kicinski 	list_for_each_entry(iter, &alink->dscp_map, list)
113174ab544SJakub Kicinski 		if ((prio & iter->mask) == iter->val)
114174ab544SJakub Kicinski 			return iter->band;
115174ab544SJakub Kicinski 
116174ab544SJakub Kicinski 	return alink->def_band;
117174ab544SJakub Kicinski }
118174ab544SJakub Kicinski 
nfp_abm_update_band_map(struct nfp_abm_link * alink)119174ab544SJakub Kicinski static int nfp_abm_update_band_map(struct nfp_abm_link *alink)
120174ab544SJakub Kicinski {
121174ab544SJakub Kicinski 	unsigned int i, bits_per_prio, prios_per_word, base_shift;
122174ab544SJakub Kicinski 	struct nfp_abm *abm = alink->abm;
123174ab544SJakub Kicinski 	u32 field_mask;
124174ab544SJakub Kicinski 
125174ab544SJakub Kicinski 	alink->has_prio = !list_empty(&alink->dscp_map);
126174ab544SJakub Kicinski 
127174ab544SJakub Kicinski 	bits_per_prio = roundup_pow_of_two(order_base_2(abm->num_bands));
128174ab544SJakub Kicinski 	field_mask = (1 << bits_per_prio) - 1;
129174ab544SJakub Kicinski 	prios_per_word = sizeof(u32) * BITS_PER_BYTE / bits_per_prio;
130174ab544SJakub Kicinski 
131174ab544SJakub Kicinski 	/* FW mask applies from top bits */
132174ab544SJakub Kicinski 	base_shift = 8 - order_base_2(abm->num_prios);
133174ab544SJakub Kicinski 
134174ab544SJakub Kicinski 	for (i = 0; i < abm->num_prios; i++) {
135174ab544SJakub Kicinski 		unsigned int offset;
136174ab544SJakub Kicinski 		u32 *word;
137174ab544SJakub Kicinski 		u8 band;
138174ab544SJakub Kicinski 
139174ab544SJakub Kicinski 		word = &alink->prio_map[i / prios_per_word];
140174ab544SJakub Kicinski 		offset = (i % prios_per_word) * bits_per_prio;
141174ab544SJakub Kicinski 
142174ab544SJakub Kicinski 		band = nfp_abm_find_band_for_prio(alink, i << base_shift);
143174ab544SJakub Kicinski 
144174ab544SJakub Kicinski 		*word &= ~(field_mask << offset);
145174ab544SJakub Kicinski 		*word |= band << offset;
146174ab544SJakub Kicinski 	}
147174ab544SJakub Kicinski 
148174ab544SJakub Kicinski 	/* Qdisc offload status may change if has_prio changed */
149174ab544SJakub Kicinski 	nfp_abm_qdisc_offload_update(alink);
150174ab544SJakub Kicinski 
151174ab544SJakub Kicinski 	return nfp_abm_ctrl_prio_map_update(alink, alink->prio_map);
152174ab544SJakub Kicinski }
153174ab544SJakub Kicinski 
154174ab544SJakub Kicinski static void
nfp_abm_u32_knode_delete(struct nfp_abm_link * alink,struct tc_cls_u32_knode * knode)155174ab544SJakub Kicinski nfp_abm_u32_knode_delete(struct nfp_abm_link *alink,
156174ab544SJakub Kicinski 			 struct tc_cls_u32_knode *knode)
157174ab544SJakub Kicinski {
158174ab544SJakub Kicinski 	struct nfp_abm_u32_match *iter;
159174ab544SJakub Kicinski 
160174ab544SJakub Kicinski 	list_for_each_entry(iter, &alink->dscp_map, list)
161174ab544SJakub Kicinski 		if (iter->handle == knode->handle) {
162174ab544SJakub Kicinski 			list_del(&iter->list);
163174ab544SJakub Kicinski 			kfree(iter);
164174ab544SJakub Kicinski 			nfp_abm_update_band_map(alink);
165174ab544SJakub Kicinski 			return;
166174ab544SJakub Kicinski 		}
167174ab544SJakub Kicinski }
168174ab544SJakub Kicinski 
169174ab544SJakub Kicinski static int
nfp_abm_u32_knode_replace(struct nfp_abm_link * alink,struct tc_cls_u32_knode * knode,__be16 proto,struct netlink_ext_ack * extack)170174ab544SJakub Kicinski nfp_abm_u32_knode_replace(struct nfp_abm_link *alink,
171174ab544SJakub Kicinski 			  struct tc_cls_u32_knode *knode,
172174ab544SJakub Kicinski 			  __be16 proto, struct netlink_ext_ack *extack)
173174ab544SJakub Kicinski {
174174ab544SJakub Kicinski 	struct nfp_abm_u32_match *match = NULL, *iter;
175174ab544SJakub Kicinski 	unsigned int tos_off;
176174ab544SJakub Kicinski 	u8 mask, val;
177174ab544SJakub Kicinski 	int err;
178174ab544SJakub Kicinski 
179*1d1997dbSJakub Kicinski 	if (!nfp_abm_u32_check_knode(alink->abm, knode, proto, extack))
180174ab544SJakub Kicinski 		goto err_delete;
181174ab544SJakub Kicinski 
182174ab544SJakub Kicinski 	tos_off = proto == htons(ETH_P_IP) ? 16 : 20;
183174ab544SJakub Kicinski 
184174ab544SJakub Kicinski 	/* Extract the DSCP Class Selector bits */
185174ab544SJakub Kicinski 	val = be32_to_cpu(knode->sel->keys[0].val) >> tos_off & 0xff;
186174ab544SJakub Kicinski 	mask = be32_to_cpu(knode->sel->keys[0].mask) >> tos_off & 0xff;
187174ab544SJakub Kicinski 
188174ab544SJakub Kicinski 	/* Check if there is no conflicting mapping and find match by handle */
189174ab544SJakub Kicinski 	list_for_each_entry(iter, &alink->dscp_map, list) {
190174ab544SJakub Kicinski 		u32 cmask;
191174ab544SJakub Kicinski 
192174ab544SJakub Kicinski 		if (iter->handle == knode->handle) {
193174ab544SJakub Kicinski 			match = iter;
194174ab544SJakub Kicinski 			continue;
195174ab544SJakub Kicinski 		}
196174ab544SJakub Kicinski 
197174ab544SJakub Kicinski 		cmask = iter->mask & mask;
198174ab544SJakub Kicinski 		if ((iter->val & cmask) == (val & cmask) &&
199174ab544SJakub Kicinski 		    iter->band != knode->res->classid) {
200174ab544SJakub Kicinski 			NL_SET_ERR_MSG_MOD(extack, "conflict with already offloaded filter");
201174ab544SJakub Kicinski 			goto err_delete;
202174ab544SJakub Kicinski 		}
203174ab544SJakub Kicinski 	}
204174ab544SJakub Kicinski 
205174ab544SJakub Kicinski 	if (!match) {
206174ab544SJakub Kicinski 		match = kzalloc(sizeof(*match), GFP_KERNEL);
207*1d1997dbSJakub Kicinski 		if (!match)
208*1d1997dbSJakub Kicinski 			return -ENOMEM;
209174ab544SJakub Kicinski 		list_add(&match->list, &alink->dscp_map);
210174ab544SJakub Kicinski 	}
211174ab544SJakub Kicinski 	match->handle = knode->handle;
212174ab544SJakub Kicinski 	match->band = knode->res->classid;
213174ab544SJakub Kicinski 	match->mask = mask;
214174ab544SJakub Kicinski 	match->val = val;
215174ab544SJakub Kicinski 
216174ab544SJakub Kicinski 	err = nfp_abm_update_band_map(alink);
217174ab544SJakub Kicinski 	if (err)
218174ab544SJakub Kicinski 		goto err_delete;
219174ab544SJakub Kicinski 
220174ab544SJakub Kicinski 	return 0;
221174ab544SJakub Kicinski 
222174ab544SJakub Kicinski err_delete:
223174ab544SJakub Kicinski 	nfp_abm_u32_knode_delete(alink, knode);
224*1d1997dbSJakub Kicinski 	return -EOPNOTSUPP;
225174ab544SJakub Kicinski }
226174ab544SJakub Kicinski 
nfp_abm_setup_tc_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv)227174ab544SJakub Kicinski static int nfp_abm_setup_tc_block_cb(enum tc_setup_type type,
228174ab544SJakub Kicinski 				     void *type_data, void *cb_priv)
229174ab544SJakub Kicinski {
230174ab544SJakub Kicinski 	struct tc_cls_u32_offload *cls_u32 = type_data;
231174ab544SJakub Kicinski 	struct nfp_repr *repr = cb_priv;
232174ab544SJakub Kicinski 	struct nfp_abm_link *alink;
233174ab544SJakub Kicinski 
234174ab544SJakub Kicinski 	alink = repr->app_priv;
235174ab544SJakub Kicinski 
236174ab544SJakub Kicinski 	if (type != TC_SETUP_CLSU32) {
237174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(cls_u32->common.extack,
238174ab544SJakub Kicinski 				   "only offload of u32 classifier supported");
239174ab544SJakub Kicinski 		return -EOPNOTSUPP;
240174ab544SJakub Kicinski 	}
241174ab544SJakub Kicinski 	if (!tc_cls_can_offload_and_chain0(repr->netdev, &cls_u32->common))
242174ab544SJakub Kicinski 		return -EOPNOTSUPP;
243174ab544SJakub Kicinski 
244174ab544SJakub Kicinski 	if (cls_u32->common.protocol != htons(ETH_P_IP) &&
245174ab544SJakub Kicinski 	    cls_u32->common.protocol != htons(ETH_P_IPV6)) {
246174ab544SJakub Kicinski 		NL_SET_ERR_MSG_MOD(cls_u32->common.extack,
247174ab544SJakub Kicinski 				   "only IP and IPv6 supported as filter protocol");
248174ab544SJakub Kicinski 		return -EOPNOTSUPP;
249174ab544SJakub Kicinski 	}
250174ab544SJakub Kicinski 
251174ab544SJakub Kicinski 	switch (cls_u32->command) {
252174ab544SJakub Kicinski 	case TC_CLSU32_NEW_KNODE:
253174ab544SJakub Kicinski 	case TC_CLSU32_REPLACE_KNODE:
254174ab544SJakub Kicinski 		return nfp_abm_u32_knode_replace(alink, &cls_u32->knode,
255174ab544SJakub Kicinski 						 cls_u32->common.protocol,
256174ab544SJakub Kicinski 						 cls_u32->common.extack);
257174ab544SJakub Kicinski 	case TC_CLSU32_DELETE_KNODE:
258174ab544SJakub Kicinski 		nfp_abm_u32_knode_delete(alink, &cls_u32->knode);
259174ab544SJakub Kicinski 		return 0;
260174ab544SJakub Kicinski 	default:
261174ab544SJakub Kicinski 		return -EOPNOTSUPP;
262174ab544SJakub Kicinski 	}
263174ab544SJakub Kicinski }
264174ab544SJakub Kicinski 
265955bcb6eSPablo Neira Ayuso static LIST_HEAD(nfp_abm_block_cb_list);
266955bcb6eSPablo Neira Ayuso 
nfp_abm_setup_cls_block(struct net_device * netdev,struct nfp_repr * repr,struct flow_block_offload * f)267174ab544SJakub Kicinski int nfp_abm_setup_cls_block(struct net_device *netdev, struct nfp_repr *repr,
268955bcb6eSPablo Neira Ayuso 			    struct flow_block_offload *f)
269174ab544SJakub Kicinski {
270955bcb6eSPablo Neira Ayuso 	return flow_block_cb_setup_simple(f, &nfp_abm_block_cb_list,
271955bcb6eSPablo Neira Ayuso 					  nfp_abm_setup_tc_block_cb,
2724e95bc26SPablo Neira Ayuso 					  repr, repr, true);
273174ab544SJakub Kicinski }
274