linux/drivers/net/ethernet/marvell/prestera/prestera_span.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_hw.h"
   9#include "prestera_acl.h"
  10#include "prestera_span.h"
  11
  12struct prestera_span_entry {
  13        struct list_head list;
  14        struct prestera_port *port;
  15        refcount_t ref_count;
  16        u8 id;
  17};
  18
  19struct prestera_span {
  20        struct prestera_switch *sw;
  21        struct list_head entries;
  22};
  23
  24static struct prestera_span_entry *
  25prestera_span_entry_create(struct prestera_port *port, u8 span_id)
  26{
  27        struct prestera_span_entry *entry;
  28
  29        entry = kzalloc(sizeof(*entry), GFP_KERNEL);
  30        if (!entry)
  31                return ERR_PTR(-ENOMEM);
  32
  33        refcount_set(&entry->ref_count, 1);
  34        entry->port = port;
  35        entry->id = span_id;
  36        list_add_tail(&entry->list, &port->sw->span->entries);
  37
  38        return entry;
  39}
  40
  41static void prestera_span_entry_del(struct prestera_span_entry *entry)
  42{
  43        list_del(&entry->list);
  44        kfree(entry);
  45}
  46
  47static struct prestera_span_entry *
  48prestera_span_entry_find_by_id(struct prestera_span *span, u8 span_id)
  49{
  50        struct prestera_span_entry *entry;
  51
  52        list_for_each_entry(entry, &span->entries, list) {
  53                if (entry->id == span_id)
  54                        return entry;
  55        }
  56
  57        return NULL;
  58}
  59
  60static struct prestera_span_entry *
  61prestera_span_entry_find_by_port(struct prestera_span *span,
  62                                 struct prestera_port *port)
  63{
  64        struct prestera_span_entry *entry;
  65
  66        list_for_each_entry(entry, &span->entries, list) {
  67                if (entry->port == port)
  68                        return entry;
  69        }
  70
  71        return NULL;
  72}
  73
  74static int prestera_span_get(struct prestera_port *port, u8 *span_id)
  75{
  76        u8 new_span_id;
  77        struct prestera_switch *sw = port->sw;
  78        struct prestera_span_entry *entry;
  79        int err;
  80
  81        entry = prestera_span_entry_find_by_port(sw->span, port);
  82        if (entry) {
  83                refcount_inc(&entry->ref_count);
  84                *span_id = entry->id;
  85                return 0;
  86        }
  87
  88        err = prestera_hw_span_get(port, &new_span_id);
  89        if (err)
  90                return err;
  91
  92        entry = prestera_span_entry_create(port, new_span_id);
  93        if (IS_ERR(entry)) {
  94                prestera_hw_span_release(sw, new_span_id);
  95                return PTR_ERR(entry);
  96        }
  97
  98        *span_id = new_span_id;
  99        return 0;
 100}
 101
 102static int prestera_span_put(struct prestera_switch *sw, u8 span_id)
 103{
 104        struct prestera_span_entry *entry;
 105        int err;
 106
 107        entry = prestera_span_entry_find_by_id(sw->span, span_id);
 108        if (!entry)
 109                return false;
 110
 111        if (!refcount_dec_and_test(&entry->ref_count))
 112                return 0;
 113
 114        err = prestera_hw_span_release(sw, span_id);
 115        if (err)
 116                return err;
 117
 118        prestera_span_entry_del(entry);
 119        return 0;
 120}
 121
 122static int prestera_span_rule_add(struct prestera_flow_block_binding *binding,
 123                                  struct prestera_port *to_port)
 124{
 125        struct prestera_switch *sw = binding->port->sw;
 126        u8 span_id;
 127        int err;
 128
 129        if (binding->span_id != PRESTERA_SPAN_INVALID_ID)
 130                /* port already in mirroring */
 131                return -EEXIST;
 132
 133        err = prestera_span_get(to_port, &span_id);
 134        if (err)
 135                return err;
 136
 137        err = prestera_hw_span_bind(binding->port, span_id);
 138        if (err) {
 139                prestera_span_put(sw, span_id);
 140                return err;
 141        }
 142
 143        binding->span_id = span_id;
 144        return 0;
 145}
 146
 147static int prestera_span_rule_del(struct prestera_flow_block_binding *binding)
 148{
 149        int err;
 150
 151        err = prestera_hw_span_unbind(binding->port);
 152        if (err)
 153                return err;
 154
 155        err = prestera_span_put(binding->port->sw, binding->span_id);
 156        if (err)
 157                return err;
 158
 159        binding->span_id = PRESTERA_SPAN_INVALID_ID;
 160        return 0;
 161}
 162
 163int prestera_span_replace(struct prestera_flow_block *block,
 164                          struct tc_cls_matchall_offload *f)
 165{
 166        struct prestera_flow_block_binding *binding;
 167        __be16 protocol = f->common.protocol;
 168        struct flow_action_entry *act;
 169        struct prestera_port *port;
 170        int err;
 171
 172        if (!flow_offload_has_one_action(&f->rule->action)) {
 173                NL_SET_ERR_MSG(f->common.extack,
 174                               "Only singular actions are supported");
 175                return -EOPNOTSUPP;
 176        }
 177
 178        act = &f->rule->action.entries[0];
 179
 180        if (!prestera_netdev_check(act->dev)) {
 181                NL_SET_ERR_MSG(f->common.extack,
 182                               "Only Marvell Prestera port is supported");
 183                return -EINVAL;
 184        }
 185        if (!tc_cls_can_offload_and_chain0(act->dev, &f->common))
 186                return -EOPNOTSUPP;
 187        if (act->id != FLOW_ACTION_MIRRED)
 188                return -EOPNOTSUPP;
 189        if (protocol != htons(ETH_P_ALL))
 190                return -EOPNOTSUPP;
 191
 192        port = netdev_priv(act->dev);
 193
 194        list_for_each_entry(binding, &block->binding_list, list) {
 195                err = prestera_span_rule_add(binding, port);
 196                if (err)
 197                        goto rollback;
 198        }
 199
 200        return 0;
 201
 202rollback:
 203        list_for_each_entry_continue_reverse(binding,
 204                                             &block->binding_list, list)
 205                prestera_span_rule_del(binding);
 206        return err;
 207}
 208
 209void prestera_span_destroy(struct prestera_flow_block *block)
 210{
 211        struct prestera_flow_block_binding *binding;
 212
 213        list_for_each_entry(binding, &block->binding_list, list)
 214                prestera_span_rule_del(binding);
 215}
 216
 217int prestera_span_init(struct prestera_switch *sw)
 218{
 219        struct prestera_span *span;
 220
 221        span = kzalloc(sizeof(*span), GFP_KERNEL);
 222        if (!span)
 223                return -ENOMEM;
 224
 225        INIT_LIST_HEAD(&span->entries);
 226
 227        sw->span = span;
 228        span->sw = sw;
 229
 230        return 0;
 231}
 232
 233void prestera_span_fini(struct prestera_switch *sw)
 234{
 235        struct prestera_span *span = sw->span;
 236
 237        WARN_ON(!list_empty(&span->entries));
 238        kfree(span);
 239}
 240