linux/drivers/net/ethernet/netronome/nfp/abm/cls.c
<<
>>
Prefs
   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 bool
  21nfp_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 */
 107static unsigned int
 108nfp_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 void
 155nfp_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 int
 170nfp_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