Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
0002 /* Copyright (C) 2018 Netronome Systems, Inc. */
0003 
0004 #include <linux/bitfield.h>
0005 #include <net/pkt_cls.h>
0006 
0007 #include "../nfpcore/nfp_cpp.h"
0008 #include "../nfp_app.h"
0009 #include "../nfp_net_repr.h"
0010 #include "main.h"
0011 
0012 struct nfp_abm_u32_match {
0013     u32 handle;
0014     u32 band;
0015     u8 mask;
0016     u8 val;
0017     struct list_head list;
0018 };
0019 
0020 static bool
0021 nfp_abm_u32_check_knode(struct nfp_abm *abm, struct tc_cls_u32_knode *knode,
0022             __be16 proto, struct netlink_ext_ack *extack)
0023 {
0024     struct tc_u32_key *k;
0025     unsigned int tos_off;
0026 
0027     if (knode->exts && tcf_exts_has_actions(knode->exts)) {
0028         NL_SET_ERR_MSG_MOD(extack, "action offload not supported");
0029         return false;
0030     }
0031     if (knode->link_handle) {
0032         NL_SET_ERR_MSG_MOD(extack, "linking not supported");
0033         return false;
0034     }
0035     if (knode->sel->flags != TC_U32_TERMINAL) {
0036         NL_SET_ERR_MSG_MOD(extack,
0037                    "flags must be equal to TC_U32_TERMINAL");
0038         return false;
0039     }
0040     if (knode->sel->off || knode->sel->offshift || knode->sel->offmask ||
0041         knode->sel->offoff || knode->fshift) {
0042         NL_SET_ERR_MSG_MOD(extack, "variable offsetting not supported");
0043         return false;
0044     }
0045     if (knode->sel->hoff || knode->sel->hmask) {
0046         NL_SET_ERR_MSG_MOD(extack, "hashing not supported");
0047         return false;
0048     }
0049     if (knode->val || knode->mask) {
0050         NL_SET_ERR_MSG_MOD(extack, "matching on mark not supported");
0051         return false;
0052     }
0053     if (knode->res && knode->res->class) {
0054         NL_SET_ERR_MSG_MOD(extack, "setting non-0 class not supported");
0055         return false;
0056     }
0057     if (knode->res && knode->res->classid >= abm->num_bands) {
0058         NL_SET_ERR_MSG_MOD(extack,
0059                    "classid higher than number of bands");
0060         return false;
0061     }
0062     if (knode->sel->nkeys != 1) {
0063         NL_SET_ERR_MSG_MOD(extack, "exactly one key required");
0064         return false;
0065     }
0066 
0067     switch (proto) {
0068     case htons(ETH_P_IP):
0069         tos_off = 16;
0070         break;
0071     case htons(ETH_P_IPV6):
0072         tos_off = 20;
0073         break;
0074     default:
0075         NL_SET_ERR_MSG_MOD(extack, "only IP and IPv6 supported as filter protocol");
0076         return false;
0077     }
0078 
0079     k = &knode->sel->keys[0];
0080     if (k->offmask) {
0081         NL_SET_ERR_MSG_MOD(extack, "offset mask - variable offsetting not supported");
0082         return false;
0083     }
0084     if (k->off) {
0085         NL_SET_ERR_MSG_MOD(extack, "only DSCP fields can be matched");
0086         return false;
0087     }
0088     if (k->val & ~k->mask) {
0089         NL_SET_ERR_MSG_MOD(extack, "mask does not cover the key");
0090         return false;
0091     }
0092     if (be32_to_cpu(k->mask) >> tos_off & ~abm->dscp_mask) {
0093         NL_SET_ERR_MSG_MOD(extack, "only high DSCP class selector bits can be used");
0094         nfp_err(abm->app->cpp,
0095             "u32 offload: requested mask %x FW can support only %x\n",
0096             be32_to_cpu(k->mask) >> tos_off, abm->dscp_mask);
0097         return false;
0098     }
0099 
0100     return true;
0101 }
0102 
0103 /* This filter list -> map conversion is O(n * m), we expect single digit or
0104  * low double digit number of prios and likewise for the filters.  Also u32
0105  * doesn't report stats, so it's really only setup time cost.
0106  */
0107 static unsigned int
0108 nfp_abm_find_band_for_prio(struct nfp_abm_link *alink, unsigned int prio)
0109 {
0110     struct nfp_abm_u32_match *iter;
0111 
0112     list_for_each_entry(iter, &alink->dscp_map, list)
0113         if ((prio & iter->mask) == iter->val)
0114             return iter->band;
0115 
0116     return alink->def_band;
0117 }
0118 
0119 static int nfp_abm_update_band_map(struct nfp_abm_link *alink)
0120 {
0121     unsigned int i, bits_per_prio, prios_per_word, base_shift;
0122     struct nfp_abm *abm = alink->abm;
0123     u32 field_mask;
0124 
0125     alink->has_prio = !list_empty(&alink->dscp_map);
0126 
0127     bits_per_prio = roundup_pow_of_two(order_base_2(abm->num_bands));
0128     field_mask = (1 << bits_per_prio) - 1;
0129     prios_per_word = sizeof(u32) * BITS_PER_BYTE / bits_per_prio;
0130 
0131     /* FW mask applies from top bits */
0132     base_shift = 8 - order_base_2(abm->num_prios);
0133 
0134     for (i = 0; i < abm->num_prios; i++) {
0135         unsigned int offset;
0136         u32 *word;
0137         u8 band;
0138 
0139         word = &alink->prio_map[i / prios_per_word];
0140         offset = (i % prios_per_word) * bits_per_prio;
0141 
0142         band = nfp_abm_find_band_for_prio(alink, i << base_shift);
0143 
0144         *word &= ~(field_mask << offset);
0145         *word |= band << offset;
0146     }
0147 
0148     /* Qdisc offload status may change if has_prio changed */
0149     nfp_abm_qdisc_offload_update(alink);
0150 
0151     return nfp_abm_ctrl_prio_map_update(alink, alink->prio_map);
0152 }
0153 
0154 static void
0155 nfp_abm_u32_knode_delete(struct nfp_abm_link *alink,
0156              struct tc_cls_u32_knode *knode)
0157 {
0158     struct nfp_abm_u32_match *iter;
0159 
0160     list_for_each_entry(iter, &alink->dscp_map, list)
0161         if (iter->handle == knode->handle) {
0162             list_del(&iter->list);
0163             kfree(iter);
0164             nfp_abm_update_band_map(alink);
0165             return;
0166         }
0167 }
0168 
0169 static int
0170 nfp_abm_u32_knode_replace(struct nfp_abm_link *alink,
0171               struct tc_cls_u32_knode *knode,
0172               __be16 proto, struct netlink_ext_ack *extack)
0173 {
0174     struct nfp_abm_u32_match *match = NULL, *iter;
0175     unsigned int tos_off;
0176     u8 mask, val;
0177     int err;
0178 
0179     if (!nfp_abm_u32_check_knode(alink->abm, knode, proto, extack))
0180         goto err_delete;
0181 
0182     tos_off = proto == htons(ETH_P_IP) ? 16 : 20;
0183 
0184     /* Extract the DSCP Class Selector bits */
0185     val = be32_to_cpu(knode->sel->keys[0].val) >> tos_off & 0xff;
0186     mask = be32_to_cpu(knode->sel->keys[0].mask) >> tos_off & 0xff;
0187 
0188     /* Check if there is no conflicting mapping and find match by handle */
0189     list_for_each_entry(iter, &alink->dscp_map, list) {
0190         u32 cmask;
0191 
0192         if (iter->handle == knode->handle) {
0193             match = iter;
0194             continue;
0195         }
0196 
0197         cmask = iter->mask & mask;
0198         if ((iter->val & cmask) == (val & cmask) &&
0199             iter->band != knode->res->classid) {
0200             NL_SET_ERR_MSG_MOD(extack, "conflict with already offloaded filter");
0201             goto err_delete;
0202         }
0203     }
0204 
0205     if (!match) {
0206         match = kzalloc(sizeof(*match), GFP_KERNEL);
0207         if (!match)
0208             return -ENOMEM;
0209         list_add(&match->list, &alink->dscp_map);
0210     }
0211     match->handle = knode->handle;
0212     match->band = knode->res->classid;
0213     match->mask = mask;
0214     match->val = val;
0215 
0216     err = nfp_abm_update_band_map(alink);
0217     if (err)
0218         goto err_delete;
0219 
0220     return 0;
0221 
0222 err_delete:
0223     nfp_abm_u32_knode_delete(alink, knode);
0224     return -EOPNOTSUPP;
0225 }
0226 
0227 static int nfp_abm_setup_tc_block_cb(enum tc_setup_type type,
0228                      void *type_data, void *cb_priv)
0229 {
0230     struct tc_cls_u32_offload *cls_u32 = type_data;
0231     struct nfp_repr *repr = cb_priv;
0232     struct nfp_abm_link *alink;
0233 
0234     alink = repr->app_priv;
0235 
0236     if (type != TC_SETUP_CLSU32) {
0237         NL_SET_ERR_MSG_MOD(cls_u32->common.extack,
0238                    "only offload of u32 classifier supported");
0239         return -EOPNOTSUPP;
0240     }
0241     if (!tc_cls_can_offload_and_chain0(repr->netdev, &cls_u32->common))
0242         return -EOPNOTSUPP;
0243 
0244     if (cls_u32->common.protocol != htons(ETH_P_IP) &&
0245         cls_u32->common.protocol != htons(ETH_P_IPV6)) {
0246         NL_SET_ERR_MSG_MOD(cls_u32->common.extack,
0247                    "only IP and IPv6 supported as filter protocol");
0248         return -EOPNOTSUPP;
0249     }
0250 
0251     switch (cls_u32->command) {
0252     case TC_CLSU32_NEW_KNODE:
0253     case TC_CLSU32_REPLACE_KNODE:
0254         return nfp_abm_u32_knode_replace(alink, &cls_u32->knode,
0255                          cls_u32->common.protocol,
0256                          cls_u32->common.extack);
0257     case TC_CLSU32_DELETE_KNODE:
0258         nfp_abm_u32_knode_delete(alink, &cls_u32->knode);
0259         return 0;
0260     default:
0261         return -EOPNOTSUPP;
0262     }
0263 }
0264 
0265 static LIST_HEAD(nfp_abm_block_cb_list);
0266 
0267 int nfp_abm_setup_cls_block(struct net_device *netdev, struct nfp_repr *repr,
0268                 struct flow_block_offload *f)
0269 {
0270     return flow_block_cb_setup_simple(f, &nfp_abm_block_cb_list,
0271                       nfp_abm_setup_tc_block_cb,
0272                       repr, repr, true);
0273 }