linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
   3
   4#include <linux/kernel.h>
   5#include <linux/errno.h>
   6#include <linux/netdevice.h>
   7#include <net/net_namespace.h>
   8#include <net/flow_dissector.h>
   9#include <net/pkt_cls.h>
  10#include <net/tc_act/tc_gact.h>
  11#include <net/tc_act/tc_mirred.h>
  12#include <net/tc_act/tc_vlan.h>
  13
  14#include "spectrum.h"
  15#include "core_acl_flex_keys.h"
  16
  17static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp,
  18                                         struct mlxsw_sp_acl_block *block,
  19                                         struct mlxsw_sp_acl_rule_info *rulei,
  20                                         struct tcf_exts *exts,
  21                                         struct netlink_ext_ack *extack)
  22{
  23        const struct tc_action *a;
  24        int err, i;
  25
  26        if (!tcf_exts_has_actions(exts))
  27                return 0;
  28
  29        /* Count action is inserted first */
  30        err = mlxsw_sp_acl_rulei_act_count(mlxsw_sp, rulei, extack);
  31        if (err)
  32                return err;
  33
  34        tcf_exts_for_each_action(i, a, exts) {
  35                if (is_tcf_gact_ok(a)) {
  36                        err = mlxsw_sp_acl_rulei_act_terminate(rulei);
  37                        if (err) {
  38                                NL_SET_ERR_MSG_MOD(extack, "Cannot append terminate action");
  39                                return err;
  40                        }
  41                } else if (is_tcf_gact_shot(a)) {
  42                        err = mlxsw_sp_acl_rulei_act_drop(rulei);
  43                        if (err) {
  44                                NL_SET_ERR_MSG_MOD(extack, "Cannot append drop action");
  45                                return err;
  46                        }
  47                } else if (is_tcf_gact_trap(a)) {
  48                        err = mlxsw_sp_acl_rulei_act_trap(rulei);
  49                        if (err) {
  50                                NL_SET_ERR_MSG_MOD(extack, "Cannot append trap action");
  51                                return err;
  52                        }
  53                } else if (is_tcf_gact_goto_chain(a)) {
  54                        u32 chain_index = tcf_gact_goto_chain_index(a);
  55                        struct mlxsw_sp_acl_ruleset *ruleset;
  56                        u16 group_id;
  57
  58                        ruleset = mlxsw_sp_acl_ruleset_lookup(mlxsw_sp, block,
  59                                                              chain_index,
  60                                                              MLXSW_SP_ACL_PROFILE_FLOWER);
  61                        if (IS_ERR(ruleset))
  62                                return PTR_ERR(ruleset);
  63
  64                        group_id = mlxsw_sp_acl_ruleset_group_id(ruleset);
  65                        err = mlxsw_sp_acl_rulei_act_jump(rulei, group_id);
  66                        if (err) {
  67                                NL_SET_ERR_MSG_MOD(extack, "Cannot append jump action");
  68                                return err;
  69                        }
  70                } else if (is_tcf_mirred_egress_redirect(a)) {
  71                        struct net_device *out_dev;
  72                        struct mlxsw_sp_fid *fid;
  73                        u16 fid_index;
  74
  75                        fid = mlxsw_sp_acl_dummy_fid(mlxsw_sp);
  76                        fid_index = mlxsw_sp_fid_index(fid);
  77                        err = mlxsw_sp_acl_rulei_act_fid_set(mlxsw_sp, rulei,
  78                                                             fid_index, extack);
  79                        if (err)
  80                                return err;
  81
  82                        out_dev = tcf_mirred_dev(a);
  83                        err = mlxsw_sp_acl_rulei_act_fwd(mlxsw_sp, rulei,
  84                                                         out_dev, extack);
  85                        if (err)
  86                                return err;
  87                } else if (is_tcf_mirred_egress_mirror(a)) {
  88                        struct net_device *out_dev = tcf_mirred_dev(a);
  89
  90                        err = mlxsw_sp_acl_rulei_act_mirror(mlxsw_sp, rulei,
  91                                                            block, out_dev,
  92                                                            extack);
  93                        if (err)
  94                                return err;
  95                } else if (is_tcf_vlan(a)) {
  96                        u16 proto = be16_to_cpu(tcf_vlan_push_proto(a));
  97                        u32 action = tcf_vlan_action(a);
  98                        u8 prio = tcf_vlan_push_prio(a);
  99                        u16 vid = tcf_vlan_push_vid(a);
 100
 101                        return mlxsw_sp_acl_rulei_act_vlan(mlxsw_sp, rulei,
 102                                                           action, vid,
 103                                                           proto, prio, extack);
 104                } else {
 105                        NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
 106                        dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n");
 107                        return -EOPNOTSUPP;
 108                }
 109        }
 110        return 0;
 111}
 112
 113static void mlxsw_sp_flower_parse_ipv4(struct mlxsw_sp_acl_rule_info *rulei,
 114                                       struct tc_cls_flower_offload *f)
 115{
 116        struct flow_dissector_key_ipv4_addrs *key =
 117                skb_flow_dissector_target(f->dissector,
 118                                          FLOW_DISSECTOR_KEY_IPV4_ADDRS,
 119                                          f->key);
 120        struct flow_dissector_key_ipv4_addrs *mask =
 121                skb_flow_dissector_target(f->dissector,
 122                                          FLOW_DISSECTOR_KEY_IPV4_ADDRS,
 123                                          f->mask);
 124
 125        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
 126                                       (char *) &key->src,
 127                                       (char *) &mask->src, 4);
 128        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
 129                                       (char *) &key->dst,
 130                                       (char *) &mask->dst, 4);
 131}
 132
 133static void mlxsw_sp_flower_parse_ipv6(struct mlxsw_sp_acl_rule_info *rulei,
 134                                       struct tc_cls_flower_offload *f)
 135{
 136        struct flow_dissector_key_ipv6_addrs *key =
 137                skb_flow_dissector_target(f->dissector,
 138                                          FLOW_DISSECTOR_KEY_IPV6_ADDRS,
 139                                          f->key);
 140        struct flow_dissector_key_ipv6_addrs *mask =
 141                skb_flow_dissector_target(f->dissector,
 142                                          FLOW_DISSECTOR_KEY_IPV6_ADDRS,
 143                                          f->mask);
 144
 145        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_96_127,
 146                                       &key->src.s6_addr[0x0],
 147                                       &mask->src.s6_addr[0x0], 4);
 148        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_64_95,
 149                                       &key->src.s6_addr[0x4],
 150                                       &mask->src.s6_addr[0x4], 4);
 151        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_32_63,
 152                                       &key->src.s6_addr[0x8],
 153                                       &mask->src.s6_addr[0x8], 4);
 154        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
 155                                       &key->src.s6_addr[0xC],
 156                                       &mask->src.s6_addr[0xC], 4);
 157        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_96_127,
 158                                       &key->dst.s6_addr[0x0],
 159                                       &mask->dst.s6_addr[0x0], 4);
 160        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_64_95,
 161                                       &key->dst.s6_addr[0x4],
 162                                       &mask->dst.s6_addr[0x4], 4);
 163        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_32_63,
 164                                       &key->dst.s6_addr[0x8],
 165                                       &mask->dst.s6_addr[0x8], 4);
 166        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
 167                                       &key->dst.s6_addr[0xC],
 168                                       &mask->dst.s6_addr[0xC], 4);
 169}
 170
 171static int mlxsw_sp_flower_parse_ports(struct mlxsw_sp *mlxsw_sp,
 172                                       struct mlxsw_sp_acl_rule_info *rulei,
 173                                       struct tc_cls_flower_offload *f,
 174                                       u8 ip_proto)
 175{
 176        struct flow_dissector_key_ports *key, *mask;
 177
 178        if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS))
 179                return 0;
 180
 181        if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) {
 182                NL_SET_ERR_MSG_MOD(f->common.extack, "Only UDP and TCP keys are supported");
 183                dev_err(mlxsw_sp->bus_info->dev, "Only UDP and TCP keys are supported\n");
 184                return -EINVAL;
 185        }
 186
 187        key = skb_flow_dissector_target(f->dissector,
 188                                        FLOW_DISSECTOR_KEY_PORTS,
 189                                        f->key);
 190        mask = skb_flow_dissector_target(f->dissector,
 191                                         FLOW_DISSECTOR_KEY_PORTS,
 192                                         f->mask);
 193        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_DST_L4_PORT,
 194                                       ntohs(key->dst), ntohs(mask->dst));
 195        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_SRC_L4_PORT,
 196                                       ntohs(key->src), ntohs(mask->src));
 197        return 0;
 198}
 199
 200static int mlxsw_sp_flower_parse_tcp(struct mlxsw_sp *mlxsw_sp,
 201                                     struct mlxsw_sp_acl_rule_info *rulei,
 202                                     struct tc_cls_flower_offload *f,
 203                                     u8 ip_proto)
 204{
 205        struct flow_dissector_key_tcp *key, *mask;
 206
 207        if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_TCP))
 208                return 0;
 209
 210        if (ip_proto != IPPROTO_TCP) {
 211                NL_SET_ERR_MSG_MOD(f->common.extack, "TCP keys supported only for TCP");
 212                dev_err(mlxsw_sp->bus_info->dev, "TCP keys supported only for TCP\n");
 213                return -EINVAL;
 214        }
 215
 216        key = skb_flow_dissector_target(f->dissector,
 217                                        FLOW_DISSECTOR_KEY_TCP,
 218                                        f->key);
 219        mask = skb_flow_dissector_target(f->dissector,
 220                                         FLOW_DISSECTOR_KEY_TCP,
 221                                         f->mask);
 222        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_TCP_FLAGS,
 223                                       ntohs(key->flags), ntohs(mask->flags));
 224        return 0;
 225}
 226
 227static int mlxsw_sp_flower_parse_ip(struct mlxsw_sp *mlxsw_sp,
 228                                    struct mlxsw_sp_acl_rule_info *rulei,
 229                                    struct tc_cls_flower_offload *f,
 230                                    u16 n_proto)
 231{
 232        struct flow_dissector_key_ip *key, *mask;
 233
 234        if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_IP))
 235                return 0;
 236
 237        if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6) {
 238                NL_SET_ERR_MSG_MOD(f->common.extack, "IP keys supported only for IPv4/6");
 239                dev_err(mlxsw_sp->bus_info->dev, "IP keys supported only for IPv4/6\n");
 240                return -EINVAL;
 241        }
 242
 243        key = skb_flow_dissector_target(f->dissector,
 244                                        FLOW_DISSECTOR_KEY_IP,
 245                                        f->key);
 246        mask = skb_flow_dissector_target(f->dissector,
 247                                         FLOW_DISSECTOR_KEY_IP,
 248                                         f->mask);
 249        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_TTL_,
 250                                       key->ttl, mask->ttl);
 251
 252        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_ECN,
 253                                       key->tos & 0x3, mask->tos & 0x3);
 254
 255        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_DSCP,
 256                                       key->tos >> 6, mask->tos >> 6);
 257
 258        return 0;
 259}
 260
 261static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp,
 262                                 struct mlxsw_sp_acl_block *block,
 263                                 struct mlxsw_sp_acl_rule_info *rulei,
 264                                 struct tc_cls_flower_offload *f)
 265{
 266        u16 n_proto_mask = 0;
 267        u16 n_proto_key = 0;
 268        u16 addr_type = 0;
 269        u8 ip_proto = 0;
 270        int err;
 271
 272        if (f->dissector->used_keys &
 273            ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
 274              BIT(FLOW_DISSECTOR_KEY_BASIC) |
 275              BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
 276              BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
 277              BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
 278              BIT(FLOW_DISSECTOR_KEY_PORTS) |
 279              BIT(FLOW_DISSECTOR_KEY_TCP) |
 280              BIT(FLOW_DISSECTOR_KEY_IP) |
 281              BIT(FLOW_DISSECTOR_KEY_VLAN))) {
 282                dev_err(mlxsw_sp->bus_info->dev, "Unsupported key\n");
 283                NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported key");
 284                return -EOPNOTSUPP;
 285        }
 286
 287        mlxsw_sp_acl_rulei_priority(rulei, f->common.prio);
 288
 289        if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
 290                struct flow_dissector_key_control *key =
 291                        skb_flow_dissector_target(f->dissector,
 292                                                  FLOW_DISSECTOR_KEY_CONTROL,
 293                                                  f->key);
 294                addr_type = key->addr_type;
 295        }
 296
 297        if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
 298                struct flow_dissector_key_basic *key =
 299                        skb_flow_dissector_target(f->dissector,
 300                                                  FLOW_DISSECTOR_KEY_BASIC,
 301                                                  f->key);
 302                struct flow_dissector_key_basic *mask =
 303                        skb_flow_dissector_target(f->dissector,
 304                                                  FLOW_DISSECTOR_KEY_BASIC,
 305                                                  f->mask);
 306                n_proto_key = ntohs(key->n_proto);
 307                n_proto_mask = ntohs(mask->n_proto);
 308
 309                if (n_proto_key == ETH_P_ALL) {
 310                        n_proto_key = 0;
 311                        n_proto_mask = 0;
 312                }
 313                mlxsw_sp_acl_rulei_keymask_u32(rulei,
 314                                               MLXSW_AFK_ELEMENT_ETHERTYPE,
 315                                               n_proto_key, n_proto_mask);
 316
 317                ip_proto = key->ip_proto;
 318                mlxsw_sp_acl_rulei_keymask_u32(rulei,
 319                                               MLXSW_AFK_ELEMENT_IP_PROTO,
 320                                               key->ip_proto, mask->ip_proto);
 321        }
 322
 323        if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
 324                struct flow_dissector_key_eth_addrs *key =
 325                        skb_flow_dissector_target(f->dissector,
 326                                                  FLOW_DISSECTOR_KEY_ETH_ADDRS,
 327                                                  f->key);
 328                struct flow_dissector_key_eth_addrs *mask =
 329                        skb_flow_dissector_target(f->dissector,
 330                                                  FLOW_DISSECTOR_KEY_ETH_ADDRS,
 331                                                  f->mask);
 332
 333                mlxsw_sp_acl_rulei_keymask_buf(rulei,
 334                                               MLXSW_AFK_ELEMENT_DMAC_32_47,
 335                                               key->dst, mask->dst, 2);
 336                mlxsw_sp_acl_rulei_keymask_buf(rulei,
 337                                               MLXSW_AFK_ELEMENT_DMAC_0_31,
 338                                               key->dst + 2, mask->dst + 2, 4);
 339                mlxsw_sp_acl_rulei_keymask_buf(rulei,
 340                                               MLXSW_AFK_ELEMENT_SMAC_32_47,
 341                                               key->src, mask->src, 2);
 342                mlxsw_sp_acl_rulei_keymask_buf(rulei,
 343                                               MLXSW_AFK_ELEMENT_SMAC_0_31,
 344                                               key->src + 2, mask->src + 2, 4);
 345        }
 346
 347        if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
 348                struct flow_dissector_key_vlan *key =
 349                        skb_flow_dissector_target(f->dissector,
 350                                                  FLOW_DISSECTOR_KEY_VLAN,
 351                                                  f->key);
 352                struct flow_dissector_key_vlan *mask =
 353                        skb_flow_dissector_target(f->dissector,
 354                                                  FLOW_DISSECTOR_KEY_VLAN,
 355                                                  f->mask);
 356
 357                if (mlxsw_sp_acl_block_is_egress_bound(block)) {
 358                        NL_SET_ERR_MSG_MOD(f->common.extack, "vlan_id key is not supported on egress");
 359                        return -EOPNOTSUPP;
 360                }
 361                if (mask->vlan_id != 0)
 362                        mlxsw_sp_acl_rulei_keymask_u32(rulei,
 363                                                       MLXSW_AFK_ELEMENT_VID,
 364                                                       key->vlan_id,
 365                                                       mask->vlan_id);
 366                if (mask->vlan_priority != 0)
 367                        mlxsw_sp_acl_rulei_keymask_u32(rulei,
 368                                                       MLXSW_AFK_ELEMENT_PCP,
 369                                                       key->vlan_priority,
 370                                                       mask->vlan_priority);
 371        }
 372
 373        if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS)
 374                mlxsw_sp_flower_parse_ipv4(rulei, f);
 375
 376        if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS)
 377                mlxsw_sp_flower_parse_ipv6(rulei, f);
 378
 379        err = mlxsw_sp_flower_parse_ports(mlxsw_sp, rulei, f, ip_proto);
 380        if (err)
 381                return err;
 382        err = mlxsw_sp_flower_parse_tcp(mlxsw_sp, rulei, f, ip_proto);
 383        if (err)
 384                return err;
 385
 386        err = mlxsw_sp_flower_parse_ip(mlxsw_sp, rulei, f, n_proto_key & n_proto_mask);
 387        if (err)
 388                return err;
 389
 390        return mlxsw_sp_flower_parse_actions(mlxsw_sp, block, rulei, f->exts,
 391                                             f->common.extack);
 392}
 393
 394int mlxsw_sp_flower_replace(struct mlxsw_sp *mlxsw_sp,
 395                            struct mlxsw_sp_acl_block *block,
 396                            struct tc_cls_flower_offload *f)
 397{
 398        struct mlxsw_sp_acl_rule_info *rulei;
 399        struct mlxsw_sp_acl_ruleset *ruleset;
 400        struct mlxsw_sp_acl_rule *rule;
 401        int err;
 402
 403        ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
 404                                           f->common.chain_index,
 405                                           MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
 406        if (IS_ERR(ruleset))
 407                return PTR_ERR(ruleset);
 408
 409        rule = mlxsw_sp_acl_rule_create(mlxsw_sp, ruleset, f->cookie,
 410                                        f->common.extack);
 411        if (IS_ERR(rule)) {
 412                err = PTR_ERR(rule);
 413                goto err_rule_create;
 414        }
 415
 416        rulei = mlxsw_sp_acl_rule_rulei(rule);
 417        err = mlxsw_sp_flower_parse(mlxsw_sp, block, rulei, f);
 418        if (err)
 419                goto err_flower_parse;
 420
 421        err = mlxsw_sp_acl_rulei_commit(rulei);
 422        if (err)
 423                goto err_rulei_commit;
 424
 425        err = mlxsw_sp_acl_rule_add(mlxsw_sp, rule);
 426        if (err)
 427                goto err_rule_add;
 428
 429        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 430        return 0;
 431
 432err_rule_add:
 433err_rulei_commit:
 434err_flower_parse:
 435        mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
 436err_rule_create:
 437        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 438        return err;
 439}
 440
 441void mlxsw_sp_flower_destroy(struct mlxsw_sp *mlxsw_sp,
 442                             struct mlxsw_sp_acl_block *block,
 443                             struct tc_cls_flower_offload *f)
 444{
 445        struct mlxsw_sp_acl_ruleset *ruleset;
 446        struct mlxsw_sp_acl_rule *rule;
 447
 448        ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
 449                                           f->common.chain_index,
 450                                           MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
 451        if (IS_ERR(ruleset))
 452                return;
 453
 454        rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie);
 455        if (rule) {
 456                mlxsw_sp_acl_rule_del(mlxsw_sp, rule);
 457                mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
 458        }
 459
 460        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 461}
 462
 463int mlxsw_sp_flower_stats(struct mlxsw_sp *mlxsw_sp,
 464                          struct mlxsw_sp_acl_block *block,
 465                          struct tc_cls_flower_offload *f)
 466{
 467        struct mlxsw_sp_acl_ruleset *ruleset;
 468        struct mlxsw_sp_acl_rule *rule;
 469        u64 packets;
 470        u64 lastuse;
 471        u64 bytes;
 472        int err;
 473
 474        ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
 475                                           f->common.chain_index,
 476                                           MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
 477        if (WARN_ON(IS_ERR(ruleset)))
 478                return -EINVAL;
 479
 480        rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie);
 481        if (!rule)
 482                return -EINVAL;
 483
 484        err = mlxsw_sp_acl_rule_get_stats(mlxsw_sp, rule, &packets, &bytes,
 485                                          &lastuse);
 486        if (err)
 487                goto err_rule_get_stats;
 488
 489        tcf_exts_stats_update(f->exts, bytes, packets, lastuse);
 490
 491        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 492        return 0;
 493
 494err_rule_get_stats:
 495        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 496        return err;
 497}
 498
 499int mlxsw_sp_flower_tmplt_create(struct mlxsw_sp *mlxsw_sp,
 500                                 struct mlxsw_sp_acl_block *block,
 501                                 struct tc_cls_flower_offload *f)
 502{
 503        struct mlxsw_sp_acl_ruleset *ruleset;
 504        struct mlxsw_sp_acl_rule_info rulei;
 505        int err;
 506
 507        memset(&rulei, 0, sizeof(rulei));
 508        err = mlxsw_sp_flower_parse(mlxsw_sp, block, &rulei, f);
 509        if (err)
 510                return err;
 511        ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
 512                                           f->common.chain_index,
 513                                           MLXSW_SP_ACL_PROFILE_FLOWER,
 514                                           &rulei.values.elusage);
 515
 516        /* keep the reference to the ruleset */
 517        return PTR_ERR_OR_ZERO(ruleset);
 518}
 519
 520void mlxsw_sp_flower_tmplt_destroy(struct mlxsw_sp *mlxsw_sp,
 521                                   struct mlxsw_sp_acl_block *block,
 522                                   struct tc_cls_flower_offload *f)
 523{
 524        struct mlxsw_sp_acl_ruleset *ruleset;
 525
 526        ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
 527                                           f->common.chain_index,
 528                                           MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
 529        if (IS_ERR(ruleset))
 530                return;
 531        /* put the reference to the ruleset kept in create */
 532        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 533        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 534}
 535