cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

prestera_span.c (5107B)


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