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_flow.c (6599B)


      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	switch (f->command) {
     33	case FLOW_CLS_REPLACE:
     34		return prestera_flower_replace(block, f);
     35	case FLOW_CLS_DESTROY:
     36		prestera_flower_destroy(block, f);
     37		return 0;
     38	case FLOW_CLS_STATS:
     39		return prestera_flower_stats(block, f);
     40	case FLOW_CLS_TMPLT_CREATE:
     41		return prestera_flower_tmplt_create(block, f);
     42	case FLOW_CLS_TMPLT_DESTROY:
     43		prestera_flower_tmplt_destroy(block, f);
     44		return 0;
     45	default:
     46		return -EOPNOTSUPP;
     47	}
     48}
     49
     50static int prestera_flow_block_cb(enum tc_setup_type type,
     51				  void *type_data, void *cb_priv)
     52{
     53	struct prestera_flow_block *block = cb_priv;
     54
     55	switch (type) {
     56	case TC_SETUP_CLSFLOWER:
     57		return prestera_flow_block_flower_cb(block, type_data);
     58	case TC_SETUP_CLSMATCHALL:
     59		return prestera_flow_block_mall_cb(block, type_data);
     60	default:
     61		return -EOPNOTSUPP;
     62	}
     63}
     64
     65static void prestera_flow_block_destroy(void *cb_priv)
     66{
     67	struct prestera_flow_block *block = cb_priv;
     68
     69	prestera_flower_template_cleanup(block);
     70
     71	WARN_ON(!list_empty(&block->template_list));
     72	WARN_ON(!list_empty(&block->binding_list));
     73
     74	kfree(block);
     75}
     76
     77static struct prestera_flow_block *
     78prestera_flow_block_create(struct prestera_switch *sw, struct net *net)
     79{
     80	struct prestera_flow_block *block;
     81
     82	block = kzalloc(sizeof(*block), GFP_KERNEL);
     83	if (!block)
     84		return NULL;
     85
     86	INIT_LIST_HEAD(&block->binding_list);
     87	INIT_LIST_HEAD(&block->template_list);
     88	block->net = net;
     89	block->sw = sw;
     90
     91	return block;
     92}
     93
     94static void prestera_flow_block_release(void *cb_priv)
     95{
     96	struct prestera_flow_block *block = cb_priv;
     97
     98	prestera_flow_block_destroy(block);
     99}
    100
    101static bool
    102prestera_flow_block_is_bound(const struct prestera_flow_block *block)
    103{
    104	return block->ruleset_zero;
    105}
    106
    107static struct prestera_flow_block_binding *
    108prestera_flow_block_lookup(struct prestera_flow_block *block,
    109			   struct prestera_port *port)
    110{
    111	struct prestera_flow_block_binding *binding;
    112
    113	list_for_each_entry(binding, &block->binding_list, list)
    114		if (binding->port == port)
    115			return binding;
    116
    117	return NULL;
    118}
    119
    120static int prestera_flow_block_bind(struct prestera_flow_block *block,
    121				    struct prestera_port *port)
    122{
    123	struct prestera_flow_block_binding *binding;
    124	int err;
    125
    126	binding = kzalloc(sizeof(*binding), GFP_KERNEL);
    127	if (!binding)
    128		return -ENOMEM;
    129
    130	binding->span_id = PRESTERA_SPAN_INVALID_ID;
    131	binding->port = port;
    132
    133	if (prestera_flow_block_is_bound(block)) {
    134		err = prestera_acl_ruleset_bind(block->ruleset_zero, port);
    135		if (err)
    136			goto err_ruleset_bind;
    137	}
    138
    139	list_add(&binding->list, &block->binding_list);
    140	return 0;
    141
    142err_ruleset_bind:
    143	kfree(binding);
    144	return err;
    145}
    146
    147static int prestera_flow_block_unbind(struct prestera_flow_block *block,
    148				      struct prestera_port *port)
    149{
    150	struct prestera_flow_block_binding *binding;
    151
    152	binding = prestera_flow_block_lookup(block, port);
    153	if (!binding)
    154		return -ENOENT;
    155
    156	list_del(&binding->list);
    157
    158	if (prestera_flow_block_is_bound(block))
    159		prestera_acl_ruleset_unbind(block->ruleset_zero, port);
    160
    161	kfree(binding);
    162	return 0;
    163}
    164
    165static struct prestera_flow_block *
    166prestera_flow_block_get(struct prestera_switch *sw,
    167			struct flow_block_offload *f,
    168			bool *register_block)
    169{
    170	struct prestera_flow_block *block;
    171	struct flow_block_cb *block_cb;
    172
    173	block_cb = flow_block_cb_lookup(f->block,
    174					prestera_flow_block_cb, sw);
    175	if (!block_cb) {
    176		block = prestera_flow_block_create(sw, f->net);
    177		if (!block)
    178			return ERR_PTR(-ENOMEM);
    179
    180		block_cb = flow_block_cb_alloc(prestera_flow_block_cb,
    181					       sw, block,
    182					       prestera_flow_block_release);
    183		if (IS_ERR(block_cb)) {
    184			prestera_flow_block_destroy(block);
    185			return ERR_CAST(block_cb);
    186		}
    187
    188		block->block_cb = block_cb;
    189		*register_block = true;
    190	} else {
    191		block = flow_block_cb_priv(block_cb);
    192		*register_block = false;
    193	}
    194
    195	flow_block_cb_incref(block_cb);
    196
    197	return block;
    198}
    199
    200static void prestera_flow_block_put(struct prestera_flow_block *block)
    201{
    202	struct flow_block_cb *block_cb = block->block_cb;
    203
    204	if (flow_block_cb_decref(block_cb))
    205		return;
    206
    207	flow_block_cb_free(block_cb);
    208	prestera_flow_block_destroy(block);
    209}
    210
    211static int prestera_setup_flow_block_bind(struct prestera_port *port,
    212					  struct flow_block_offload *f)
    213{
    214	struct prestera_switch *sw = port->sw;
    215	struct prestera_flow_block *block;
    216	struct flow_block_cb *block_cb;
    217	bool register_block;
    218	int err;
    219
    220	block = prestera_flow_block_get(sw, f, &register_block);
    221	if (IS_ERR(block))
    222		return PTR_ERR(block);
    223
    224	block_cb = block->block_cb;
    225
    226	err = prestera_flow_block_bind(block, port);
    227	if (err)
    228		goto err_block_bind;
    229
    230	if (register_block) {
    231		flow_block_cb_add(block_cb, f);
    232		list_add_tail(&block_cb->driver_list, &prestera_block_cb_list);
    233	}
    234
    235	port->flow_block = block;
    236	return 0;
    237
    238err_block_bind:
    239	prestera_flow_block_put(block);
    240
    241	return err;
    242}
    243
    244static void prestera_setup_flow_block_unbind(struct prestera_port *port,
    245					     struct flow_block_offload *f)
    246{
    247	struct prestera_switch *sw = port->sw;
    248	struct prestera_flow_block *block;
    249	struct flow_block_cb *block_cb;
    250	int err;
    251
    252	block_cb = flow_block_cb_lookup(f->block, prestera_flow_block_cb, sw);
    253	if (!block_cb)
    254		return;
    255
    256	block = flow_block_cb_priv(block_cb);
    257
    258	prestera_span_destroy(block);
    259
    260	err = prestera_flow_block_unbind(block, port);
    261	if (err)
    262		goto error;
    263
    264	if (!flow_block_cb_decref(block_cb)) {
    265		flow_block_cb_remove(block_cb, f);
    266		list_del(&block_cb->driver_list);
    267	}
    268error:
    269	port->flow_block = NULL;
    270}
    271
    272int prestera_flow_block_setup(struct prestera_port *port,
    273			      struct flow_block_offload *f)
    274{
    275	if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
    276		return -EOPNOTSUPP;
    277
    278	f->driver_block_list = &prestera_block_cb_list;
    279
    280	switch (f->command) {
    281	case FLOW_BLOCK_BIND:
    282		return prestera_setup_flow_block_bind(port, f);
    283	case FLOW_BLOCK_UNBIND:
    284		prestera_setup_flow_block_unbind(port, f);
    285		return 0;
    286	default:
    287		return -EOPNOTSUPP;
    288	}
    289}