brintos

brintos / linux-shallow public Read only

0
0
Text · 7.1 KiB · 9f4267f Raw
316 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/* 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_flower.h"11#include "prestera_matchall.h"12#include "prestera_span.h"13 14static LIST_HEAD(prestera_block_cb_list);15 16static int prestera_flow_block_mall_cb(struct prestera_flow_block *block,17				       struct tc_cls_matchall_offload *f)18{19	switch (f->command) {20	case TC_CLSMATCHALL_REPLACE:21		return prestera_mall_replace(block, f);22	case TC_CLSMATCHALL_DESTROY:23		prestera_mall_destroy(block);24		return 0;25	default:26		return -EOPNOTSUPP;27	}28}29 30static int prestera_flow_block_flower_cb(struct prestera_flow_block *block,31					 struct flow_cls_offload *f)32{33	switch (f->command) {34	case FLOW_CLS_REPLACE:35		return prestera_flower_replace(block, f);36	case FLOW_CLS_DESTROY:37		prestera_flower_destroy(block, f);38		return 0;39	case FLOW_CLS_STATS:40		return prestera_flower_stats(block, f);41	case FLOW_CLS_TMPLT_CREATE:42		return prestera_flower_tmplt_create(block, f);43	case FLOW_CLS_TMPLT_DESTROY:44		prestera_flower_tmplt_destroy(block, f);45		return 0;46	default:47		return -EOPNOTSUPP;48	}49}50 51static int prestera_flow_block_cb(enum tc_setup_type type,52				  void *type_data, void *cb_priv)53{54	struct prestera_flow_block *block = cb_priv;55 56	switch (type) {57	case TC_SETUP_CLSFLOWER:58		return prestera_flow_block_flower_cb(block, type_data);59	case TC_SETUP_CLSMATCHALL:60		return prestera_flow_block_mall_cb(block, type_data);61	default:62		return -EOPNOTSUPP;63	}64}65 66static void prestera_flow_block_destroy(void *cb_priv)67{68	struct prestera_flow_block *block = cb_priv;69 70	prestera_flower_template_cleanup(block);71 72	WARN_ON(!list_empty(&block->template_list));73	WARN_ON(!list_empty(&block->binding_list));74 75	kfree(block);76}77 78static struct prestera_flow_block *79prestera_flow_block_create(struct prestera_switch *sw,80			   struct net *net,81			   bool ingress)82{83	struct prestera_flow_block *block;84 85	block = kzalloc(sizeof(*block), GFP_KERNEL);86	if (!block)87		return NULL;88 89	INIT_LIST_HEAD(&block->binding_list);90	INIT_LIST_HEAD(&block->template_list);91	block->net = net;92	block->sw = sw;93	block->mall.prio_min = UINT_MAX;94	block->mall.prio_max = 0;95	block->mall.bound = false;96	block->ingress = ingress;97 98	return block;99}100 101static void prestera_flow_block_release(void *cb_priv)102{103	struct prestera_flow_block *block = cb_priv;104 105	prestera_flow_block_destroy(block);106}107 108static bool109prestera_flow_block_is_bound(const struct prestera_flow_block *block)110{111	return block->ruleset_zero;112}113 114static struct prestera_flow_block_binding *115prestera_flow_block_lookup(struct prestera_flow_block *block,116			   struct prestera_port *port)117{118	struct prestera_flow_block_binding *binding;119 120	list_for_each_entry(binding, &block->binding_list, list)121		if (binding->port == port)122			return binding;123 124	return NULL;125}126 127static int prestera_flow_block_bind(struct prestera_flow_block *block,128				    struct prestera_port *port)129{130	struct prestera_flow_block_binding *binding;131	int err;132 133	binding = kzalloc(sizeof(*binding), GFP_KERNEL);134	if (!binding)135		return -ENOMEM;136 137	binding->span_id = PRESTERA_SPAN_INVALID_ID;138	binding->port = port;139 140	if (prestera_flow_block_is_bound(block)) {141		err = prestera_acl_ruleset_bind(block->ruleset_zero, port);142		if (err)143			goto err_ruleset_bind;144	}145 146	list_add(&binding->list, &block->binding_list);147	return 0;148 149err_ruleset_bind:150	kfree(binding);151	return err;152}153 154static int prestera_flow_block_unbind(struct prestera_flow_block *block,155				      struct prestera_port *port)156{157	struct prestera_flow_block_binding *binding;158 159	binding = prestera_flow_block_lookup(block, port);160	if (!binding)161		return -ENOENT;162 163	list_del(&binding->list);164 165	if (prestera_flow_block_is_bound(block))166		prestera_acl_ruleset_unbind(block->ruleset_zero, port);167 168	kfree(binding);169	return 0;170}171 172static struct prestera_flow_block *173prestera_flow_block_get(struct prestera_switch *sw,174			struct flow_block_offload *f,175			bool *register_block,176			bool ingress)177{178	struct prestera_flow_block *block;179	struct flow_block_cb *block_cb;180 181	block_cb = flow_block_cb_lookup(f->block,182					prestera_flow_block_cb, sw);183	if (!block_cb) {184		block = prestera_flow_block_create(sw, f->net, ingress);185		if (!block)186			return ERR_PTR(-ENOMEM);187 188		block_cb = flow_block_cb_alloc(prestera_flow_block_cb,189					       sw, block,190					       prestera_flow_block_release);191		if (IS_ERR(block_cb)) {192			prestera_flow_block_destroy(block);193			return ERR_CAST(block_cb);194		}195 196		block->block_cb = block_cb;197		*register_block = true;198	} else {199		block = flow_block_cb_priv(block_cb);200		*register_block = false;201	}202 203	flow_block_cb_incref(block_cb);204 205	return block;206}207 208static void prestera_flow_block_put(struct prestera_flow_block *block)209{210	struct flow_block_cb *block_cb = block->block_cb;211 212	if (flow_block_cb_decref(block_cb))213		return;214 215	flow_block_cb_free(block_cb);216	prestera_flow_block_destroy(block);217}218 219static int prestera_setup_flow_block_bind(struct prestera_port *port,220					  struct flow_block_offload *f, bool ingress)221{222	struct prestera_switch *sw = port->sw;223	struct prestera_flow_block *block;224	struct flow_block_cb *block_cb;225	bool register_block;226	int err;227 228	block = prestera_flow_block_get(sw, f, &register_block, ingress);229	if (IS_ERR(block))230		return PTR_ERR(block);231 232	block_cb = block->block_cb;233 234	err = prestera_flow_block_bind(block, port);235	if (err)236		goto err_block_bind;237 238	if (register_block) {239		flow_block_cb_add(block_cb, f);240		list_add_tail(&block_cb->driver_list, &prestera_block_cb_list);241	}242 243	if (ingress)244		port->ingress_flow_block = block;245	else246		port->egress_flow_block = block;247 248	return 0;249 250err_block_bind:251	prestera_flow_block_put(block);252 253	return err;254}255 256static void prestera_setup_flow_block_unbind(struct prestera_port *port,257					     struct flow_block_offload *f, bool ingress)258{259	struct prestera_switch *sw = port->sw;260	struct prestera_flow_block *block;261	struct flow_block_cb *block_cb;262	int err;263 264	block_cb = flow_block_cb_lookup(f->block, prestera_flow_block_cb, sw);265	if (!block_cb)266		return;267 268	block = flow_block_cb_priv(block_cb);269 270	prestera_mall_destroy(block);271 272	err = prestera_flow_block_unbind(block, port);273	if (err)274		goto error;275 276	if (!flow_block_cb_decref(block_cb)) {277		flow_block_cb_remove(block_cb, f);278		list_del(&block_cb->driver_list);279	}280error:281	if (ingress)282		port->ingress_flow_block = NULL;283	else284		port->egress_flow_block = NULL;285}286 287static int prestera_setup_flow_block_clsact(struct prestera_port *port,288					    struct flow_block_offload *f,289					    bool ingress)290{291	f->driver_block_list = &prestera_block_cb_list;292 293	switch (f->command) {294	case FLOW_BLOCK_BIND:295		return prestera_setup_flow_block_bind(port, f, ingress);296	case FLOW_BLOCK_UNBIND:297		prestera_setup_flow_block_unbind(port, f, ingress);298		return 0;299	default:300		return -EOPNOTSUPP;301	}302}303 304int prestera_flow_block_setup(struct prestera_port *port,305			      struct flow_block_offload *f)306{307	switch (f->binder_type) {308	case FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS:309		return prestera_setup_flow_block_clsact(port, f, true);310	case FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS:311		return prestera_setup_flow_block_clsact(port, f, false);312	default:313		return -EOPNOTSUPP;314	}315}316