1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (C) 2018 Netronome Systems, Inc. */
4 #include <linux/bitfield.h>
5 #include <net/pkt_cls.h>
7 #include "../nfpcore/nfp_cpp.h"
8 #include "../nfp_app.h"
9 #include "../nfp_net_repr.h"
12 struct nfp_abm_u32_match
{
17 struct list_head list
;
21 nfp_abm_u32_check_knode(struct nfp_abm
*abm
, struct tc_cls_u32_knode
*knode
,
22 __be16 proto
, struct netlink_ext_ack
*extack
)
27 if (knode
->exts
&& tcf_exts_has_actions(knode
->exts
)) {
28 NL_SET_ERR_MSG_MOD(extack
, "action offload not supported");
31 if (knode
->link_handle
) {
32 NL_SET_ERR_MSG_MOD(extack
, "linking not supported");
35 if (knode
->sel
->flags
!= TC_U32_TERMINAL
) {
36 NL_SET_ERR_MSG_MOD(extack
,
37 "flags must be equal to TC_U32_TERMINAL");
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");
45 if (knode
->sel
->hoff
|| knode
->sel
->hmask
) {
46 NL_SET_ERR_MSG_MOD(extack
, "hashing not supported");
49 if (knode
->val
|| knode
->mask
) {
50 NL_SET_ERR_MSG_MOD(extack
, "matching on mark not supported");
53 if (knode
->res
&& knode
->res
->class) {
54 NL_SET_ERR_MSG_MOD(extack
, "setting non-0 class not supported");
57 if (knode
->res
&& knode
->res
->classid
>= abm
->num_bands
) {
58 NL_SET_ERR_MSG_MOD(extack
,
59 "classid higher than number of bands");
62 if (knode
->sel
->nkeys
!= 1) {
63 NL_SET_ERR_MSG_MOD(extack
, "exactly one key required");
71 case htons(ETH_P_IPV6
):
75 NL_SET_ERR_MSG_MOD(extack
, "only IP and IPv6 supported as filter protocol");
79 k
= &knode
->sel
->keys
[0];
81 NL_SET_ERR_MSG_MOD(extack
, "offset mask - variable offsetting not supported");
85 NL_SET_ERR_MSG_MOD(extack
, "only DSCP fields can be matched");
88 if (k
->val
& ~k
->mask
) {
89 NL_SET_ERR_MSG_MOD(extack
, "mask does not cover the key");
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
);
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.
108 nfp_abm_find_band_for_prio(struct nfp_abm_link
*alink
, unsigned int prio
)
110 struct nfp_abm_u32_match
*iter
;
112 list_for_each_entry(iter
, &alink
->dscp_map
, list
)
113 if ((prio
& iter
->mask
) == iter
->val
)
116 return alink
->def_band
;
119 static int nfp_abm_update_band_map(struct nfp_abm_link
*alink
)
121 unsigned int i
, bits_per_prio
, prios_per_word
, base_shift
;
122 struct nfp_abm
*abm
= alink
->abm
;
125 alink
->has_prio
= !list_empty(&alink
->dscp_map
);
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
;
131 /* FW mask applies from top bits */
132 base_shift
= 8 - order_base_2(abm
->num_prios
);
134 for (i
= 0; i
< abm
->num_prios
; i
++) {
139 word
= &alink
->prio_map
[i
/ prios_per_word
];
140 offset
= (i
% prios_per_word
) * bits_per_prio
;
142 band
= nfp_abm_find_band_for_prio(alink
, i
<< base_shift
);
144 *word
&= ~(field_mask
<< offset
);
145 *word
|= band
<< offset
;
148 /* Qdisc offload status may change if has_prio changed */
149 nfp_abm_qdisc_offload_update(alink
);
151 return nfp_abm_ctrl_prio_map_update(alink
, alink
->prio_map
);
155 nfp_abm_u32_knode_delete(struct nfp_abm_link
*alink
,
156 struct tc_cls_u32_knode
*knode
)
158 struct nfp_abm_u32_match
*iter
;
160 list_for_each_entry(iter
, &alink
->dscp_map
, list
)
161 if (iter
->handle
== knode
->handle
) {
162 list_del(&iter
->list
);
164 nfp_abm_update_band_map(alink
);
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
)
174 struct nfp_abm_u32_match
*match
= NULL
, *iter
;
175 unsigned int tos_off
;
179 if (!nfp_abm_u32_check_knode(alink
->abm
, knode
, proto
, extack
))
182 tos_off
= proto
== htons(ETH_P_IP
) ? 16 : 20;
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;
188 /* Check if there is no conflicting mapping and find match by handle */
189 list_for_each_entry(iter
, &alink
->dscp_map
, list
) {
192 if (iter
->handle
== knode
->handle
) {
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");
206 match
= kzalloc(sizeof(*match
), GFP_KERNEL
);
209 list_add(&match
->list
, &alink
->dscp_map
);
211 match
->handle
= knode
->handle
;
212 match
->band
= knode
->res
->classid
;
216 err
= nfp_abm_update_band_map(alink
);
223 nfp_abm_u32_knode_delete(alink
, knode
);
227 static int nfp_abm_setup_tc_block_cb(enum tc_setup_type type
,
228 void *type_data
, void *cb_priv
)
230 struct tc_cls_u32_offload
*cls_u32
= type_data
;
231 struct nfp_repr
*repr
= cb_priv
;
232 struct nfp_abm_link
*alink
;
234 alink
= repr
->app_priv
;
236 if (type
!= TC_SETUP_CLSU32
) {
237 NL_SET_ERR_MSG_MOD(cls_u32
->common
.extack
,
238 "only offload of u32 classifier supported");
241 if (!tc_cls_can_offload_and_chain0(repr
->netdev
, &cls_u32
->common
))
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");
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
);
265 static LIST_HEAD(nfp_abm_block_cb_list
);
267 int nfp_abm_setup_cls_block(struct net_device
*netdev
, struct nfp_repr
*repr
,
268 struct flow_block_offload
*f
)
270 return flow_block_cb_setup_simple(f
, &nfp_abm_block_cb_list
,
271 nfp_abm_setup_tc_block_cb
,