xref: /OK3568_Linux_fs/kernel/drivers/net/ethernet/netronome/nfp/abm/cls.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2*4882a593Smuzhiyun /* Copyright (C) 2018 Netronome Systems, Inc. */
3*4882a593Smuzhiyun 
4*4882a593Smuzhiyun #include <linux/bitfield.h>
5*4882a593Smuzhiyun #include <net/pkt_cls.h>
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include "../nfpcore/nfp_cpp.h"
8*4882a593Smuzhiyun #include "../nfp_app.h"
9*4882a593Smuzhiyun #include "../nfp_net_repr.h"
10*4882a593Smuzhiyun #include "main.h"
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun struct nfp_abm_u32_match {
13*4882a593Smuzhiyun 	u32 handle;
14*4882a593Smuzhiyun 	u32 band;
15*4882a593Smuzhiyun 	u8 mask;
16*4882a593Smuzhiyun 	u8 val;
17*4882a593Smuzhiyun 	struct list_head list;
18*4882a593Smuzhiyun };
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun static bool
nfp_abm_u32_check_knode(struct nfp_abm * abm,struct tc_cls_u32_knode * knode,__be16 proto,struct netlink_ext_ack * extack)21*4882a593Smuzhiyun nfp_abm_u32_check_knode(struct nfp_abm *abm, struct tc_cls_u32_knode *knode,
22*4882a593Smuzhiyun 			__be16 proto, struct netlink_ext_ack *extack)
23*4882a593Smuzhiyun {
24*4882a593Smuzhiyun 	struct tc_u32_key *k;
25*4882a593Smuzhiyun 	unsigned int tos_off;
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun 	if (knode->exts && tcf_exts_has_actions(knode->exts)) {
28*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "action offload not supported");
29*4882a593Smuzhiyun 		return false;
30*4882a593Smuzhiyun 	}
31*4882a593Smuzhiyun 	if (knode->link_handle) {
32*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "linking not supported");
33*4882a593Smuzhiyun 		return false;
34*4882a593Smuzhiyun 	}
35*4882a593Smuzhiyun 	if (knode->sel->flags != TC_U32_TERMINAL) {
36*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack,
37*4882a593Smuzhiyun 				   "flags must be equal to TC_U32_TERMINAL");
38*4882a593Smuzhiyun 		return false;
39*4882a593Smuzhiyun 	}
40*4882a593Smuzhiyun 	if (knode->sel->off || knode->sel->offshift || knode->sel->offmask ||
41*4882a593Smuzhiyun 	    knode->sel->offoff || knode->fshift) {
42*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "variable offsetting not supported");
43*4882a593Smuzhiyun 		return false;
44*4882a593Smuzhiyun 	}
45*4882a593Smuzhiyun 	if (knode->sel->hoff || knode->sel->hmask) {
46*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "hashing not supported");
47*4882a593Smuzhiyun 		return false;
48*4882a593Smuzhiyun 	}
49*4882a593Smuzhiyun 	if (knode->val || knode->mask) {
50*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "matching on mark not supported");
51*4882a593Smuzhiyun 		return false;
52*4882a593Smuzhiyun 	}
53*4882a593Smuzhiyun 	if (knode->res && knode->res->class) {
54*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "setting non-0 class not supported");
55*4882a593Smuzhiyun 		return false;
56*4882a593Smuzhiyun 	}
57*4882a593Smuzhiyun 	if (knode->res && knode->res->classid >= abm->num_bands) {
58*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack,
59*4882a593Smuzhiyun 				   "classid higher than number of bands");
60*4882a593Smuzhiyun 		return false;
61*4882a593Smuzhiyun 	}
62*4882a593Smuzhiyun 	if (knode->sel->nkeys != 1) {
63*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "exactly one key required");
64*4882a593Smuzhiyun 		return false;
65*4882a593Smuzhiyun 	}
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	switch (proto) {
68*4882a593Smuzhiyun 	case htons(ETH_P_IP):
69*4882a593Smuzhiyun 		tos_off = 16;
70*4882a593Smuzhiyun 		break;
71*4882a593Smuzhiyun 	case htons(ETH_P_IPV6):
72*4882a593Smuzhiyun 		tos_off = 20;
73*4882a593Smuzhiyun 		break;
74*4882a593Smuzhiyun 	default:
75*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "only IP and IPv6 supported as filter protocol");
76*4882a593Smuzhiyun 		return false;
77*4882a593Smuzhiyun 	}
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	k = &knode->sel->keys[0];
80*4882a593Smuzhiyun 	if (k->offmask) {
81*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "offset mask - variable offsetting not supported");
82*4882a593Smuzhiyun 		return false;
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun 	if (k->off) {
85*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "only DSCP fields can be matched");
86*4882a593Smuzhiyun 		return false;
87*4882a593Smuzhiyun 	}
88*4882a593Smuzhiyun 	if (k->val & ~k->mask) {
89*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "mask does not cover the key");
90*4882a593Smuzhiyun 		return false;
91*4882a593Smuzhiyun 	}
92*4882a593Smuzhiyun 	if (be32_to_cpu(k->mask) >> tos_off & ~abm->dscp_mask) {
93*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "only high DSCP class selector bits can be used");
94*4882a593Smuzhiyun 		nfp_err(abm->app->cpp,
95*4882a593Smuzhiyun 			"u32 offload: requested mask %x FW can support only %x\n",
96*4882a593Smuzhiyun 			be32_to_cpu(k->mask) >> tos_off, abm->dscp_mask);
97*4882a593Smuzhiyun 		return false;
98*4882a593Smuzhiyun 	}
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	return true;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun /* This filter list -> map conversion is O(n * m), we expect single digit or
104*4882a593Smuzhiyun  * low double digit number of prios and likewise for the filters.  Also u32
105*4882a593Smuzhiyun  * doesn't report stats, so it's really only setup time cost.
106*4882a593Smuzhiyun  */
107*4882a593Smuzhiyun static unsigned int
nfp_abm_find_band_for_prio(struct nfp_abm_link * alink,unsigned int prio)108*4882a593Smuzhiyun nfp_abm_find_band_for_prio(struct nfp_abm_link *alink, unsigned int prio)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun 	struct nfp_abm_u32_match *iter;
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	list_for_each_entry(iter, &alink->dscp_map, list)
113*4882a593Smuzhiyun 		if ((prio & iter->mask) == iter->val)
114*4882a593Smuzhiyun 			return iter->band;
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	return alink->def_band;
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun 
nfp_abm_update_band_map(struct nfp_abm_link * alink)119*4882a593Smuzhiyun static int nfp_abm_update_band_map(struct nfp_abm_link *alink)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun 	unsigned int i, bits_per_prio, prios_per_word, base_shift;
122*4882a593Smuzhiyun 	struct nfp_abm *abm = alink->abm;
123*4882a593Smuzhiyun 	u32 field_mask;
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	alink->has_prio = !list_empty(&alink->dscp_map);
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	bits_per_prio = roundup_pow_of_two(order_base_2(abm->num_bands));
128*4882a593Smuzhiyun 	field_mask = (1 << bits_per_prio) - 1;
129*4882a593Smuzhiyun 	prios_per_word = sizeof(u32) * BITS_PER_BYTE / bits_per_prio;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	/* FW mask applies from top bits */
132*4882a593Smuzhiyun 	base_shift = 8 - order_base_2(abm->num_prios);
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	for (i = 0; i < abm->num_prios; i++) {
135*4882a593Smuzhiyun 		unsigned int offset;
136*4882a593Smuzhiyun 		u32 *word;
137*4882a593Smuzhiyun 		u8 band;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 		word = &alink->prio_map[i / prios_per_word];
140*4882a593Smuzhiyun 		offset = (i % prios_per_word) * bits_per_prio;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 		band = nfp_abm_find_band_for_prio(alink, i << base_shift);
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 		*word &= ~(field_mask << offset);
145*4882a593Smuzhiyun 		*word |= band << offset;
146*4882a593Smuzhiyun 	}
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	/* Qdisc offload status may change if has_prio changed */
149*4882a593Smuzhiyun 	nfp_abm_qdisc_offload_update(alink);
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	return nfp_abm_ctrl_prio_map_update(alink, alink->prio_map);
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun static void
nfp_abm_u32_knode_delete(struct nfp_abm_link * alink,struct tc_cls_u32_knode * knode)155*4882a593Smuzhiyun nfp_abm_u32_knode_delete(struct nfp_abm_link *alink,
156*4882a593Smuzhiyun 			 struct tc_cls_u32_knode *knode)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun 	struct nfp_abm_u32_match *iter;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	list_for_each_entry(iter, &alink->dscp_map, list)
161*4882a593Smuzhiyun 		if (iter->handle == knode->handle) {
162*4882a593Smuzhiyun 			list_del(&iter->list);
163*4882a593Smuzhiyun 			kfree(iter);
164*4882a593Smuzhiyun 			nfp_abm_update_band_map(alink);
165*4882a593Smuzhiyun 			return;
166*4882a593Smuzhiyun 		}
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 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)170*4882a593Smuzhiyun nfp_abm_u32_knode_replace(struct nfp_abm_link *alink,
171*4882a593Smuzhiyun 			  struct tc_cls_u32_knode *knode,
172*4882a593Smuzhiyun 			  __be16 proto, struct netlink_ext_ack *extack)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun 	struct nfp_abm_u32_match *match = NULL, *iter;
175*4882a593Smuzhiyun 	unsigned int tos_off;
176*4882a593Smuzhiyun 	u8 mask, val;
177*4882a593Smuzhiyun 	int err;
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	if (!nfp_abm_u32_check_knode(alink->abm, knode, proto, extack))
180*4882a593Smuzhiyun 		goto err_delete;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	tos_off = proto == htons(ETH_P_IP) ? 16 : 20;
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	/* Extract the DSCP Class Selector bits */
185*4882a593Smuzhiyun 	val = be32_to_cpu(knode->sel->keys[0].val) >> tos_off & 0xff;
186*4882a593Smuzhiyun 	mask = be32_to_cpu(knode->sel->keys[0].mask) >> tos_off & 0xff;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	/* Check if there is no conflicting mapping and find match by handle */
189*4882a593Smuzhiyun 	list_for_each_entry(iter, &alink->dscp_map, list) {
190*4882a593Smuzhiyun 		u32 cmask;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 		if (iter->handle == knode->handle) {
193*4882a593Smuzhiyun 			match = iter;
194*4882a593Smuzhiyun 			continue;
195*4882a593Smuzhiyun 		}
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 		cmask = iter->mask & mask;
198*4882a593Smuzhiyun 		if ((iter->val & cmask) == (val & cmask) &&
199*4882a593Smuzhiyun 		    iter->band != knode->res->classid) {
200*4882a593Smuzhiyun 			NL_SET_ERR_MSG_MOD(extack, "conflict with already offloaded filter");
201*4882a593Smuzhiyun 			goto err_delete;
202*4882a593Smuzhiyun 		}
203*4882a593Smuzhiyun 	}
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	if (!match) {
206*4882a593Smuzhiyun 		match = kzalloc(sizeof(*match), GFP_KERNEL);
207*4882a593Smuzhiyun 		if (!match)
208*4882a593Smuzhiyun 			return -ENOMEM;
209*4882a593Smuzhiyun 		list_add(&match->list, &alink->dscp_map);
210*4882a593Smuzhiyun 	}
211*4882a593Smuzhiyun 	match->handle = knode->handle;
212*4882a593Smuzhiyun 	match->band = knode->res->classid;
213*4882a593Smuzhiyun 	match->mask = mask;
214*4882a593Smuzhiyun 	match->val = val;
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	err = nfp_abm_update_band_map(alink);
217*4882a593Smuzhiyun 	if (err)
218*4882a593Smuzhiyun 		goto err_delete;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	return 0;
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun err_delete:
223*4882a593Smuzhiyun 	nfp_abm_u32_knode_delete(alink, knode);
224*4882a593Smuzhiyun 	return -EOPNOTSUPP;
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun 
nfp_abm_setup_tc_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv)227*4882a593Smuzhiyun static int nfp_abm_setup_tc_block_cb(enum tc_setup_type type,
228*4882a593Smuzhiyun 				     void *type_data, void *cb_priv)
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun 	struct tc_cls_u32_offload *cls_u32 = type_data;
231*4882a593Smuzhiyun 	struct nfp_repr *repr = cb_priv;
232*4882a593Smuzhiyun 	struct nfp_abm_link *alink;
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun 	alink = repr->app_priv;
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	if (type != TC_SETUP_CLSU32) {
237*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(cls_u32->common.extack,
238*4882a593Smuzhiyun 				   "only offload of u32 classifier supported");
239*4882a593Smuzhiyun 		return -EOPNOTSUPP;
240*4882a593Smuzhiyun 	}
241*4882a593Smuzhiyun 	if (!tc_cls_can_offload_and_chain0(repr->netdev, &cls_u32->common))
242*4882a593Smuzhiyun 		return -EOPNOTSUPP;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	if (cls_u32->common.protocol != htons(ETH_P_IP) &&
245*4882a593Smuzhiyun 	    cls_u32->common.protocol != htons(ETH_P_IPV6)) {
246*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(cls_u32->common.extack,
247*4882a593Smuzhiyun 				   "only IP and IPv6 supported as filter protocol");
248*4882a593Smuzhiyun 		return -EOPNOTSUPP;
249*4882a593Smuzhiyun 	}
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	switch (cls_u32->command) {
252*4882a593Smuzhiyun 	case TC_CLSU32_NEW_KNODE:
253*4882a593Smuzhiyun 	case TC_CLSU32_REPLACE_KNODE:
254*4882a593Smuzhiyun 		return nfp_abm_u32_knode_replace(alink, &cls_u32->knode,
255*4882a593Smuzhiyun 						 cls_u32->common.protocol,
256*4882a593Smuzhiyun 						 cls_u32->common.extack);
257*4882a593Smuzhiyun 	case TC_CLSU32_DELETE_KNODE:
258*4882a593Smuzhiyun 		nfp_abm_u32_knode_delete(alink, &cls_u32->knode);
259*4882a593Smuzhiyun 		return 0;
260*4882a593Smuzhiyun 	default:
261*4882a593Smuzhiyun 		return -EOPNOTSUPP;
262*4882a593Smuzhiyun 	}
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun static LIST_HEAD(nfp_abm_block_cb_list);
266*4882a593Smuzhiyun 
nfp_abm_setup_cls_block(struct net_device * netdev,struct nfp_repr * repr,struct flow_block_offload * f)267*4882a593Smuzhiyun int nfp_abm_setup_cls_block(struct net_device *netdev, struct nfp_repr *repr,
268*4882a593Smuzhiyun 			    struct flow_block_offload *f)
269*4882a593Smuzhiyun {
270*4882a593Smuzhiyun 	return flow_block_cb_setup_simple(f, &nfp_abm_block_cb_list,
271*4882a593Smuzhiyun 					  nfp_abm_setup_tc_block_cb,
272*4882a593Smuzhiyun 					  repr, repr, true);
273*4882a593Smuzhiyun }
274