274 lines · 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 12struct nfp_abm_u32_match {13 u32 handle;14 u32 band;15 u8 mask;16 u8 val;17 struct list_head list;18};19 20static bool21nfp_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 or104 * low double digit number of prios and likewise for the filters. Also u32105 * doesn't report stats, so it's really only setup time cost.106 */107static unsigned int108nfp_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 119static 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 154static void155nfp_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 169static int170nfp_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 222err_delete:223 nfp_abm_u32_knode_delete(alink, knode);224 return -EOPNOTSUPP;225}226 227static 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 265static LIST_HEAD(nfp_abm_block_cb_list);266 267int 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