Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / drivers / net / ethernet / netronome / nfp / abm / cls.c
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
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
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
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
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
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                 err = -EOPNOTSUPP;
181                 goto err_delete;
182         }
183
184         tos_off = proto == htons(ETH_P_IP) ? 16 : 20;
185
186         /* Extract the DSCP Class Selector bits */
187         val = be32_to_cpu(knode->sel->keys[0].val) >> tos_off & 0xff;
188         mask = be32_to_cpu(knode->sel->keys[0].mask) >> tos_off & 0xff;
189
190         /* Check if there is no conflicting mapping and find match by handle */
191         list_for_each_entry(iter, &alink->dscp_map, list) {
192                 u32 cmask;
193
194                 if (iter->handle == knode->handle) {
195                         match = iter;
196                         continue;
197                 }
198
199                 cmask = iter->mask & mask;
200                 if ((iter->val & cmask) == (val & cmask) &&
201                     iter->band != knode->res->classid) {
202                         NL_SET_ERR_MSG_MOD(extack, "conflict with already offloaded filter");
203                         err = -EOPNOTSUPP;
204                         goto err_delete;
205                 }
206         }
207
208         if (!match) {
209                 match = kzalloc(sizeof(*match), GFP_KERNEL);
210                 if (!match) {
211                         err = -ENOMEM;
212                         goto err_delete;
213                 }
214
215                 list_add(&match->list, &alink->dscp_map);
216         }
217         match->handle = knode->handle;
218         match->band = knode->res->classid;
219         match->mask = mask;
220         match->val = val;
221
222         err = nfp_abm_update_band_map(alink);
223         if (err)
224                 goto err_delete;
225
226         return 0;
227
228 err_delete:
229         nfp_abm_u32_knode_delete(alink, knode);
230         return err;
231 }
232
233 static int nfp_abm_setup_tc_block_cb(enum tc_setup_type type,
234                                      void *type_data, void *cb_priv)
235 {
236         struct tc_cls_u32_offload *cls_u32 = type_data;
237         struct nfp_repr *repr = cb_priv;
238         struct nfp_abm_link *alink;
239
240         alink = repr->app_priv;
241
242         if (type != TC_SETUP_CLSU32) {
243                 NL_SET_ERR_MSG_MOD(cls_u32->common.extack,
244                                    "only offload of u32 classifier supported");
245                 return -EOPNOTSUPP;
246         }
247         if (!tc_cls_can_offload_and_chain0(repr->netdev, &cls_u32->common))
248                 return -EOPNOTSUPP;
249
250         if (cls_u32->common.protocol != htons(ETH_P_IP) &&
251             cls_u32->common.protocol != htons(ETH_P_IPV6)) {
252                 NL_SET_ERR_MSG_MOD(cls_u32->common.extack,
253                                    "only IP and IPv6 supported as filter protocol");
254                 return -EOPNOTSUPP;
255         }
256
257         switch (cls_u32->command) {
258         case TC_CLSU32_NEW_KNODE:
259         case TC_CLSU32_REPLACE_KNODE:
260                 return nfp_abm_u32_knode_replace(alink, &cls_u32->knode,
261                                                  cls_u32->common.protocol,
262                                                  cls_u32->common.extack);
263         case TC_CLSU32_DELETE_KNODE:
264                 nfp_abm_u32_knode_delete(alink, &cls_u32->knode);
265                 return 0;
266         default:
267                 return -EOPNOTSUPP;
268         }
269 }
270
271 static LIST_HEAD(nfp_abm_block_cb_list);
272
273 int nfp_abm_setup_cls_block(struct net_device *netdev, struct nfp_repr *repr,
274                             struct flow_block_offload *f)
275 {
276         return flow_block_cb_setup_simple(f, &nfp_abm_block_cb_list,
277                                           nfp_abm_setup_tc_block_cb,
278                                           repr, repr, true);
279 }