linux/drivers/net/ethernet/marvell/prestera/prestera_flow.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
   2/* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
   3
   4#include <linux/kernel.h>
   5#include <linux/list.h>
   6
   7#include "prestera.h"
   8#include "prestera_acl.h"
   9#include "prestera_flow.h"
  10#include "prestera_span.h"
  11#include "prestera_flower.h"
  12
  13static LIST_HEAD(prestera_block_cb_list);
  14
  15static int prestera_flow_block_mall_cb(struct prestera_flow_block *block,
  16                                       struct tc_cls_matchall_offload *f)
  17{
  18        switch (f->command) {
  19        case TC_CLSMATCHALL_REPLACE:
  20                return prestera_span_replace(block, f);
  21        case TC_CLSMATCHALL_DESTROY:
  22                prestera_span_destroy(block);
  23                return 0;
  24        default:
  25                return -EOPNOTSUPP;
  26        }
  27}
  28
  29static int prestera_flow_block_flower_cb(struct prestera_flow_block *block,
  30                                         struct flow_cls_offload *f)
  31{
  32        if (f->common.chain_index != 0)
  33                return -EOPNOTSUPP;
  34
  35        switch (f->command) {
  36        case FLOW_CLS_REPLACE:
  37                return prestera_flower_replace(block, f);
  38        case FLOW_CLS_DESTROY:
  39                prestera_flower_destroy(block, f);
  40                return 0;
  41        case FLOW_CLS_STATS:
  42                return prestera_flower_stats(block, f);
  43        default:
  44                return -EOPNOTSUPP;
  45        }
  46}
  47
  48static int prestera_flow_block_cb(enum tc_setup_type type,
  49                                  void *type_data, void *cb_priv)
  50{
  51        struct prestera_flow_block *block = cb_priv;
  52
  53        switch (type) {
  54        case TC_SETUP_CLSFLOWER:
  55                return prestera_flow_block_flower_cb(block, type_data);
  56        case TC_SETUP_CLSMATCHALL:
  57                return prestera_flow_block_mall_cb(block, type_data);
  58        default:
  59                return -EOPNOTSUPP;
  60        }
  61}
  62
  63static void prestera_flow_block_release(void *cb_priv)
  64{
  65        struct prestera_flow_block *block = cb_priv;
  66
  67        prestera_acl_block_destroy(block);
  68}
  69
  70static struct prestera_flow_block *
  71prestera_flow_block_get(struct prestera_switch *sw,
  72                        struct flow_block_offload *f,
  73                        bool *register_block)
  74{
  75        struct prestera_flow_block *block;
  76        struct flow_block_cb *block_cb;
  77
  78        block_cb = flow_block_cb_lookup(f->block,
  79                                        prestera_flow_block_cb, sw);
  80        if (!block_cb) {
  81                block = prestera_acl_block_create(sw, f->net);
  82                if (!block)
  83                        return ERR_PTR(-ENOMEM);
  84
  85                block_cb = flow_block_cb_alloc(prestera_flow_block_cb,
  86                                               sw, block,
  87                                               prestera_flow_block_release);
  88                if (IS_ERR(block_cb)) {
  89                        prestera_acl_block_destroy(block);
  90                        return ERR_CAST(block_cb);
  91                }
  92
  93                block->block_cb = block_cb;
  94                *register_block = true;
  95        } else {
  96                block = flow_block_cb_priv(block_cb);
  97                *register_block = false;
  98        }
  99
 100        flow_block_cb_incref(block_cb);
 101
 102        return block;
 103}
 104
 105static void prestera_flow_block_put(struct prestera_flow_block *block)
 106{
 107        struct flow_block_cb *block_cb = block->block_cb;
 108
 109        if (flow_block_cb_decref(block_cb))
 110                return;
 111
 112        flow_block_cb_free(block_cb);
 113        prestera_acl_block_destroy(block);
 114}
 115
 116static int prestera_setup_flow_block_bind(struct prestera_port *port,
 117                                          struct flow_block_offload *f)
 118{
 119        struct prestera_switch *sw = port->sw;
 120        struct prestera_flow_block *block;
 121        struct flow_block_cb *block_cb;
 122        bool register_block;
 123        int err;
 124
 125        block = prestera_flow_block_get(sw, f, &register_block);
 126        if (IS_ERR(block))
 127                return PTR_ERR(block);
 128
 129        block_cb = block->block_cb;
 130
 131        err = prestera_acl_block_bind(block, port);
 132        if (err)
 133                goto err_block_bind;
 134
 135        if (register_block) {
 136                flow_block_cb_add(block_cb, f);
 137                list_add_tail(&block_cb->driver_list, &prestera_block_cb_list);
 138        }
 139
 140        port->flow_block = block;
 141        return 0;
 142
 143err_block_bind:
 144        prestera_flow_block_put(block);
 145
 146        return err;
 147}
 148
 149static void prestera_setup_flow_block_unbind(struct prestera_port *port,
 150                                             struct flow_block_offload *f)
 151{
 152        struct prestera_switch *sw = port->sw;
 153        struct prestera_flow_block *block;
 154        struct flow_block_cb *block_cb;
 155        int err;
 156
 157        block_cb = flow_block_cb_lookup(f->block, prestera_flow_block_cb, sw);
 158        if (!block_cb)
 159                return;
 160
 161        block = flow_block_cb_priv(block_cb);
 162
 163        prestera_span_destroy(block);
 164
 165        err = prestera_acl_block_unbind(block, port);
 166        if (err)
 167                goto error;
 168
 169        if (!flow_block_cb_decref(block_cb)) {
 170                flow_block_cb_remove(block_cb, f);
 171                list_del(&block_cb->driver_list);
 172        }
 173error:
 174        port->flow_block = NULL;
 175}
 176
 177int prestera_flow_block_setup(struct prestera_port *port,
 178                              struct flow_block_offload *f)
 179{
 180        if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
 181                return -EOPNOTSUPP;
 182
 183        f->driver_block_list = &prestera_block_cb_list;
 184
 185        switch (f->command) {
 186        case FLOW_BLOCK_BIND:
 187                return prestera_setup_flow_block_bind(port, f);
 188        case FLOW_BLOCK_UNBIND:
 189                prestera_setup_flow_block_unbind(port, f);
 190                return 0;
 191        default:
 192                return -EOPNOTSUPP;
 193        }
 194}
 195