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