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