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                        if (a->police.rate_pkt_ps) {
 224                                NL_SET_ERR_MSG_MOD(extack,
 225                                                   "QoS offload not support packets per second");
 226                                return -EOPNOTSUPP;
 227                        }
 228                        filter->action.police_ena = true;
 229                        rate = a->police.rate_bytes_ps;
 230                        filter->action.pol.rate = div_u64(rate, 1000) * 8;
 231                        filter->action.pol.burst = a->police.burst;
 232                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 233                        break;
 234                case FLOW_ACTION_REDIRECT:
 235                        if (filter->block_id != VCAP_IS2) {
 236                                NL_SET_ERR_MSG_MOD(extack,
 237                                                   "Redirect action can only be offloaded to VCAP IS2");
 238                                return -EOPNOTSUPP;
 239                        }
 240                        if (filter->goto_target != -1) {
 241                                NL_SET_ERR_MSG_MOD(extack,
 242                                                   "Last action must be GOTO");
 243                                return -EOPNOTSUPP;
 244                        }
 245                        egress_port = ocelot->ops->netdev_to_port(a->dev);
 246                        if (egress_port < 0) {
 247                                NL_SET_ERR_MSG_MOD(extack,
 248                                                   "Destination not an ocelot port");
 249                                return -EOPNOTSUPP;
 250                        }
 251                        filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
 252                        filter->action.port_mask = BIT(egress_port);
 253                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 254                        break;
 255                case FLOW_ACTION_VLAN_POP:
 256                        if (filter->block_id != VCAP_IS1) {
 257                                NL_SET_ERR_MSG_MOD(extack,
 258                                                   "VLAN pop action can only be offloaded to VCAP IS1");
 259                                return -EOPNOTSUPP;
 260                        }
 261                        if (filter->goto_target != -1) {
 262                                NL_SET_ERR_MSG_MOD(extack,
 263                                                   "Last action must be GOTO");
 264                                return -EOPNOTSUPP;
 265                        }
 266                        filter->action.vlan_pop_cnt_ena = true;
 267                        filter->action.vlan_pop_cnt++;
 268                        if (filter->action.vlan_pop_cnt > 2) {
 269                                NL_SET_ERR_MSG_MOD(extack,
 270                                                   "Cannot pop more than 2 VLAN headers");
 271                                return -EOPNOTSUPP;
 272                        }
 273                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 274                        break;
 275                case FLOW_ACTION_VLAN_MANGLE:
 276                        if (filter->block_id != VCAP_IS1) {
 277                                NL_SET_ERR_MSG_MOD(extack,
 278                                                   "VLAN modify action can only be offloaded to VCAP IS1");
 279                                return -EOPNOTSUPP;
 280                        }
 281                        if (filter->goto_target != -1) {
 282                                NL_SET_ERR_MSG_MOD(extack,
 283                                                   "Last action must be GOTO");
 284                                return -EOPNOTSUPP;
 285                        }
 286                        if (!ocelot_port->vlan_aware) {
 287                                NL_SET_ERR_MSG_MOD(extack,
 288                                                   "Can only modify VLAN under VLAN aware bridge");
 289                                return -EOPNOTSUPP;
 290                        }
 291                        filter->action.vid_replace_ena = true;
 292                        filter->action.pcp_dei_ena = true;
 293                        filter->action.vid = a->vlan.vid;
 294                        filter->action.pcp = a->vlan.prio;
 295                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 296                        break;
 297                case FLOW_ACTION_PRIORITY:
 298                        if (filter->block_id != VCAP_IS1) {
 299                                NL_SET_ERR_MSG_MOD(extack,
 300                                                   "Priority action can only be offloaded to VCAP IS1");
 301                                return -EOPNOTSUPP;
 302                        }
 303                        if (filter->goto_target != -1) {
 304                                NL_SET_ERR_MSG_MOD(extack,
 305                                                   "Last action must be GOTO");
 306                                return -EOPNOTSUPP;
 307                        }
 308                        filter->action.qos_ena = true;
 309                        filter->action.qos_val = a->priority;
 310                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 311                        break;
 312                case FLOW_ACTION_GOTO:
 313                        filter->goto_target = a->chain_index;
 314
 315                        if (filter->block_id == VCAP_IS1 && filter->lookup == 2) {
 316                                int pag = ocelot_chain_to_pag(filter->goto_target);
 317
 318                                filter->action.pag_override_mask = 0xff;
 319                                filter->action.pag_val = pag;
 320                                filter->type = OCELOT_VCAP_FILTER_PAG;
 321                        }
 322                        break;
 323                case FLOW_ACTION_VLAN_PUSH:
 324                        if (filter->block_id != VCAP_ES0) {
 325                                NL_SET_ERR_MSG_MOD(extack,
 326                                                   "VLAN push action can only be offloaded to VCAP ES0");
 327                                return -EOPNOTSUPP;
 328                        }
 329                        switch (ntohs(a->vlan.proto)) {
 330                        case ETH_P_8021Q:
 331                                tpid = OCELOT_TAG_TPID_SEL_8021Q;
 332                                break;
 333                        case ETH_P_8021AD:
 334                                tpid = OCELOT_TAG_TPID_SEL_8021AD;
 335                                break;
 336                        default:
 337                                NL_SET_ERR_MSG_MOD(extack,
 338                                                   "Cannot push custom TPID");
 339                                return -EOPNOTSUPP;
 340                        }
 341                        filter->action.tag_a_tpid_sel = tpid;
 342                        filter->action.push_outer_tag = OCELOT_ES0_TAG;
 343                        filter->action.tag_a_vid_sel = 1;
 344                        filter->action.vid_a_val = a->vlan.vid;
 345                        filter->action.pcp_a_val = a->vlan.prio;
 346                        filter->type = OCELOT_VCAP_FILTER_OFFLOAD;
 347                        break;
 348                default:
 349                        NL_SET_ERR_MSG_MOD(extack, "Cannot offload action");
 350                        return -EOPNOTSUPP;
 351                }
 352        }
 353
 354        if (filter->goto_target == -1) {
 355                if ((filter->block_id == VCAP_IS2 && filter->lookup == 1) ||
 356                    chain == 0) {
 357                        allow_missing_goto_target = true;
 358                } else {
 359                        NL_SET_ERR_MSG_MOD(extack, "Missing GOTO action");
 360                        return -EOPNOTSUPP;
 361                }
 362        }
 363
 364        if (!ocelot_is_goto_target_valid(filter->goto_target, chain, ingress) &&
 365            !allow_missing_goto_target) {
 366                NL_SET_ERR_MSG_MOD(extack, "Cannot offload this GOTO target");
 367                return -EOPNOTSUPP;
 368        }
 369
 370        return 0;
 371}
 372
 373static int ocelot_flower_parse_indev(struct ocelot *ocelot, int port,
 374                                     struct flow_cls_offload *f,
 375                                     struct ocelot_vcap_filter *filter)
 376{
 377        struct flow_rule *rule = flow_cls_offload_flow_rule(f);
 378        const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
 379        int key_length = vcap->keys[VCAP_ES0_IGR_PORT].length;
 380        struct netlink_ext_ack *extack = f->common.extack;
 381        struct net_device *dev, *indev;
 382        struct flow_match_meta match;
 383        int ingress_port;
 384
 385        flow_rule_match_meta(rule, &match);
 386
 387        if (!match.mask->ingress_ifindex)
 388                return 0;
 389
 390        if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
 391                NL_SET_ERR_MSG_MOD(extack, "Unsupported ingress ifindex mask");
 392                return -EOPNOTSUPP;
 393        }
 394
 395        dev = ocelot->ops->port_to_netdev(ocelot, port);
 396        if (!dev)
 397                return -EINVAL;
 398
 399        indev = __dev_get_by_index(dev_net(dev), match.key->ingress_ifindex);
 400        if (!indev) {
 401                NL_SET_ERR_MSG_MOD(extack,
 402                                   "Can't find the ingress port to match on");
 403                return -ENOENT;
 404        }
 405
 406        ingress_port = ocelot->ops->netdev_to_port(indev);
 407        if (ingress_port < 0) {
 408                NL_SET_ERR_MSG_MOD(extack,
 409                                   "Can only offload an ocelot ingress port");
 410                return -EOPNOTSUPP;
 411        }
 412        if (ingress_port == port) {
 413                NL_SET_ERR_MSG_MOD(extack,
 414                                   "Ingress port is equal to the egress port");
 415                return -EINVAL;
 416        }
 417
 418        filter->ingress_port.value = ingress_port;
 419        filter->ingress_port.mask = GENMASK(key_length - 1, 0);
 420
 421        return 0;
 422}
 423
 424static int
 425ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress,
 426                        struct flow_cls_offload *f,
 427                        struct ocelot_vcap_filter *filter)
 428{
 429        struct flow_rule *rule = flow_cls_offload_flow_rule(f);
 430        struct flow_dissector *dissector = rule->match.dissector;
 431        struct netlink_ext_ack *extack = f->common.extack;
 432        u16 proto = ntohs(f->common.protocol);
 433        bool match_protocol = true;
 434        int ret;
 435
 436        if (dissector->used_keys &
 437            ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
 438              BIT(FLOW_DISSECTOR_KEY_BASIC) |
 439              BIT(FLOW_DISSECTOR_KEY_META) |
 440              BIT(FLOW_DISSECTOR_KEY_PORTS) |
 441              BIT(FLOW_DISSECTOR_KEY_VLAN) |
 442              BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
 443              BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
 444              BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS))) {
 445                return -EOPNOTSUPP;
 446        }
 447
 448        /* For VCAP ES0 (egress rewriter) we can match on the ingress port */
 449        if (!ingress) {
 450                ret = ocelot_flower_parse_indev(ocelot, port, f, filter);
 451                if (ret)
 452                        return ret;
 453        }
 454
 455        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
 456                struct flow_match_control match;
 457
 458                flow_rule_match_control(rule, &match);
 459        }
 460
 461        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
 462                struct flow_match_eth_addrs match;
 463
 464                if (filter->block_id == VCAP_ES0) {
 465                        NL_SET_ERR_MSG_MOD(extack,
 466                                           "VCAP ES0 cannot match on MAC address");
 467                        return -EOPNOTSUPP;
 468                }
 469
 470                if (filter->block_id == VCAP_IS1 &&
 471                    !is_zero_ether_addr(match.mask->dst)) {
 472                        NL_SET_ERR_MSG_MOD(extack,
 473                                           "Key type S1_NORMAL cannot match on destination MAC");
 474                        return -EOPNOTSUPP;
 475                }
 476
 477                /* The hw support mac matches only for MAC_ETYPE key,
 478                 * therefore if other matches(port, tcp flags, etc) are added
 479                 * then just bail out
 480                 */
 481                if ((dissector->used_keys &
 482                    (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
 483                     BIT(FLOW_DISSECTOR_KEY_BASIC) |
 484                     BIT(FLOW_DISSECTOR_KEY_CONTROL))) !=
 485                    (BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
 486                     BIT(FLOW_DISSECTOR_KEY_BASIC) |
 487                     BIT(FLOW_DISSECTOR_KEY_CONTROL)))
 488                        return -EOPNOTSUPP;
 489
 490                flow_rule_match_eth_addrs(rule, &match);
 491                filter->key_type = OCELOT_VCAP_KEY_ETYPE;
 492                ether_addr_copy(filter->key.etype.dmac.value,
 493                                match.key->dst);
 494                ether_addr_copy(filter->key.etype.smac.value,
 495                                match.key->src);
 496                ether_addr_copy(filter->key.etype.dmac.mask,
 497                                match.mask->dst);
 498                ether_addr_copy(filter->key.etype.smac.mask,
 499                                match.mask->src);
 500                goto finished_key_parsing;
 501        }
 502
 503        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
 504                struct flow_match_basic match;
 505
 506                flow_rule_match_basic(rule, &match);
 507                if (ntohs(match.key->n_proto) == ETH_P_IP) {
 508                        if (filter->block_id == VCAP_ES0) {
 509                                NL_SET_ERR_MSG_MOD(extack,
 510                                                   "VCAP ES0 cannot match on IP protocol");
 511                                return -EOPNOTSUPP;
 512                        }
 513
 514                        filter->key_type = OCELOT_VCAP_KEY_IPV4;
 515                        filter->key.ipv4.proto.value[0] =
 516                                match.key->ip_proto;
 517                        filter->key.ipv4.proto.mask[0] =
 518                                match.mask->ip_proto;
 519                        match_protocol = false;
 520                }
 521                if (ntohs(match.key->n_proto) == ETH_P_IPV6) {
 522                        if (filter->block_id == VCAP_ES0) {
 523                                NL_SET_ERR_MSG_MOD(extack,
 524                                                   "VCAP ES0 cannot match on IP protocol");
 525                                return -EOPNOTSUPP;
 526                        }
 527
 528                        filter->key_type = OCELOT_VCAP_KEY_IPV6;
 529                        filter->key.ipv6.proto.value[0] =
 530                                match.key->ip_proto;
 531                        filter->key.ipv6.proto.mask[0] =
 532                                match.mask->ip_proto;
 533                        match_protocol = false;
 534                }
 535        }
 536
 537        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS) &&
 538            proto == ETH_P_IP) {
 539                struct flow_match_ipv4_addrs match;
 540                u8 *tmp;
 541
 542                if (filter->block_id == VCAP_ES0) {
 543                        NL_SET_ERR_MSG_MOD(extack,
 544                                           "VCAP ES0 cannot match on IP address");
 545                        return -EOPNOTSUPP;
 546                }
 547
 548                flow_rule_match_ipv4_addrs(rule, &match);
 549
 550                if (filter->block_id == VCAP_IS1 && *(u32 *)&match.mask->dst) {
 551                        NL_SET_ERR_MSG_MOD(extack,
 552                                           "Key type S1_NORMAL cannot match on destination IP");
 553                        return -EOPNOTSUPP;
 554                }
 555
 556                tmp = &filter->key.ipv4.sip.value.addr[0];
 557                memcpy(tmp, &match.key->src, 4);
 558
 559                tmp = &filter->key.ipv4.sip.mask.addr[0];
 560                memcpy(tmp, &match.mask->src, 4);
 561
 562                tmp = &filter->key.ipv4.dip.value.addr[0];
 563                memcpy(tmp, &match.key->dst, 4);
 564
 565                tmp = &filter->key.ipv4.dip.mask.addr[0];
 566                memcpy(tmp, &match.mask->dst, 4);
 567                match_protocol = false;
 568        }
 569
 570        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS) &&
 571            proto == ETH_P_IPV6) {
 572                return -EOPNOTSUPP;
 573        }
 574
 575        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
 576                struct flow_match_ports match;
 577
 578                if (filter->block_id == VCAP_ES0) {
 579                        NL_SET_ERR_MSG_MOD(extack,
 580                                           "VCAP ES0 cannot match on L4 ports");
 581                        return -EOPNOTSUPP;
 582                }
 583
 584                flow_rule_match_ports(rule, &match);
 585                filter->key.ipv4.sport.value = ntohs(match.key->src);
 586                filter->key.ipv4.sport.mask = ntohs(match.mask->src);
 587                filter->key.ipv4.dport.value = ntohs(match.key->dst);
 588                filter->key.ipv4.dport.mask = ntohs(match.mask->dst);
 589                match_protocol = false;
 590        }
 591
 592        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
 593                struct flow_match_vlan match;
 594
 595                flow_rule_match_vlan(rule, &match);
 596                filter->key_type = OCELOT_VCAP_KEY_ANY;
 597                filter->vlan.vid.value = match.key->vlan_id;
 598                filter->vlan.vid.mask = match.mask->vlan_id;
 599                filter->vlan.pcp.value[0] = match.key->vlan_priority;
 600                filter->vlan.pcp.mask[0] = match.mask->vlan_priority;
 601                match_protocol = false;
 602        }
 603
 604finished_key_parsing:
 605        if (match_protocol && proto != ETH_P_ALL) {
 606                if (filter->block_id == VCAP_ES0) {
 607                        NL_SET_ERR_MSG_MOD(extack,
 608                                           "VCAP ES0 cannot match on L2 proto");
 609                        return -EOPNOTSUPP;
 610                }
 611
 612                /* TODO: support SNAP, LLC etc */
 613                if (proto < ETH_P_802_3_MIN)
 614                        return -EOPNOTSUPP;
 615                filter->key_type = OCELOT_VCAP_KEY_ETYPE;
 616                *(__be16 *)filter->key.etype.etype.value = htons(proto);
 617                *(__be16 *)filter->key.etype.etype.mask = htons(0xffff);
 618        }
 619        /* else, a filter of type OCELOT_VCAP_KEY_ANY is implicitly added */
 620
 621        return 0;
 622}
 623
 624static int ocelot_flower_parse(struct ocelot *ocelot, int port, bool ingress,
 625                               struct flow_cls_offload *f,
 626                               struct ocelot_vcap_filter *filter)
 627{
 628        int ret;
 629
 630        filter->prio = f->common.prio;
 631        filter->id.cookie = f->cookie;
 632        filter->id.tc_offload = true;
 633
 634        ret = ocelot_flower_parse_action(ocelot, port, ingress, f, filter);
 635        if (ret)
 636                return ret;
 637
 638        return ocelot_flower_parse_key(ocelot, port, ingress, f, filter);
 639}
 640
 641static struct ocelot_vcap_filter
 642*ocelot_vcap_filter_create(struct ocelot *ocelot, int port, bool ingress,
 643                           struct flow_cls_offload *f)
 644{
 645        struct ocelot_vcap_filter *filter;
 646
 647        filter = kzalloc(sizeof(*filter), GFP_KERNEL);
 648        if (!filter)
 649                return NULL;
 650
 651        if (ingress) {
 652                filter->ingress_port_mask = BIT(port);
 653        } else {
 654                const struct vcap_props *vcap = &ocelot->vcap[VCAP_ES0];
 655                int key_length = vcap->keys[VCAP_ES0_EGR_PORT].length;
 656
 657                filter->egress_port.value = port;
 658                filter->egress_port.mask = GENMASK(key_length - 1, 0);
 659        }
 660
 661        return filter;
 662}
 663
 664static int ocelot_vcap_dummy_filter_add(struct ocelot *ocelot,
 665                                        struct ocelot_vcap_filter *filter)
 666{
 667        list_add(&filter->list, &ocelot->dummy_rules);
 668
 669        return 0;
 670}
 671
 672static int ocelot_vcap_dummy_filter_del(struct ocelot *ocelot,
 673                                        struct ocelot_vcap_filter *filter)
 674{
 675        list_del(&filter->list);
 676        kfree(filter);
 677
 678        return 0;
 679}
 680
 681int ocelot_cls_flower_replace(struct ocelot *ocelot, int port,
 682                              struct flow_cls_offload *f, bool ingress)
 683{
 684        struct netlink_ext_ack *extack = f->common.extack;
 685        struct ocelot_vcap_filter *filter;
 686        int chain = f->common.chain_index;
 687        int ret;
 688
 689        if (chain && !ocelot_find_vcap_filter_that_points_at(ocelot, chain)) {
 690                NL_SET_ERR_MSG_MOD(extack, "No default GOTO action points to this chain");
 691                return -EOPNOTSUPP;
 692        }
 693
 694        filter = ocelot_vcap_filter_create(ocelot, port, ingress, f);
 695        if (!filter)
 696                return -ENOMEM;
 697
 698        ret = ocelot_flower_parse(ocelot, port, ingress, f, filter);
 699        if (ret) {
 700                kfree(filter);
 701                return ret;
 702        }
 703
 704        /* The non-optional GOTOs for the TCAM skeleton don't need
 705         * to be actually offloaded.
 706         */
 707        if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
 708                return ocelot_vcap_dummy_filter_add(ocelot, filter);
 709
 710        return ocelot_vcap_filter_add(ocelot, filter, f->common.extack);
 711}
 712EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace);
 713
 714int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port,
 715                              struct flow_cls_offload *f, bool ingress)
 716{
 717        struct ocelot_vcap_filter *filter;
 718        struct ocelot_vcap_block *block;
 719        int block_id;
 720
 721        block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
 722        if (block_id < 0)
 723                return 0;
 724
 725        block = &ocelot->block[block_id];
 726
 727        filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie, true);
 728        if (!filter)
 729                return 0;
 730
 731        if (filter->type == OCELOT_VCAP_FILTER_DUMMY)
 732                return ocelot_vcap_dummy_filter_del(ocelot, filter);
 733
 734        return ocelot_vcap_filter_del(ocelot, filter);
 735}
 736EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy);
 737
 738int ocelot_cls_flower_stats(struct ocelot *ocelot, int port,
 739                            struct flow_cls_offload *f, bool ingress)
 740{
 741        struct ocelot_vcap_filter *filter;
 742        struct ocelot_vcap_block *block;
 743        int block_id, ret;
 744
 745        block_id = ocelot_chain_to_block(f->common.chain_index, ingress);
 746        if (block_id < 0)
 747                return 0;
 748
 749        block = &ocelot->block[block_id];
 750
 751        filter = ocelot_vcap_block_find_filter_by_id(block, f->cookie, true);
 752        if (!filter || filter->type == OCELOT_VCAP_FILTER_DUMMY)
 753                return 0;
 754
 755        ret = ocelot_vcap_filter_stats_update(ocelot, filter);
 756        if (ret)
 757                return ret;
 758
 759        flow_stats_update(&f->stats, 0x0, filter->stats.pkts, 0, 0x0,
 760                          FLOW_ACTION_HW_STATS_IMMEDIATE);
 761        return 0;
 762}
 763EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats);
 764