linux/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-flower.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * DPAA2 Ethernet Switch flower support
   4 *
   5 * Copyright 2021 NXP
   6 *
   7 */
   8
   9#include "dpaa2-switch.h"
  10
  11static int dpaa2_switch_flower_parse_key(struct flow_cls_offload *cls,
  12                                         struct dpsw_acl_key *acl_key)
  13{
  14        struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
  15        struct flow_dissector *dissector = rule->match.dissector;
  16        struct netlink_ext_ack *extack = cls->common.extack;
  17        struct dpsw_acl_fields *acl_h, *acl_m;
  18
  19        if (dissector->used_keys &
  20            ~(BIT(FLOW_DISSECTOR_KEY_BASIC) |
  21              BIT(FLOW_DISSECTOR_KEY_CONTROL) |
  22              BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
  23              BIT(FLOW_DISSECTOR_KEY_VLAN) |
  24              BIT(FLOW_DISSECTOR_KEY_PORTS) |
  25              BIT(FLOW_DISSECTOR_KEY_IP) |
  26              BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
  27              BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS))) {
  28                NL_SET_ERR_MSG_MOD(extack,
  29                                   "Unsupported keys used");
  30                return -EOPNOTSUPP;
  31        }
  32
  33        acl_h = &acl_key->match;
  34        acl_m = &acl_key->mask;
  35
  36        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
  37                struct flow_match_basic match;
  38
  39                flow_rule_match_basic(rule, &match);
  40                acl_h->l3_protocol = match.key->ip_proto;
  41                acl_h->l2_ether_type = be16_to_cpu(match.key->n_proto);
  42                acl_m->l3_protocol = match.mask->ip_proto;
  43                acl_m->l2_ether_type = be16_to_cpu(match.mask->n_proto);
  44        }
  45
  46        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
  47                struct flow_match_eth_addrs match;
  48
  49                flow_rule_match_eth_addrs(rule, &match);
  50                ether_addr_copy(acl_h->l2_dest_mac, &match.key->dst[0]);
  51                ether_addr_copy(acl_h->l2_source_mac, &match.key->src[0]);
  52                ether_addr_copy(acl_m->l2_dest_mac, &match.mask->dst[0]);
  53                ether_addr_copy(acl_m->l2_source_mac, &match.mask->src[0]);
  54        }
  55
  56        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
  57                struct flow_match_vlan match;
  58
  59                flow_rule_match_vlan(rule, &match);
  60                acl_h->l2_vlan_id = match.key->vlan_id;
  61                acl_h->l2_tpid = be16_to_cpu(match.key->vlan_tpid);
  62                acl_h->l2_pcp_dei = match.key->vlan_priority << 1 |
  63                                    match.key->vlan_dei;
  64
  65                acl_m->l2_vlan_id = match.mask->vlan_id;
  66                acl_m->l2_tpid = be16_to_cpu(match.mask->vlan_tpid);
  67                acl_m->l2_pcp_dei = match.mask->vlan_priority << 1 |
  68                                    match.mask->vlan_dei;
  69        }
  70
  71        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
  72                struct flow_match_ipv4_addrs match;
  73
  74                flow_rule_match_ipv4_addrs(rule, &match);
  75                acl_h->l3_source_ip = be32_to_cpu(match.key->src);
  76                acl_h->l3_dest_ip = be32_to_cpu(match.key->dst);
  77                acl_m->l3_source_ip = be32_to_cpu(match.mask->src);
  78                acl_m->l3_dest_ip = be32_to_cpu(match.mask->dst);
  79        }
  80
  81        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
  82                struct flow_match_ports match;
  83
  84                flow_rule_match_ports(rule, &match);
  85                acl_h->l4_source_port = be16_to_cpu(match.key->src);
  86                acl_h->l4_dest_port = be16_to_cpu(match.key->dst);
  87                acl_m->l4_source_port = be16_to_cpu(match.mask->src);
  88                acl_m->l4_dest_port = be16_to_cpu(match.mask->dst);
  89        }
  90
  91        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) {
  92                struct flow_match_ip match;
  93
  94                flow_rule_match_ip(rule, &match);
  95                if (match.mask->ttl != 0) {
  96                        NL_SET_ERR_MSG_MOD(extack,
  97                                           "Matching on TTL not supported");
  98                        return -EOPNOTSUPP;
  99                }
 100
 101                if ((match.mask->tos & 0x3) != 0) {
 102                        NL_SET_ERR_MSG_MOD(extack,
 103                                           "Matching on ECN not supported, only DSCP");
 104                        return -EOPNOTSUPP;
 105                }
 106
 107                acl_h->l3_dscp = match.key->tos >> 2;
 108                acl_m->l3_dscp = match.mask->tos >> 2;
 109        }
 110
 111        return 0;
 112}
 113
 114int dpaa2_switch_acl_entry_add(struct dpaa2_switch_acl_tbl *acl_tbl,
 115                               struct dpaa2_switch_acl_entry *entry)
 116{
 117        struct dpsw_acl_entry_cfg *acl_entry_cfg = &entry->cfg;
 118        struct ethsw_core *ethsw = acl_tbl->ethsw;
 119        struct dpsw_acl_key *acl_key = &entry->key;
 120        struct device *dev = ethsw->dev;
 121        u8 *cmd_buff;
 122        int err;
 123
 124        cmd_buff = kzalloc(DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE, GFP_KERNEL);
 125        if (!cmd_buff)
 126                return -ENOMEM;
 127
 128        dpsw_acl_prepare_entry_cfg(acl_key, cmd_buff);
 129
 130        acl_entry_cfg->key_iova = dma_map_single(dev, cmd_buff,
 131                                                 DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE,
 132                                                 DMA_TO_DEVICE);
 133        if (unlikely(dma_mapping_error(dev, acl_entry_cfg->key_iova))) {
 134                dev_err(dev, "DMA mapping failed\n");
 135                return -EFAULT;
 136        }
 137
 138        err = dpsw_acl_add_entry(ethsw->mc_io, 0, ethsw->dpsw_handle,
 139                                 acl_tbl->id, acl_entry_cfg);
 140
 141        dma_unmap_single(dev, acl_entry_cfg->key_iova, sizeof(cmd_buff),
 142                         DMA_TO_DEVICE);
 143        if (err) {
 144                dev_err(dev, "dpsw_acl_add_entry() failed %d\n", err);
 145                return err;
 146        }
 147
 148        kfree(cmd_buff);
 149
 150        return 0;
 151}
 152
 153static int dpaa2_switch_acl_entry_remove(struct dpaa2_switch_acl_tbl *acl_tbl,
 154                                         struct dpaa2_switch_acl_entry *entry)
 155{
 156        struct dpsw_acl_entry_cfg *acl_entry_cfg = &entry->cfg;
 157        struct dpsw_acl_key *acl_key = &entry->key;
 158        struct ethsw_core *ethsw = acl_tbl->ethsw;
 159        struct device *dev = ethsw->dev;
 160        u8 *cmd_buff;
 161        int err;
 162
 163        cmd_buff = kzalloc(DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE, GFP_KERNEL);
 164        if (!cmd_buff)
 165                return -ENOMEM;
 166
 167        dpsw_acl_prepare_entry_cfg(acl_key, cmd_buff);
 168
 169        acl_entry_cfg->key_iova = dma_map_single(dev, cmd_buff,
 170                                                 DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE,
 171                                                 DMA_TO_DEVICE);
 172        if (unlikely(dma_mapping_error(dev, acl_entry_cfg->key_iova))) {
 173                dev_err(dev, "DMA mapping failed\n");
 174                return -EFAULT;
 175        }
 176
 177        err = dpsw_acl_remove_entry(ethsw->mc_io, 0, ethsw->dpsw_handle,
 178                                    acl_tbl->id, acl_entry_cfg);
 179
 180        dma_unmap_single(dev, acl_entry_cfg->key_iova, sizeof(cmd_buff),
 181                         DMA_TO_DEVICE);
 182        if (err) {
 183                dev_err(dev, "dpsw_acl_remove_entry() failed %d\n", err);
 184                return err;
 185        }
 186
 187        kfree(cmd_buff);
 188
 189        return 0;
 190}
 191
 192static int
 193dpaa2_switch_acl_entry_add_to_list(struct dpaa2_switch_acl_tbl *acl_tbl,
 194                                   struct dpaa2_switch_acl_entry *entry)
 195{
 196        struct dpaa2_switch_acl_entry *tmp;
 197        struct list_head *pos, *n;
 198        int index = 0;
 199
 200        if (list_empty(&acl_tbl->entries)) {
 201                list_add(&entry->list, &acl_tbl->entries);
 202                return index;
 203        }
 204
 205        list_for_each_safe(pos, n, &acl_tbl->entries) {
 206                tmp = list_entry(pos, struct dpaa2_switch_acl_entry, list);
 207                if (entry->prio < tmp->prio)
 208                        break;
 209                index++;
 210        }
 211        list_add(&entry->list, pos->prev);
 212        return index;
 213}
 214
 215static struct dpaa2_switch_acl_entry*
 216dpaa2_switch_acl_entry_get_by_index(struct dpaa2_switch_acl_tbl *acl_tbl,
 217                                    int index)
 218{
 219        struct dpaa2_switch_acl_entry *tmp;
 220        int i = 0;
 221
 222        list_for_each_entry(tmp, &acl_tbl->entries, list) {
 223                if (i == index)
 224                        return tmp;
 225                ++i;
 226        }
 227
 228        return NULL;
 229}
 230
 231static int
 232dpaa2_switch_acl_entry_set_precedence(struct dpaa2_switch_acl_tbl *acl_tbl,
 233                                      struct dpaa2_switch_acl_entry *entry,
 234                                      int precedence)
 235{
 236        int err;
 237
 238        err = dpaa2_switch_acl_entry_remove(acl_tbl, entry);
 239        if (err)
 240                return err;
 241
 242        entry->cfg.precedence = precedence;
 243        return dpaa2_switch_acl_entry_add(acl_tbl, entry);
 244}
 245
 246static int dpaa2_switch_acl_tbl_add_entry(struct dpaa2_switch_acl_tbl *acl_tbl,
 247                                          struct dpaa2_switch_acl_entry *entry)
 248{
 249        struct dpaa2_switch_acl_entry *tmp;
 250        int index, i, precedence, err;
 251
 252        /* Add the new ACL entry to the linked list and get its index */
 253        index = dpaa2_switch_acl_entry_add_to_list(acl_tbl, entry);
 254
 255        /* Move up in priority the ACL entries to make space
 256         * for the new filter.
 257         */
 258        precedence = DPAA2_ETHSW_PORT_MAX_ACL_ENTRIES - acl_tbl->num_rules - 1;
 259        for (i = 0; i < index; i++) {
 260                tmp = dpaa2_switch_acl_entry_get_by_index(acl_tbl, i);
 261
 262                err = dpaa2_switch_acl_entry_set_precedence(acl_tbl, tmp,
 263                                                            precedence);
 264                if (err)
 265                        return err;
 266
 267                precedence++;
 268        }
 269
 270        /* Add the new entry to hardware */
 271        entry->cfg.precedence = precedence;
 272        err = dpaa2_switch_acl_entry_add(acl_tbl, entry);
 273        acl_tbl->num_rules++;
 274
 275        return err;
 276}
 277
 278static struct dpaa2_switch_acl_entry *
 279dpaa2_switch_acl_tbl_find_entry_by_cookie(struct dpaa2_switch_acl_tbl *acl_tbl,
 280                                          unsigned long cookie)
 281{
 282        struct dpaa2_switch_acl_entry *tmp, *n;
 283
 284        list_for_each_entry_safe(tmp, n, &acl_tbl->entries, list) {
 285                if (tmp->cookie == cookie)
 286                        return tmp;
 287        }
 288        return NULL;
 289}
 290
 291static int
 292dpaa2_switch_acl_entry_get_index(struct dpaa2_switch_acl_tbl *acl_tbl,
 293                                 struct dpaa2_switch_acl_entry *entry)
 294{
 295        struct dpaa2_switch_acl_entry *tmp, *n;
 296        int index = 0;
 297
 298        list_for_each_entry_safe(tmp, n, &acl_tbl->entries, list) {
 299                if (tmp->cookie == entry->cookie)
 300                        return index;
 301                index++;
 302        }
 303        return -ENOENT;
 304}
 305
 306static int
 307dpaa2_switch_acl_tbl_remove_entry(struct dpaa2_switch_acl_tbl *acl_tbl,
 308                                  struct dpaa2_switch_acl_entry *entry)
 309{
 310        struct dpaa2_switch_acl_entry *tmp;
 311        int index, i, precedence, err;
 312
 313        index = dpaa2_switch_acl_entry_get_index(acl_tbl, entry);
 314
 315        /* Remove from hardware the ACL entry */
 316        err = dpaa2_switch_acl_entry_remove(acl_tbl, entry);
 317        if (err)
 318                return err;
 319
 320        acl_tbl->num_rules--;
 321
 322        /* Remove it from the list also */
 323        list_del(&entry->list);
 324
 325        /* Move down in priority the entries over the deleted one */
 326        precedence = entry->cfg.precedence;
 327        for (i = index - 1; i >= 0; i--) {
 328                tmp = dpaa2_switch_acl_entry_get_by_index(acl_tbl, i);
 329                err = dpaa2_switch_acl_entry_set_precedence(acl_tbl, tmp,
 330                                                            precedence);
 331                if (err)
 332                        return err;
 333
 334                precedence--;
 335        }
 336
 337        kfree(entry);
 338
 339        return 0;
 340}
 341
 342static int dpaa2_switch_tc_parse_action(struct ethsw_core *ethsw,
 343                                        struct flow_action_entry *cls_act,
 344                                        struct dpsw_acl_result *dpsw_act,
 345                                        struct netlink_ext_ack *extack)
 346{
 347        int err = 0;
 348
 349        switch (cls_act->id) {
 350        case FLOW_ACTION_TRAP:
 351                dpsw_act->action = DPSW_ACL_ACTION_REDIRECT_TO_CTRL_IF;
 352                break;
 353        case FLOW_ACTION_REDIRECT:
 354                if (!dpaa2_switch_port_dev_check(cls_act->dev)) {
 355                        NL_SET_ERR_MSG_MOD(extack,
 356                                           "Destination not a DPAA2 switch port");
 357                        return -EOPNOTSUPP;
 358                }
 359
 360                dpsw_act->if_id = dpaa2_switch_get_index(ethsw, cls_act->dev);
 361                dpsw_act->action = DPSW_ACL_ACTION_REDIRECT;
 362                break;
 363        case FLOW_ACTION_DROP:
 364                dpsw_act->action = DPSW_ACL_ACTION_DROP;
 365                break;
 366        default:
 367                NL_SET_ERR_MSG_MOD(extack,
 368                                   "Action not supported");
 369                err = -EOPNOTSUPP;
 370                goto out;
 371        }
 372
 373out:
 374        return err;
 375}
 376
 377int dpaa2_switch_cls_flower_replace(struct dpaa2_switch_acl_tbl *acl_tbl,
 378                                    struct flow_cls_offload *cls)
 379{
 380        struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
 381        struct netlink_ext_ack *extack = cls->common.extack;
 382        struct ethsw_core *ethsw = acl_tbl->ethsw;
 383        struct dpaa2_switch_acl_entry *acl_entry;
 384        struct flow_action_entry *act;
 385        int err;
 386
 387        if (!flow_offload_has_one_action(&rule->action)) {
 388                NL_SET_ERR_MSG(extack, "Only singular actions are supported");
 389                return -EOPNOTSUPP;
 390        }
 391
 392        if (dpaa2_switch_acl_tbl_is_full(acl_tbl)) {
 393                NL_SET_ERR_MSG(extack, "Maximum filter capacity reached");
 394                return -ENOMEM;
 395        }
 396
 397        acl_entry = kzalloc(sizeof(*acl_entry), GFP_KERNEL);
 398        if (!acl_entry)
 399                return -ENOMEM;
 400
 401        err = dpaa2_switch_flower_parse_key(cls, &acl_entry->key);
 402        if (err)
 403                goto free_acl_entry;
 404
 405        act = &rule->action.entries[0];
 406        err = dpaa2_switch_tc_parse_action(ethsw, act,
 407                                           &acl_entry->cfg.result, extack);
 408        if (err)
 409                goto free_acl_entry;
 410
 411        acl_entry->prio = cls->common.prio;
 412        acl_entry->cookie = cls->cookie;
 413
 414        err = dpaa2_switch_acl_tbl_add_entry(acl_tbl, acl_entry);
 415        if (err)
 416                goto free_acl_entry;
 417
 418        return 0;
 419
 420free_acl_entry:
 421        kfree(acl_entry);
 422
 423        return err;
 424}
 425
 426int dpaa2_switch_cls_flower_destroy(struct dpaa2_switch_acl_tbl *acl_tbl,
 427                                    struct flow_cls_offload *cls)
 428{
 429        struct dpaa2_switch_acl_entry *entry;
 430
 431        entry = dpaa2_switch_acl_tbl_find_entry_by_cookie(acl_tbl, cls->cookie);
 432        if (!entry)
 433                return 0;
 434
 435        return dpaa2_switch_acl_tbl_remove_entry(acl_tbl, entry);
 436}
 437
 438int dpaa2_switch_cls_matchall_replace(struct dpaa2_switch_acl_tbl *acl_tbl,
 439                                      struct tc_cls_matchall_offload *cls)
 440{
 441        struct netlink_ext_ack *extack = cls->common.extack;
 442        struct ethsw_core *ethsw = acl_tbl->ethsw;
 443        struct dpaa2_switch_acl_entry *acl_entry;
 444        struct flow_action_entry *act;
 445        int err;
 446
 447        if (!flow_offload_has_one_action(&cls->rule->action)) {
 448                NL_SET_ERR_MSG(extack, "Only singular actions are supported");
 449                return -EOPNOTSUPP;
 450        }
 451
 452        if (dpaa2_switch_acl_tbl_is_full(acl_tbl)) {
 453                NL_SET_ERR_MSG(extack, "Maximum filter capacity reached");
 454                return -ENOMEM;
 455        }
 456
 457        acl_entry = kzalloc(sizeof(*acl_entry), GFP_KERNEL);
 458        if (!acl_entry)
 459                return -ENOMEM;
 460
 461        act = &cls->rule->action.entries[0];
 462        err = dpaa2_switch_tc_parse_action(ethsw, act,
 463                                           &acl_entry->cfg.result, extack);
 464        if (err)
 465                goto free_acl_entry;
 466
 467        acl_entry->prio = cls->common.prio;
 468        acl_entry->cookie = cls->cookie;
 469
 470        err = dpaa2_switch_acl_tbl_add_entry(acl_tbl, acl_entry);
 471        if (err)
 472                goto free_acl_entry;
 473
 474        return 0;
 475
 476free_acl_entry:
 477        kfree(acl_entry);
 478
 479        return err;
 480}
 481
 482int dpaa2_switch_cls_matchall_destroy(struct dpaa2_switch_acl_tbl *acl_tbl,
 483                                      struct tc_cls_matchall_offload *cls)
 484{
 485        struct dpaa2_switch_acl_entry *entry;
 486
 487        entry = dpaa2_switch_acl_tbl_find_entry_by_cookie(acl_tbl, cls->cookie);
 488        if (!entry)
 489                return 0;
 490
 491        return  dpaa2_switch_acl_tbl_remove_entry(acl_tbl, entry);
 492}
 493