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 flow_action *flow_action,
  21                                         struct netlink_ext_ack *extack)
  22{
  23        const struct flow_action_entry *act;
  24        int mirror_act_count = 0;
  25        int err, i;
  26
  27        if (!flow_action_has_entries(flow_action))
  28                return 0;
  29
  30        /* Count action is inserted first */
  31        err = mlxsw_sp_acl_rulei_act_count(mlxsw_sp, rulei, extack);
  32        if (err)
  33                return err;
  34
  35        flow_action_for_each(i, act, flow_action) {
  36                switch (act->id) {
  37                case FLOW_ACTION_ACCEPT:
  38                        err = mlxsw_sp_acl_rulei_act_terminate(rulei);
  39                        if (err) {
  40                                NL_SET_ERR_MSG_MOD(extack, "Cannot append terminate action");
  41                                return err;
  42                        }
  43                        break;
  44                case FLOW_ACTION_DROP:
  45                        err = mlxsw_sp_acl_rulei_act_drop(rulei);
  46                        if (err) {
  47                                NL_SET_ERR_MSG_MOD(extack, "Cannot append drop action");
  48                                return err;
  49                        }
  50                        break;
  51                case FLOW_ACTION_TRAP:
  52                        err = mlxsw_sp_acl_rulei_act_trap(rulei);
  53                        if (err) {
  54                                NL_SET_ERR_MSG_MOD(extack, "Cannot append trap action");
  55                                return err;
  56                        }
  57                        break;
  58                case FLOW_ACTION_GOTO: {
  59                        u32 chain_index = act->chain_index;
  60                        struct mlxsw_sp_acl_ruleset *ruleset;
  61                        u16 group_id;
  62
  63                        ruleset = mlxsw_sp_acl_ruleset_lookup(mlxsw_sp, block,
  64                                                              chain_index,
  65                                                              MLXSW_SP_ACL_PROFILE_FLOWER);
  66                        if (IS_ERR(ruleset))
  67                                return PTR_ERR(ruleset);
  68
  69                        group_id = mlxsw_sp_acl_ruleset_group_id(ruleset);
  70                        err = mlxsw_sp_acl_rulei_act_jump(rulei, group_id);
  71                        if (err) {
  72                                NL_SET_ERR_MSG_MOD(extack, "Cannot append jump action");
  73                                return err;
  74                        }
  75                        }
  76                        break;
  77                case FLOW_ACTION_REDIRECT: {
  78                        struct net_device *out_dev;
  79                        struct mlxsw_sp_fid *fid;
  80                        u16 fid_index;
  81
  82                        if (mlxsw_sp_acl_block_is_egress_bound(block)) {
  83                                NL_SET_ERR_MSG_MOD(extack, "Redirect action is not supported on egress");
  84                                return -EOPNOTSUPP;
  85                        }
  86
  87                        /* Forbid block with this rulei to be bound
  88                         * to egress in future.
  89                         */
  90                        rulei->egress_bind_blocker = 1;
  91
  92                        fid = mlxsw_sp_acl_dummy_fid(mlxsw_sp);
  93                        fid_index = mlxsw_sp_fid_index(fid);
  94                        err = mlxsw_sp_acl_rulei_act_fid_set(mlxsw_sp, rulei,
  95                                                             fid_index, extack);
  96                        if (err)
  97                                return err;
  98
  99                        out_dev = act->dev;
 100                        err = mlxsw_sp_acl_rulei_act_fwd(mlxsw_sp, rulei,
 101                                                         out_dev, extack);
 102                        if (err)
 103                                return err;
 104                        }
 105                        break;
 106                case FLOW_ACTION_MIRRED: {
 107                        struct net_device *out_dev = act->dev;
 108
 109                        if (mirror_act_count++) {
 110                                NL_SET_ERR_MSG_MOD(extack, "Multiple mirror actions per rule are not supported");
 111                                return -EOPNOTSUPP;
 112                        }
 113
 114                        err = mlxsw_sp_acl_rulei_act_mirror(mlxsw_sp, rulei,
 115                                                            block, out_dev,
 116                                                            extack);
 117                        if (err)
 118                                return err;
 119                        }
 120                        break;
 121                case FLOW_ACTION_VLAN_MANGLE: {
 122                        u16 proto = be16_to_cpu(act->vlan.proto);
 123                        u8 prio = act->vlan.prio;
 124                        u16 vid = act->vlan.vid;
 125
 126                        return mlxsw_sp_acl_rulei_act_vlan(mlxsw_sp, rulei,
 127                                                           act->id, vid,
 128                                                           proto, prio, extack);
 129                        }
 130                default:
 131                        NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
 132                        dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n");
 133                        return -EOPNOTSUPP;
 134                }
 135        }
 136        return 0;
 137}
 138
 139static int mlxsw_sp_flower_parse_meta(struct mlxsw_sp_acl_rule_info *rulei,
 140                                      struct flow_cls_offload *f,
 141                                      struct mlxsw_sp_acl_block *block)
 142{
 143        struct flow_rule *rule = flow_cls_offload_flow_rule(f);
 144        struct mlxsw_sp_port *mlxsw_sp_port;
 145        struct net_device *ingress_dev;
 146        struct flow_match_meta match;
 147
 148        if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META))
 149                return 0;
 150
 151        flow_rule_match_meta(rule, &match);
 152        if (match.mask->ingress_ifindex != 0xFFFFFFFF) {
 153                NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported ingress ifindex mask");
 154                return -EINVAL;
 155        }
 156
 157        ingress_dev = __dev_get_by_index(block->net,
 158                                         match.key->ingress_ifindex);
 159        if (!ingress_dev) {
 160                NL_SET_ERR_MSG_MOD(f->common.extack, "Can't find specified ingress port to match on");
 161                return -EINVAL;
 162        }
 163
 164        if (!mlxsw_sp_port_dev_check(ingress_dev)) {
 165                NL_SET_ERR_MSG_MOD(f->common.extack, "Can't match on non-mlxsw ingress port");
 166                return -EINVAL;
 167        }
 168
 169        mlxsw_sp_port = netdev_priv(ingress_dev);
 170        if (mlxsw_sp_port->mlxsw_sp != block->mlxsw_sp) {
 171                NL_SET_ERR_MSG_MOD(f->common.extack, "Can't match on a port from different device");
 172                return -EINVAL;
 173        }
 174
 175        mlxsw_sp_acl_rulei_keymask_u32(rulei,
 176                                       MLXSW_AFK_ELEMENT_SRC_SYS_PORT,
 177                                       mlxsw_sp_port->local_port,
 178                                       0xFFFFFFFF);
 179        return 0;
 180}
 181
 182static void mlxsw_sp_flower_parse_ipv4(struct mlxsw_sp_acl_rule_info *rulei,
 183                                       struct flow_cls_offload *f)
 184{
 185        struct flow_match_ipv4_addrs match;
 186
 187        flow_rule_match_ipv4_addrs(f->rule, &match);
 188
 189        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
 190                                       (char *) &match.key->src,
 191                                       (char *) &match.mask->src, 4);
 192        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
 193                                       (char *) &match.key->dst,
 194                                       (char *) &match.mask->dst, 4);
 195}
 196
 197static void mlxsw_sp_flower_parse_ipv6(struct mlxsw_sp_acl_rule_info *rulei,
 198                                       struct flow_cls_offload *f)
 199{
 200        struct flow_match_ipv6_addrs match;
 201
 202        flow_rule_match_ipv6_addrs(f->rule, &match);
 203
 204        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_96_127,
 205                                       &match.key->src.s6_addr[0x0],
 206                                       &match.mask->src.s6_addr[0x0], 4);
 207        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_64_95,
 208                                       &match.key->src.s6_addr[0x4],
 209                                       &match.mask->src.s6_addr[0x4], 4);
 210        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_32_63,
 211                                       &match.key->src.s6_addr[0x8],
 212                                       &match.mask->src.s6_addr[0x8], 4);
 213        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
 214                                       &match.key->src.s6_addr[0xC],
 215                                       &match.mask->src.s6_addr[0xC], 4);
 216        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_96_127,
 217                                       &match.key->dst.s6_addr[0x0],
 218                                       &match.mask->dst.s6_addr[0x0], 4);
 219        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_64_95,
 220                                       &match.key->dst.s6_addr[0x4],
 221                                       &match.mask->dst.s6_addr[0x4], 4);
 222        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_32_63,
 223                                       &match.key->dst.s6_addr[0x8],
 224                                       &match.mask->dst.s6_addr[0x8], 4);
 225        mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
 226                                       &match.key->dst.s6_addr[0xC],
 227                                       &match.mask->dst.s6_addr[0xC], 4);
 228}
 229
 230static int mlxsw_sp_flower_parse_ports(struct mlxsw_sp *mlxsw_sp,
 231                                       struct mlxsw_sp_acl_rule_info *rulei,
 232                                       struct flow_cls_offload *f,
 233                                       u8 ip_proto)
 234{
 235        const struct flow_rule *rule = flow_cls_offload_flow_rule(f);
 236        struct flow_match_ports match;
 237
 238        if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS))
 239                return 0;
 240
 241        if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) {
 242                NL_SET_ERR_MSG_MOD(f->common.extack, "Only UDP and TCP keys are supported");
 243                dev_err(mlxsw_sp->bus_info->dev, "Only UDP and TCP keys are supported\n");
 244                return -EINVAL;
 245        }
 246
 247        flow_rule_match_ports(rule, &match);
 248        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_DST_L4_PORT,
 249                                       ntohs(match.key->dst),
 250                                       ntohs(match.mask->dst));
 251        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_SRC_L4_PORT,
 252                                       ntohs(match.key->src),
 253                                       ntohs(match.mask->src));
 254        return 0;
 255}
 256
 257static int mlxsw_sp_flower_parse_tcp(struct mlxsw_sp *mlxsw_sp,
 258                                     struct mlxsw_sp_acl_rule_info *rulei,
 259                                     struct flow_cls_offload *f,
 260                                     u8 ip_proto)
 261{
 262        const struct flow_rule *rule = flow_cls_offload_flow_rule(f);
 263        struct flow_match_tcp match;
 264
 265        if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP))
 266                return 0;
 267
 268        if (ip_proto != IPPROTO_TCP) {
 269                NL_SET_ERR_MSG_MOD(f->common.extack, "TCP keys supported only for TCP");
 270                dev_err(mlxsw_sp->bus_info->dev, "TCP keys supported only for TCP\n");
 271                return -EINVAL;
 272        }
 273
 274        flow_rule_match_tcp(rule, &match);
 275
 276        if (match.mask->flags & htons(0x0E00)) {
 277                NL_SET_ERR_MSG_MOD(f->common.extack, "TCP flags match not supported on reserved bits");
 278                dev_err(mlxsw_sp->bus_info->dev, "TCP flags match not supported on reserved bits\n");
 279                return -EINVAL;
 280        }
 281
 282        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_TCP_FLAGS,
 283                                       ntohs(match.key->flags),
 284                                       ntohs(match.mask->flags));
 285        return 0;
 286}
 287
 288static int mlxsw_sp_flower_parse_ip(struct mlxsw_sp *mlxsw_sp,
 289                                    struct mlxsw_sp_acl_rule_info *rulei,
 290                                    struct flow_cls_offload *f,
 291                                    u16 n_proto)
 292{
 293        const struct flow_rule *rule = flow_cls_offload_flow_rule(f);
 294        struct flow_match_ip match;
 295
 296        if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP))
 297                return 0;
 298
 299        if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6) {
 300                NL_SET_ERR_MSG_MOD(f->common.extack, "IP keys supported only for IPv4/6");
 301                dev_err(mlxsw_sp->bus_info->dev, "IP keys supported only for IPv4/6\n");
 302                return -EINVAL;
 303        }
 304
 305        flow_rule_match_ip(rule, &match);
 306
 307        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_TTL_,
 308                                       match.key->ttl, match.mask->ttl);
 309
 310        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_ECN,
 311                                       match.key->tos & 0x3,
 312                                       match.mask->tos & 0x3);
 313
 314        mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_DSCP,
 315                                       match.key->tos >> 2,
 316                                       match.mask->tos >> 2);
 317
 318        return 0;
 319}
 320
 321static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp,
 322                                 struct mlxsw_sp_acl_block *block,
 323                                 struct mlxsw_sp_acl_rule_info *rulei,
 324                                 struct flow_cls_offload *f)
 325{
 326        struct flow_rule *rule = flow_cls_offload_flow_rule(f);
 327        struct flow_dissector *dissector = rule->match.dissector;
 328        u16 n_proto_mask = 0;
 329        u16 n_proto_key = 0;
 330        u16 addr_type = 0;
 331        u8 ip_proto = 0;
 332        int err;
 333
 334        if (dissector->used_keys &
 335            ~(BIT(FLOW_DISSECTOR_KEY_META) |
 336              BIT(FLOW_DISSECTOR_KEY_CONTROL) |
 337              BIT(FLOW_DISSECTOR_KEY_BASIC) |
 338              BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
 339              BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
 340              BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
 341              BIT(FLOW_DISSECTOR_KEY_PORTS) |
 342              BIT(FLOW_DISSECTOR_KEY_TCP) |
 343              BIT(FLOW_DISSECTOR_KEY_IP) |
 344              BIT(FLOW_DISSECTOR_KEY_VLAN))) {
 345                dev_err(mlxsw_sp->bus_info->dev, "Unsupported key\n");
 346                NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported key");
 347                return -EOPNOTSUPP;
 348        }
 349
 350        mlxsw_sp_acl_rulei_priority(rulei, f->common.prio);
 351
 352        err = mlxsw_sp_flower_parse_meta(rulei, f, block);
 353        if (err)
 354                return err;
 355
 356        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
 357                struct flow_match_control match;
 358
 359                flow_rule_match_control(rule, &match);
 360                addr_type = match.key->addr_type;
 361        }
 362
 363        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
 364                struct flow_match_basic match;
 365
 366                flow_rule_match_basic(rule, &match);
 367                n_proto_key = ntohs(match.key->n_proto);
 368                n_proto_mask = ntohs(match.mask->n_proto);
 369
 370                if (n_proto_key == ETH_P_ALL) {
 371                        n_proto_key = 0;
 372                        n_proto_mask = 0;
 373                }
 374                mlxsw_sp_acl_rulei_keymask_u32(rulei,
 375                                               MLXSW_AFK_ELEMENT_ETHERTYPE,
 376                                               n_proto_key, n_proto_mask);
 377
 378                ip_proto = match.key->ip_proto;
 379                mlxsw_sp_acl_rulei_keymask_u32(rulei,
 380                                               MLXSW_AFK_ELEMENT_IP_PROTO,
 381                                               match.key->ip_proto,
 382                                               match.mask->ip_proto);
 383        }
 384
 385        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
 386                struct flow_match_eth_addrs match;
 387
 388                flow_rule_match_eth_addrs(rule, &match);
 389                mlxsw_sp_acl_rulei_keymask_buf(rulei,
 390                                               MLXSW_AFK_ELEMENT_DMAC_32_47,
 391                                               match.key->dst,
 392                                               match.mask->dst, 2);
 393                mlxsw_sp_acl_rulei_keymask_buf(rulei,
 394                                               MLXSW_AFK_ELEMENT_DMAC_0_31,
 395                                               match.key->dst + 2,
 396                                               match.mask->dst + 2, 4);
 397                mlxsw_sp_acl_rulei_keymask_buf(rulei,
 398                                               MLXSW_AFK_ELEMENT_SMAC_32_47,
 399                                               match.key->src,
 400                                               match.mask->src, 2);
 401                mlxsw_sp_acl_rulei_keymask_buf(rulei,
 402                                               MLXSW_AFK_ELEMENT_SMAC_0_31,
 403                                               match.key->src + 2,
 404                                               match.mask->src + 2, 4);
 405        }
 406
 407        if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
 408                struct flow_match_vlan match;
 409
 410                flow_rule_match_vlan(rule, &match);
 411                if (mlxsw_sp_acl_block_is_egress_bound(block)) {
 412                        NL_SET_ERR_MSG_MOD(f->common.extack, "vlan_id key is not supported on egress");
 413                        return -EOPNOTSUPP;
 414                }
 415
 416                /* Forbid block with this rulei to be bound
 417                 * to egress in future.
 418                 */
 419                rulei->egress_bind_blocker = 1;
 420
 421                if (match.mask->vlan_id != 0)
 422                        mlxsw_sp_acl_rulei_keymask_u32(rulei,
 423                                                       MLXSW_AFK_ELEMENT_VID,
 424                                                       match.key->vlan_id,
 425                                                       match.mask->vlan_id);
 426                if (match.mask->vlan_priority != 0)
 427                        mlxsw_sp_acl_rulei_keymask_u32(rulei,
 428                                                       MLXSW_AFK_ELEMENT_PCP,
 429                                                       match.key->vlan_priority,
 430                                                       match.mask->vlan_priority);
 431        }
 432
 433        if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS)
 434                mlxsw_sp_flower_parse_ipv4(rulei, f);
 435
 436        if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS)
 437                mlxsw_sp_flower_parse_ipv6(rulei, f);
 438
 439        err = mlxsw_sp_flower_parse_ports(mlxsw_sp, rulei, f, ip_proto);
 440        if (err)
 441                return err;
 442        err = mlxsw_sp_flower_parse_tcp(mlxsw_sp, rulei, f, ip_proto);
 443        if (err)
 444                return err;
 445
 446        err = mlxsw_sp_flower_parse_ip(mlxsw_sp, rulei, f, n_proto_key & n_proto_mask);
 447        if (err)
 448                return err;
 449
 450        return mlxsw_sp_flower_parse_actions(mlxsw_sp, block, rulei,
 451                                             &f->rule->action,
 452                                             f->common.extack);
 453}
 454
 455int mlxsw_sp_flower_replace(struct mlxsw_sp *mlxsw_sp,
 456                            struct mlxsw_sp_acl_block *block,
 457                            struct flow_cls_offload *f)
 458{
 459        struct mlxsw_sp_acl_rule_info *rulei;
 460        struct mlxsw_sp_acl_ruleset *ruleset;
 461        struct mlxsw_sp_acl_rule *rule;
 462        int err;
 463
 464        ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
 465                                           f->common.chain_index,
 466                                           MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
 467        if (IS_ERR(ruleset))
 468                return PTR_ERR(ruleset);
 469
 470        rule = mlxsw_sp_acl_rule_create(mlxsw_sp, ruleset, f->cookie, NULL,
 471                                        f->common.extack);
 472        if (IS_ERR(rule)) {
 473                err = PTR_ERR(rule);
 474                goto err_rule_create;
 475        }
 476
 477        rulei = mlxsw_sp_acl_rule_rulei(rule);
 478        err = mlxsw_sp_flower_parse(mlxsw_sp, block, rulei, f);
 479        if (err)
 480                goto err_flower_parse;
 481
 482        err = mlxsw_sp_acl_rulei_commit(rulei);
 483        if (err)
 484                goto err_rulei_commit;
 485
 486        err = mlxsw_sp_acl_rule_add(mlxsw_sp, rule);
 487        if (err)
 488                goto err_rule_add;
 489
 490        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 491        return 0;
 492
 493err_rule_add:
 494err_rulei_commit:
 495err_flower_parse:
 496        mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
 497err_rule_create:
 498        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 499        return err;
 500}
 501
 502void mlxsw_sp_flower_destroy(struct mlxsw_sp *mlxsw_sp,
 503                             struct mlxsw_sp_acl_block *block,
 504                             struct flow_cls_offload *f)
 505{
 506        struct mlxsw_sp_acl_ruleset *ruleset;
 507        struct mlxsw_sp_acl_rule *rule;
 508
 509        ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
 510                                           f->common.chain_index,
 511                                           MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
 512        if (IS_ERR(ruleset))
 513                return;
 514
 515        rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie);
 516        if (rule) {
 517                mlxsw_sp_acl_rule_del(mlxsw_sp, rule);
 518                mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
 519        }
 520
 521        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 522}
 523
 524int mlxsw_sp_flower_stats(struct mlxsw_sp *mlxsw_sp,
 525                          struct mlxsw_sp_acl_block *block,
 526                          struct flow_cls_offload *f)
 527{
 528        struct mlxsw_sp_acl_ruleset *ruleset;
 529        struct mlxsw_sp_acl_rule *rule;
 530        u64 packets;
 531        u64 lastuse;
 532        u64 bytes;
 533        int err;
 534
 535        ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
 536                                           f->common.chain_index,
 537                                           MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
 538        if (WARN_ON(IS_ERR(ruleset)))
 539                return -EINVAL;
 540
 541        rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie);
 542        if (!rule)
 543                return -EINVAL;
 544
 545        err = mlxsw_sp_acl_rule_get_stats(mlxsw_sp, rule, &packets, &bytes,
 546                                          &lastuse);
 547        if (err)
 548                goto err_rule_get_stats;
 549
 550        flow_stats_update(&f->stats, bytes, packets, lastuse);
 551
 552        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 553        return 0;
 554
 555err_rule_get_stats:
 556        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 557        return err;
 558}
 559
 560int mlxsw_sp_flower_tmplt_create(struct mlxsw_sp *mlxsw_sp,
 561                                 struct mlxsw_sp_acl_block *block,
 562                                 struct flow_cls_offload *f)
 563{
 564        struct mlxsw_sp_acl_ruleset *ruleset;
 565        struct mlxsw_sp_acl_rule_info rulei;
 566        int err;
 567
 568        memset(&rulei, 0, sizeof(rulei));
 569        err = mlxsw_sp_flower_parse(mlxsw_sp, block, &rulei, f);
 570        if (err)
 571                return err;
 572        ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
 573                                           f->common.chain_index,
 574                                           MLXSW_SP_ACL_PROFILE_FLOWER,
 575                                           &rulei.values.elusage);
 576
 577        /* keep the reference to the ruleset */
 578        return PTR_ERR_OR_ZERO(ruleset);
 579}
 580
 581void mlxsw_sp_flower_tmplt_destroy(struct mlxsw_sp *mlxsw_sp,
 582                                   struct mlxsw_sp_acl_block *block,
 583                                   struct flow_cls_offload *f)
 584{
 585        struct mlxsw_sp_acl_ruleset *ruleset;
 586
 587        ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block,
 588                                           f->common.chain_index,
 589                                           MLXSW_SP_ACL_PROFILE_FLOWER, NULL);
 590        if (IS_ERR(ruleset))
 591                return;
 592        /* put the reference to the ruleset kept in create */
 593        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 594        mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset);
 595}
 596