linux/drivers/net/ethernet/mscc/ocelot_flower.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0 OR MIT)
   2/* Microsemi Ocelot Switch driver
   3 * Copyright (c) 2019 Microsemi Corporation
   4 */
   5
   6#include <net/pkt_cls.h>
   7#include <net/tc_act/tc_gact.h>
   8#include <soc/mscc/ocelot_vcap.h>
   9#include "ocelot_vcap.h"
  10
  11/* Arbitrarily chosen constants for encoding the VCAP block and lookup number
  12 * into the chain number. This is UAPI.
  13 */
  14#define VCAP_BLOCK                      10000
  15#define VCAP_LOOKUP                     1000
  16#define VCAP_IS1_NUM_LOOKUPS            3
  17#define VCAP_IS2_NUM_LOOKUPS            2
  18#define VCAP_IS2_NUM_PAG                256
  19#define VCAP_IS1_CHAIN(lookup)          \
  20        (1 * VCAP_BLOCK + (lookup) * VCAP_LOOKUP)
  21#define VCAP_IS2_CHAIN(lookup, pag)     \
  22        (2 * VCAP_BLOCK + (lookup) * VCAP_LOOKUP + (pag))
  23
  24static int ocelot_chain_to_block(int chain, bool ingress)
  25{
  26        int lookup, pag;
  27
  28        if (!ingress) {
  29                if (chain == 0)
  30                        return VCAP_ES0;
  31                return -EOPNOTSUPP;
  32        }
  33
  34        /* Backwards compatibility with older, single-chain tc-flower
  35         * offload support in Ocelot
  36         */
  37        if (chain == 0)
  38                return VCAP_IS2;
  39
  40        for (lookup = 0; lookup < VCAP_IS1_NUM_LOOKUPS; lookup++)
  41                if (chain == VCAP_IS1_CHAIN(lookup))
  42                        return VCAP_IS1;
  43
  44        for (lookup = 0; lookup < VCAP_IS2_NUM_LOOKUPS; lookup++)
  45                for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)
  46                        if (chain == VCAP_IS2_CHAIN(lookup, pag))
  47                                return VCAP_IS2;
  48
  49        return -EOPNOTSUPP;
  50}
  51
  52/* Caller must ensure this is a valid IS1 or IS2 chain first,
  53 * by calling ocelot_chain_to_block.
  54 */
  55static int ocelot_chain_to_lookup(int chain)
  56{
  57        return (chain / VCAP_LOOKUP) % 10;
  58}
  59
  60/* Caller must ensure this is a valid IS2 chain first,
  61 * by calling ocelot_chain_to_block.
  62 */
  63static int ocelot_chain_to_pag(int chain)
  64{
  65        int lookup = ocelot_chain_to_lookup(chain);
  66
  67        /* calculate PAG value as chain index relative to the first PAG */
  68        return chain - VCAP_IS2_CHAIN(lookup, 0);
  69}
  70
  71static bool ocelot_is_goto_target_valid(int goto_target, int chain,
  72                                        bool ingress)
  73{
  74        int pag;
  75
  76        /* Can't offload GOTO in VCAP ES0 */
  77        if (!ingress)
  78                return (goto_target < 0);
  79
  80        /* Non-optional GOTOs */
  81        if (chain == 0)
  82                /* VCAP IS1 can be skipped, either partially or completely */
  83                return (goto_target == VCAP_IS1_CHAIN(0) ||
  84                        goto_target == VCAP_IS1_CHAIN(1) ||
  85                        goto_target == VCAP_IS1_CHAIN(2) ||
  86                        goto_target == VCAP_IS2_CHAIN(0, 0) ||
  87                        goto_target == VCAP_IS2_CHAIN(1, 0));
  88
  89        if (chain == VCAP_IS1_CHAIN(0))
  90                return (goto_target == VCAP_IS1_CHAIN(1));
  91
  92        if (chain == VCAP_IS1_CHAIN(1))
  93                return (goto_target == VCAP_IS1_CHAIN(2));
  94
  95        /* Lookup 2 of VCAP IS1 can really support non-optional GOTOs,
  96         * using a Policy Association Group (PAG) value, which is an 8-bit
  97         * value encoding a VCAP IS2 target chain.
  98         */
  99        if (chain == VCAP_IS1_CHAIN(2)) {
 100                for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)
 101                        if (goto_target == VCAP_IS2_CHAIN(0, pag))
 102                                return true;
 103
 104                return false;
 105        }
 106
 107        /* Non-optional GOTO from VCAP IS2 lookup 0 to lookup 1.
 108         * We cannot change the PAG at this point.
 109         */
 110        for (pag = 0; pag < VCAP_IS2_NUM_PAG; pag++)
 111                if (chain == VCAP_IS2_CHAIN(0, pag))
 112                        return (goto_target == VCAP_IS2_CHAIN(1, pag));
 113
 114        /* VCAP IS2 lookup 1 cannot jump anywhere */
 115        return false;
 116}
 117
 118static struct ocelot_vcap_filter *
 119ocelot_find_vcap_filter_that_points_at(struct ocelot *ocelot, int chain)
 120{
 121        struct ocelot_vcap_filter *filter;
 122        struct ocelot_vcap_block *block;
 123        int block_id;
 124
 125        block_id = ocelot_chain_to_block(chain, true);
 126        if (block_id < 0)
 127                return NULL;
 128
 129        if (block_id == VCAP_IS2) {
 130                block = &ocelot->block[VCAP_IS1];
 131
 132                list_for_each_entry(filter, &block->rules, list)
 133                        if (filter->type == OCELOT_VCAP_FILTER_PAG &&
 134                            filter->goto_target == chain)
 135                                return filter;
 136        }
 137
 138        list_for_each_entry(filter, &ocelot->dummy_rules, list)
 139                if (filter->goto_target == chain)
 140                        return filter;
 141
 142        return NULL;
 143}
 144
 145static int ocelot_flower_parse_action(struct ocelot *ocelot, int port,
 146                                      bool ingress, struct flow_cls_offload *f,
 147                                      struct ocelot_vcap_filter *filter)
 148{
 149        struct ocelot_port *ocelot_port = ocelot->ports[port];
 150        struct netlink_ext_ack *extack = f->common.extack;
 151        bool allow_missing_goto_target = false;
 152        const struct flow_action_entry *a;
 153        enum ocelot_tag_tpid_sel tpid;
 154        int i, chain, egress_port;
 155        u64 rate;
 156
 157        if (!flow_action_basic_hw_stats_check(&f->rule->action,
 158                                              f->common.extack))
 159                return -EOPNOTSUPP;
 160
 161        chain = f->common.chain_index;
 162        filter->block_id = ocelot_chain_to_block(chain, ingress);
 163        if (filter->block_id < 0) {
 164                NL_SET_ERR_MSG_MOD(extack, "Cannot offload to this chain");
 165                return -EOPNOTSUPP;
 166        }
 167        if (filter->block_id == VCAP_IS1 || filter->block_id == VCAP_IS2)
 168                filter->lookup = ocelot_chain_to_lookup(chain);
 169        if (filter->block_id == VCAP_IS2)
 170                filter->pag = ocelot_chain_to_pag(chain);
 171
 172        filter->goto_target = -1;
 173        filter->type = OCELOT_VCAP_FILTER_DUMMY;
 174
 175        flow_action_for_each(i, a, &f->rule->action) {
 176                switch (a->id) {
 177                case FLOW_ACTION_DROP:
 178                        if (filter->block_id != VCAP_IS2) {
 179                                NL_SET_ERR_MSG_MOD(extack,
 180                                                   "Drop action can only be offloaded to VCAP IS2");
 181                                return -EOPNOTSUPP;
 182                        }
 183                        if (filter->goto_target != -1) {
 184                                NL_SET_ERR_MSG_MOD(extack,
 185                                                   "Last action must be GOTO");
 186                                return -EOPNOTSUPP;
 187                        }
 188                        filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
 189                        filter->action.port_mask = 0;
 190                        filter->action.police_ena = true;
 191                        filter->action.pol_ix = OCELOT_POLICER_DISCARD;
 192                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 193                        break;
 194                case FLOW_ACTION_TRAP:
 195                        if (filter->block_id != VCAP_IS2) {
 196                                NL_SET_ERR_MSG_MOD(extack,
 197                                                   "Trap action can only be offloaded to VCAP IS2");
 198                                return -EOPNOTSUPP;
 199                        }
 200                        if (filter->goto_target != -1) {
 201                                NL_SET_ERR_MSG_MOD(extack,
 202                                                   "Last action must be GOTO");
 203                                return -EOPNOTSUPP;
 204                        }
 205                        filter->action.mask_mode = OCELOT_MASK_MODE_PERMIT_DENY;
 206                        filter->action.port_mask = 0;
 207                        filter->action.cpu_copy_ena = true;
 208                        filter->action.cpu_qu_num = 0;
 209                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 210                        break;
 211                case FLOW_ACTION_POLICE:
 212                        if (filter->block_id != VCAP_IS2 ||
 213                            filter->lookup != 0) {
 214                                NL_SET_ERR_MSG_MOD(extack,
 215                                                   "Police action can only be offloaded to VCAP IS2 lookup 0");
 216                                return -EOPNOTSUPP;
 217                        }
 218                        if (filter->goto_target != -1) {
 219                                NL_SET_ERR_MSG_MOD(extack,
 220                                                   "Last action must be GOTO");
 221                                return -EOPNOTSUPP;
 222                        }
 223                        filter->action.police_ena = true;
 224                        rate = a->police.rate_bytes_ps;
 225                        filter->action.pol.rate = div_u64(rate, 1000) * 8;
 226                        filter->action.pol.burst = a->police.burst;
 227                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 228                        break;
 229                case FLOW_ACTION_REDIRECT:
 230                        if (filter->block_id != VCAP_IS2) {
 231                                NL_SET_ERR_MSG_MOD(extack,
 232                                                   "Redirect action can only be offloaded to VCAP IS2");
 233                                return -EOPNOTSUPP;
 234                        }
 235                        if (filter->goto_target != -1) {
 236                                NL_SET_ERR_MSG_MOD(extack,
 237                                                   "Last action must be GOTO");
 238                                return -EOPNOTSUPP;
 239                        }
 240                        egress_port = ocelot->ops->netdev_to_port(a->dev);
 241                        if (egress_port < 0) {
 242                                NL_SET_ERR_MSG_MOD(extack,
 243                                                   "Destination not an ocelot port");
 244                                return -EOPNOTSUPP;
 245                        }
 246                        filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
 247                        filter->action.port_mask = BIT(egress_port);
 248                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 249                        break;
 250                case FLOW_ACTION_VLAN_POP:
 251                        if (filter->block_id != VCAP_IS1) {
 252                                NL_SET_ERR_MSG_MOD(extack,
 253                                                   "VLAN pop action can only be offloaded to VCAP IS1");
 254                                return -EOPNOTSUPP;
 255                        }
 256                        if (filter->goto_target != -1) {
 257                                NL_SET_ERR_MSG_MOD(extack,
 258                                                   "Last action must be GOTO");
 259                                return -EOPNOTSUPP;
 260                        }
 261                        filter->action.vlan_pop_cnt_ena = true;
 262                        filter->action.vlan_pop_cnt++;
 263                        if (filter->action.vlan_pop_cnt > 2) {
 264                                NL_SET_ERR_MSG_MOD(extack,
 265                                                   "Cannot pop more than 2 VLAN headers");
 266                                return -EOPNOTSUPP;
 267                        }
 268                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 269                        break;
 270                case FLOW_ACTION_VLAN_MANGLE:
 271                        if (filter->block_id != VCAP_IS1) {
 272                                NL_SET_ERR_MSG_MOD(extack,
 273                                                   "VLAN modify action can only be offloaded to VCAP IS1");
 274                                return -EOPNOTSUPP;
 275                        }
 276                        if (filter->goto_target != -1) {
 277                                NL_SET_ERR_MSG_MOD(extack,
 278                                                   "Last action must be GOTO");
 279                                return -EOPNOTSUPP;
 280                        }
 281                        if (!ocelot_port->vlan_aware) {
 282                                NL_SET_ERR_MSG_MOD(extack,
 283                                                   "Can only modify VLAN under VLAN aware bridge");
 284                                return -EOPNOTSUPP;
 285                        }
 286                        filter->action.vid_replace_ena = true;
 287                        filter->action.pcp_dei_ena = true;
 288                        filter->action.vid = a->vlan.vid;
 289                        filter->action.pcp = a->vlan.prio;
 290                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 291                        break;
 292                case FLOW_ACTION_PRIORITY:
 293                        if (filter->block_id != VCAP_IS1) {
 294                                NL_SET_ERR_MSG_MOD(extack,
 295                                                   "Priority action can only be offloaded to VCAP IS1");
 296                                return -EOPNOTSUPP;
 297                        }
 298                        if (filter->goto_target != -1) {
 299                                NL_SET_ERR_MSG_MOD(extack,
 300                                                   "Last action must be GOTO");
 301                                return -EOPNOTSUPP;
 302                        }
 303                        filter->action.qos_ena = true;
 304                        filter->action.qos_val = a->priority;
 305                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 306                        break;
 307                case FLOW_ACTION_GOTO:
 308                        filter->goto_target = a->chain_index;
 309
 310                        if (filter->block_id == VCAP_IS1 && filter->lookup == 2) {
 311                                int pag = ocelot_chain_to_pag(filter->goto_target);
 312
 313                                filter->action.pag_override_mask = 0xff;
 314                                filter->action.pag_val = pag;
 315                                filter->type = OCELOT_VCAP_FILTER_PAG;
 316                        }
 317                        break;
 318                case FLOW_ACTION_VLAN_PUSH:
 319                        if (filter->block_id != VCAP_ES0) {
 320                                NL_SET_ERR_MSG_MOD(extack,
 321                                                   "VLAN push action can only be offloaded to VCAP ES0");
 322                                return -EOPNOTSUPP;
 323                        }
 324                        switch (ntohs(a->vlan.proto)) {
 325                        case ETH_P_8021Q:
 326                                tpid = OCELOT_TAG_TPID_SEL_8021Q;
 327                                break;
 328                        case ETH_P_8021AD:
 329                                tpid = OCELOT_TAG_TPID_SEL_8021AD;
 330                                break;
 331                        default:
 332                                NL_SET_ERR_MSG_MOD(extack,
 333                                                   "Cannot push custom TPID");
 334                                return -EOPNOTSUPP;
 335                        }
 336                        filter->action.tag_a_tpid_sel = tpid;
 337                        filter->action.push_outer_tag = OCELOT_ES0_TAG;
 338                        filter->action.tag_a_vid_sel = 1;
 339                        filter->action.vid_a_val = a->vlan.vid;
 340                        filter->action.pcp_a_val = a->vlan.prio;
 341                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 342                        break;
 343                default:
 344                        NL_SET_ERR_MSG_MOD(extack, "Cannot offload action");
 345                        return -EOPNOTSUPP;
 346                }
 347        }
 348
 349        if (filter->goto_target == -1) {
 350                if ((filter->block_id == VCAP_IS2 && filter->lookup == 1) ||
 351                    chain == 0) {
 352                        allow_missing_goto_target = true;
 353                } else {
 354                        NL_SET_ERR_MSG_MOD(extack, "Missing GOTO action");
 355                        return -EOPNOTSUPP;
 356                }
 357        }
 358
 359        if (!ocelot_is_goto_target_valid(filter->goto_target, chain, ingress) &&
 360            !allow_missing_goto_target) {
 361                NL_SET_ERR_MSG_MOD(extack, "Cannot offload this GOTO target");
 362                return -EOPNOTSUPP;
 363        }
 364
 365        return 0;
 366}
 367
 368static int ocelot_flower_parse_indev(struct ocelot *ocelot, int port,
 369                                     struct flow_cls_offload *f,
 370                                     struct ocelot_vcap_filter *filter)
 371{
 372        struct flow_rule *rule = flow_cls_offload_flow_rule(f);
 373        const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
 374        int key_length = vcap->keys[VCAP_ES0_IGR_PORT].length;
 375        struct netlink_ext_ack *extack = f->common.extack;
 376        struct net_device *dev, *indev;
 377        struct flow_match_meta match;
 378        int ingress_port;
 379
 380        flow_rule_match_meta(rule, &match);
 381
 382        if (!match.mask->ingress_ifindex)
 383                return 0;
 384
 385        if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
 386                NL_SET_ERR_MSG_MOD(extack, "Unsupported ingress ifindex mask");
 387                return -EOPNOTSUPP;
 388        }
 389
 390        dev = ocelot->ops->port_to_netdev(ocelot, port);
 391        if (!dev)
 392                return -EINVAL;
 393
 394        indev = __dev_get_by_index(dev_net(dev), match.key->ingress_ifindex);
 395        if (!indev) {
 396                NL_SET_ERR_MSG_MOD(extack,
 397                                   "Can't find the ingress port to match on");
 398                return -ENOENT;
 399        }
 400
 401        ingress_port = ocelot->ops->netdev_to_port(indev);
 402        if (ingress_port < 0) {
 403                NL_SET_ERR_MSG_MOD(extack,
 404                                   "Can only offload an ocelot ingress port");
 405                return -EOPNOTSUPP;
 406        }
 407        if (ingress_port == port) {
 408                NL_SET_ERR_MSG_MOD(extack,
 409                                   "Ingress port is equal to the egress port");
 410                return -EINVAL;
 411        }
 412
 413        filter->ingress_port.value = ingress_port;
 414        filter->ingress_port.mask = GENMASK(key_length - 1, 0);
 415
 416        return 0;
 417}
 418
 419static int
 420ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress,
 421                        struct flow_cls_offload *f,
 422                        struct ocelot_vcap_filter *filter)
 423{
 424        struct flow_rule *rule = flow_cls_offload_flow_rule(f);
 425        struct flow_dissector *dissector = rule->match.dissector;
 426        struct netlink_ext_ack *extack = f->common.extack;
 427        u16 proto = ntohs(f->common.protocol);
 428        bool match_protocol = true;
 429        int ret;
 430
 431        if (dissector->used_keys &
 432            ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
 433              BIT(FLOW_DISSECTOR_KEY_BASIC) |
 434              BIT(FLOW_DISSECTOR_KEY_META) |
 435              BIT(FLOW_DISSECTOR_KEY_PORTS) |
 436              BIT(FLOW_DISSECTOR_KEY_VLAN) |
 437              BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
 438              BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
 439              BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) {
 440                return -EOPNOTSUPP;
 441        }
 442
 443        /* For VCAP ES0 (egress rewriter) we can match on the ingress port */
 444        if (!ingress) {
 445                ret = ocelot_flower_parse_indev(ocelot, port, f, filter);
 446                if (ret)
 447                        return ret;
 448        }
 449
 450        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
 451                struct flow_match_control match;
 452
 453                flow_rule_match_control(rule, &match);
 454        }
 455
 456        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
 457                struct flow_match_eth_addrs match;
 458
 459                if (filter->block_id == VCAP_ES0) {
 460                        NL_SET_ERR_MSG_MOD(extack,
 461                                           "VCAP ES0 cannot match on MAC address");
 462                        return -EOPNOTSUPP;
 463                }
 464
 465                if (filter->block_id == VCAP_IS1 &&
 466                    !is_zero_ether_addr(match.mask->dst)) {
 467                        NL_SET_ERR_MSG_MOD(extack,
 468                                           "Key type S1_NORMAL cannot match on destination MAC");
 469                        return -EOPNOTSUPP;
 470                }
 471
 472                /* The hw support mac matches only for MAC_ETYPE key,
 473                 * therefore if other matches(port, tcp flags, etc) are added
 474                 * then just bail out
 475                 */
 476                if ((dissector->used_keys &
 477                    (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
 478                     BIT(FLOW_DISSECTOR_KEY_BASIC) |
 479                     BIT(FLOW_DISSECTOR_KEY_CONTROL))) !=
 480                    (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
 481                     BIT(FLOW_DISSECTOR_KEY_BASIC) |
 482                     BIT(FLOW_DISSECTOR_KEY_CONTROL)))
 483                        return -EOPNOTSUPP;
 484
 485                flow_rule_match_eth_addrs(rule, &match);
 486                filter->key_type = OCELOT_VCAP_KEY_ETYPE;
 487                ether_addr_copy(filter->key.etype.dmac.value,
 488                                match.key->dst);
 489                ether_addr_copy(filter->key.etype.smac.value,
 490                                match.key->src);
 491                ether_addr_copy(filter->key.etype.dmac.mask,
 492                                match.mask->dst);
 493                ether_addr_copy(filter->key.etype.smac.mask,
 494                                match.mask->src);
 495                goto finished_key_parsing;
 496        }
 497
 498        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
 499                struct flow_match_basic match;
 500
 501                flow_rule_match_basic(rule, &match);
 502                if (ntohs(match.key->n_proto) == ETH_P_IP) {
 503                        if (filter->block_id == VCAP_ES0) {
 504                                NL_SET_ERR_MSG_MOD(extack,
 505                                                   "VCAP ES0 cannot match on IP protocol");
 506                                return -EOPNOTSUPP;
 507                        }
 508
 509                        filter->key_type = OCELOT_VCAP_KEY_IPV4;
 510                        filter->key.ipv4.proto.value[0] =
 511                                match.key->ip_proto;
 512                        filter->key.ipv4.proto.mask[0] =
 513                                match.mask->ip_proto;
 514                        match_protocol = false;
 515                }
 516                if (ntohs(match.key->n_proto) == ETH_P_IPV6) {
 517                        if (filter->block_id == VCAP_ES0) {
 518                                NL_SET_ERR_MSG_MOD(extack,
 519                                                   "VCAP ES0 cannot match on IP protocol");
 520                                return -EOPNOTSUPP;
 521                        }
 522
 523                        filter->key_type = OCELOT_VCAP_KEY_IPV6;
 524                        filter->key.ipv6.proto.value[0] =
 525                                match.key->ip_proto;
 526                        filter->key.ipv6.proto.mask[0] =
 527                                match.mask->ip_proto;
 528                        match_protocol = false;
 529                }
 530        }
 531
 532        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) &&
 533            proto == ETH_P_IP) {
 534                struct flow_match_ipv4_addrs match;
 535                u8 *tmp;
 536
 537                if (filter->block_id == VCAP_ES0) {
 538                        NL_SET_ERR_MSG_MOD(extack,
 539                                           "VCAP ES0 cannot match on IP address");
 540                        return -EOPNOTSUPP;
 541                }
 542
 543                if (filter->block_id == VCAP_IS1 && *(u32 *)&match.mask->dst) {
 544                        NL_SET_ERR_MSG_MOD(extack,
 545                                           "Key type S1_NORMAL cannot match on destination IP");
 546                        return -EOPNOTSUPP;
 547                }
 548
 549                flow_rule_match_ipv4_addrs(rule, &match);
 550                tmp = &filter->key.ipv4.sip.value.addr[0];
 551                memcpy(tmp, &match.key->src, 4);
 552
 553                tmp = &filter->key.ipv4.sip.mask.addr[0];
 554                memcpy(tmp, &match.mask->src, 4);
 555
 556                tmp = &filter->key.ipv4.dip.value.addr[0];
 557                memcpy(tmp, &match.key->dst, 4);
 558
 559                tmp = &filter->key.ipv4.dip.mask.addr[0];
 560                memcpy(tmp, &match.mask->dst, 4);
 561                match_protocol = false;
 562        }
 563
 564        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) &&
 565            proto == ETH_P_IPV6) {
 566                return -EOPNOTSUPP;
 567        }
 568
 569        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
 570                struct flow_match_ports match;
 571
 572                if (filter->block_id == VCAP_ES0) {
 573                        NL_SET_ERR_MSG_MOD(extack,
 574                                           "VCAP ES0 cannot match on L4 ports");
 575                        return -EOPNOTSUPP;
 576                }
 577
 578                flow_rule_match_ports(rule, &match);
 579                filter->key.ipv4.sport.value = ntohs(match.key->src);
 580                filter->key.ipv4.sport.mask = ntohs(match.mask->src);
 581                filter->key.ipv4.dport.value = ntohs(match.key->dst);
 582                filter->key.ipv4.dport.mask = ntohs(match.mask->dst);
 583                match_protocol = false;
 584        }
 585
 586        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
 587                struct flow_match_vlan match;
 588
 589                flow_rule_match_vlan(rule, &match);
 590                filter->key_type = OCELOT_VCAP_KEY_ANY;
 591                filter->vlan.vid.value = match.key->vlan_id;
 592                filter->vlan.vid.mask = match.mask->vlan_id;
 593                filter->vlan.pcp.value[0] = match.key->vlan_priority;
 594                filter->vlan.pcp.mask[0] = match.mask->vlan_priority;
 595                match_protocol = false;
 596        }
 597
 598finished_key_parsing:
 599        if (match_protocol && proto != ETH_P_ALL) {
 600                if (filter->block_id == VCAP_ES0) {
 601                        NL_SET_ERR_MSG_MOD(extack,
 602                                           "VCAP ES0 cannot match on L2 proto");
 603                        return -EOPNOTSUPP;
 604                }
 605
 606                /* TODO: support SNAP, LLC etc */
 607                if (proto < ETH_P_802_3_MIN)
 608                        return -EOPNOTSUPP;
 609                filter->key_type = OCELOT_VCAP_KEY_ETYPE;
 610                *(__be16 *)filter->key.etype.etype.value = htons(proto);
 611                *(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
 612        }
 613        /* else, a filter of type OCELOT_VCAP_KEY_ANY is implicitly added */
 614
 615        return 0;
 616}
 617
 618static int ocelot_flower_parse(struct ocelot *ocelot, int port, bool ingress,
 619                               struct flow_cls_offload *f,
 620                               struct ocelot_vcap_filter *filter)
 621{
 622        int ret;
 623
 624        filter->prio = f->common.prio;
 625        filter->id = f->cookie;
 626
 627        ret = ocelot_flower_parse_action(ocelot, port, ingress, f, filter);
 628        if (ret)
 629                return ret;
 630
 631        return ocelot_flower_parse_key(ocelot, port, ingress, f, filter);
 632}
 633
 634static struct ocelot_vcap_filter
 635*ocelot_vcap_filter_create(struct ocelot *ocelot, int port, bool ingress,
 636                           struct flow_cls_offload *f)
 637{
 638        struct ocelot_vcap_filter *filter;
 639
 640        filter = kzalloc(sizeof(*filter), GFP_KERNEL);
 641        if (!filter)
 642                return NULL;
 643
 644        if (ingress) {
 645                filter->ingress_port_mask = BIT(port);
 646        } else {
 647                const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
 648                int key_length = vcap->keys[VCAP_ES0_EGR_PORT].length;
 649
 650                filter->egress_port.value = port;
 651                filter->egress_port.mask = GENMASK(key_length - 1, 0);
 652        }
 653
 654        return filter;
 655}
 656
 657static int ocelot_vcap_dummy_filter_add(struct ocelot *ocelot,
 658                                        struct ocelot_vcap_filter *filter)
 659{
 660        list_add(&filter->list, &ocelot->dummy_rules);
 661
 662        return 0;
 663}
 664
 665static int ocelot_vcap_dummy_filter_del(struct ocelot *ocelot,
 666                                        struct ocelot_vcap_filter *filter)
 667{
 668        list_del(&filter->list);
 669        kfree(filter);
 670
 671        return 0;
 672}
 673
 674int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
 675                              struct flow_cls_offload *f, bool ingress)
 676{
 677        struct netlink_ext_ack *extack = f->common.extack;
 678        struct ocelot_vcap_filter *filter;
 679        int chain = f->common.chain_index;
 680        int ret;
 681
 682        if (chain && !ocelot_find_vcap_filter_that_points_at(ocelot, chain)) {
 683                NL_SET_ERR_MSG_MOD(extack, "No default GOTO action points to this chain");
 684                return -EOPNOTSUPP;
 685        }
 686
 687        filter = ocelot_vcap_filter_create(ocelot, port, ingress, f);
 688        if (!filter)
 689                return -ENOMEM;
 690
 691        ret = ocelot_flower_parse(ocelot, port, ingress, f, filter);
 692        if (ret) {
 693                kfree(filter);
 694                return ret;
 695        }
 696
 697        /* The non-optional GOTOs for the TCAM skeleton don't need
 698         * to be actually offloaded.
 699         */
 700        if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
 701                return ocelot_vcap_dummy_filter_add(ocelot, filter);
 702
 703        return ocelot_vcap_filter_add(ocelot, filter, f->common.extack);
 704}
 705EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);
 706
 707int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
 708                              struct flow_cls_offload *f, bool ingress)
 709{
 710        struct ocelot_vcap_filter *filter;
 711        struct ocelot_vcap_block *block;
 712        int block_id;
 713
 714        block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
 715        if (block_id < 0)
 716                return 0;
 717
 718        block = &ocelot->block[block_id];
 719
 720        filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie);
 721        if (!filter)
 722                return 0;
 723
 724        if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
 725                return ocelot_vcap_dummy_filter_del(ocelot, filter);
 726
 727        return ocelot_vcap_filter_del(ocelot, filter);
 728}
 729EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);
 730
 731int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
 732                            struct flow_cls_offload *f, bool ingress)
 733{
 734        struct ocelot_vcap_filter *filter;
 735        struct ocelot_vcap_block *block;
 736        int block_id, ret;
 737
 738        block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
 739        if (block_id < 0)
 740                return 0;
 741
 742        block = &ocelot->block[block_id];
 743
 744        filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie);
 745        if (!filter || filter->type == OCELOT_VCAP_FILTER_DUMMY)
 746                return 0;
 747
 748        ret = ocelot_vcap_filter_stats_update(ocelot, filter);
 749        if (ret)
 750                return ret;
 751
 752        flow_stats_update(&f->stats, 0x0, filter->stats.pkts, 0, 0x0,
 753                          FLOW_ACTION_HW_STATS_IMMEDIATE);
 754        return 0;
 755}
 756EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats);
 757