net: marvell: prestera: Add matchall support
- Introduce matchall filter support - Add SPAN API to configure port mirroring. - Add tc mirror action. At this moment, only mirror (egress) action is supported. Example: tc filter ... action mirred egress mirror dev DEV Co-developed-by: Volodymyr Mytnyk <vmytnyk@marvell.com> Signed-off-by: Volodymyr Mytnyk <vmytnyk@marvell.com> Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu> Signed-off-by: Vadym Kochan <vkochan@marvell.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
8b474a9f6b
commit
13defa275e
@ -3,6 +3,6 @@ obj-$(CONFIG_PRESTERA) += prestera.o
|
||||
prestera-objs := prestera_main.o prestera_hw.o prestera_dsa.o \
|
||||
prestera_rxtx.o prestera_devlink.o prestera_ethtool.o \
|
||||
prestera_switchdev.o prestera_acl.o prestera_flow.o \
|
||||
prestera_flower.o
|
||||
prestera_flower.o prestera_span.o
|
||||
|
||||
obj-$(CONFIG_PRESTERA_PCI) += prestera_pci.o
|
||||
|
@ -172,6 +172,7 @@ struct prestera_event {
|
||||
};
|
||||
|
||||
struct prestera_switchdev;
|
||||
struct prestera_span;
|
||||
struct prestera_rxtx;
|
||||
struct prestera_trap_data;
|
||||
struct prestera_acl;
|
||||
@ -181,6 +182,7 @@ struct prestera_switch {
|
||||
struct prestera_switchdev *swdev;
|
||||
struct prestera_rxtx *rxtx;
|
||||
struct prestera_acl *acl;
|
||||
struct prestera_span *span;
|
||||
struct list_head event_handlers;
|
||||
struct notifier_block netdev_nb;
|
||||
struct prestera_trap_data *trap_data;
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "prestera.h"
|
||||
#include "prestera_hw.h"
|
||||
#include "prestera_acl.h"
|
||||
#include "prestera_span.h"
|
||||
|
||||
struct prestera_acl {
|
||||
struct prestera_switch *sw;
|
||||
@ -127,6 +128,7 @@ int prestera_acl_block_bind(struct prestera_flow_block *block,
|
||||
binding = kzalloc(sizeof(*binding), GFP_KERNEL);
|
||||
if (!binding)
|
||||
return -ENOMEM;
|
||||
binding->span_id = PRESTERA_SPAN_INVALID_ID;
|
||||
binding->port = port;
|
||||
|
||||
err = prestera_hw_acl_port_bind(port, block->ruleset->id);
|
||||
|
@ -36,6 +36,7 @@ struct prestera_acl_ruleset;
|
||||
struct prestera_flow_block_binding {
|
||||
struct list_head list;
|
||||
struct prestera_port *port;
|
||||
int span_id;
|
||||
};
|
||||
|
||||
struct prestera_flow_block {
|
||||
|
@ -7,10 +7,25 @@
|
||||
#include "prestera.h"
|
||||
#include "prestera_acl.h"
|
||||
#include "prestera_flow.h"
|
||||
#include "prestera_span.h"
|
||||
#include "prestera_flower.h"
|
||||
|
||||
static LIST_HEAD(prestera_block_cb_list);
|
||||
|
||||
static int prestera_flow_block_mall_cb(struct prestera_flow_block *block,
|
||||
struct tc_cls_matchall_offload *f)
|
||||
{
|
||||
switch (f->command) {
|
||||
case TC_CLSMATCHALL_REPLACE:
|
||||
return prestera_span_replace(block, f);
|
||||
case TC_CLSMATCHALL_DESTROY:
|
||||
prestera_span_destroy(block);
|
||||
return 0;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int prestera_flow_block_flower_cb(struct prestera_flow_block *block,
|
||||
struct flow_cls_offload *f)
|
||||
{
|
||||
@ -38,6 +53,8 @@ static int prestera_flow_block_cb(enum tc_setup_type type,
|
||||
switch (type) {
|
||||
case TC_SETUP_CLSFLOWER:
|
||||
return prestera_flow_block_flower_cb(block, type_data);
|
||||
case TC_SETUP_CLSMATCHALL:
|
||||
return prestera_flow_block_mall_cb(block, type_data);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@ -143,6 +160,8 @@ static void prestera_setup_flow_block_unbind(struct prestera_port *port,
|
||||
|
||||
block = flow_block_cb_priv(block_cb);
|
||||
|
||||
prestera_span_destroy(block);
|
||||
|
||||
err = prestera_acl_block_unbind(block, port);
|
||||
if (err)
|
||||
goto error;
|
||||
|
@ -56,6 +56,11 @@ enum prestera_cmd_type_t {
|
||||
|
||||
PRESTERA_CMD_TYPE_STP_PORT_SET = 0x1000,
|
||||
|
||||
PRESTERA_CMD_TYPE_SPAN_GET = 0x1100,
|
||||
PRESTERA_CMD_TYPE_SPAN_BIND = 0x1101,
|
||||
PRESTERA_CMD_TYPE_SPAN_UNBIND = 0x1102,
|
||||
PRESTERA_CMD_TYPE_SPAN_RELEASE = 0x1103,
|
||||
|
||||
PRESTERA_CMD_TYPE_CPU_CODE_COUNTERS_GET = 0x2000,
|
||||
|
||||
PRESTERA_CMD_TYPE_ACK = 0x10000,
|
||||
@ -377,6 +382,18 @@ struct prestera_msg_acl_ruleset_resp {
|
||||
u16 id;
|
||||
};
|
||||
|
||||
struct prestera_msg_span_req {
|
||||
struct prestera_msg_cmd cmd;
|
||||
u32 port;
|
||||
u32 dev;
|
||||
u8 id;
|
||||
} __packed __aligned(4);
|
||||
|
||||
struct prestera_msg_span_resp {
|
||||
struct prestera_msg_ret ret;
|
||||
u8 id;
|
||||
} __packed __aligned(4);
|
||||
|
||||
struct prestera_msg_stp_req {
|
||||
struct prestera_msg_cmd cmd;
|
||||
u32 port;
|
||||
@ -1055,6 +1072,58 @@ int prestera_hw_acl_port_unbind(const struct prestera_port *port,
|
||||
&req.cmd, sizeof(req));
|
||||
}
|
||||
|
||||
int prestera_hw_span_get(const struct prestera_port *port, u8 *span_id)
|
||||
{
|
||||
struct prestera_msg_span_resp resp;
|
||||
struct prestera_msg_span_req req = {
|
||||
.port = port->hw_id,
|
||||
.dev = port->dev_id,
|
||||
};
|
||||
int err;
|
||||
|
||||
err = prestera_cmd_ret(port->sw, PRESTERA_CMD_TYPE_SPAN_GET,
|
||||
&req.cmd, sizeof(req), &resp.ret, sizeof(resp));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*span_id = resp.id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prestera_hw_span_bind(const struct prestera_port *port, u8 span_id)
|
||||
{
|
||||
struct prestera_msg_span_req req = {
|
||||
.port = port->hw_id,
|
||||
.dev = port->dev_id,
|
||||
.id = span_id,
|
||||
};
|
||||
|
||||
return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_SPAN_BIND,
|
||||
&req.cmd, sizeof(req));
|
||||
}
|
||||
|
||||
int prestera_hw_span_unbind(const struct prestera_port *port)
|
||||
{
|
||||
struct prestera_msg_span_req req = {
|
||||
.port = port->hw_id,
|
||||
.dev = port->dev_id,
|
||||
};
|
||||
|
||||
return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_SPAN_UNBIND,
|
||||
&req.cmd, sizeof(req));
|
||||
}
|
||||
|
||||
int prestera_hw_span_release(struct prestera_switch *sw, u8 span_id)
|
||||
{
|
||||
struct prestera_msg_span_req req = {
|
||||
.id = span_id
|
||||
};
|
||||
|
||||
return prestera_cmd(sw, PRESTERA_CMD_TYPE_SPAN_RELEASE,
|
||||
&req.cmd, sizeof(req));
|
||||
}
|
||||
|
||||
int prestera_hw_port_type_get(const struct prestera_port *port, u8 *type)
|
||||
{
|
||||
struct prestera_msg_port_attr_req req = {
|
||||
|
@ -188,6 +188,12 @@ int prestera_hw_acl_port_bind(const struct prestera_port *port,
|
||||
int prestera_hw_acl_port_unbind(const struct prestera_port *port,
|
||||
u16 ruleset_id);
|
||||
|
||||
/* SPAN API */
|
||||
int prestera_hw_span_get(const struct prestera_port *port, u8 *span_id);
|
||||
int prestera_hw_span_bind(const struct prestera_port *port, u8 span_id);
|
||||
int prestera_hw_span_unbind(const struct prestera_port *port);
|
||||
int prestera_hw_span_release(struct prestera_switch *sw, u8 span_id);
|
||||
|
||||
/* Event handlers */
|
||||
int prestera_hw_event_handler_register(struct prestera_switch *sw,
|
||||
enum prestera_event_type type,
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "prestera_hw.h"
|
||||
#include "prestera_acl.h"
|
||||
#include "prestera_flow.h"
|
||||
#include "prestera_span.h"
|
||||
#include "prestera_rxtx.h"
|
||||
#include "prestera_devlink.h"
|
||||
#include "prestera_ethtool.h"
|
||||
@ -845,6 +846,10 @@ static int prestera_switch_init(struct prestera_switch *sw)
|
||||
if (err)
|
||||
goto err_acl_init;
|
||||
|
||||
err = prestera_span_init(sw);
|
||||
if (err)
|
||||
goto err_span_init;
|
||||
|
||||
err = prestera_devlink_register(sw);
|
||||
if (err)
|
||||
goto err_dl_register;
|
||||
@ -864,6 +869,8 @@ err_ports_create:
|
||||
err_lag_init:
|
||||
prestera_devlink_unregister(sw);
|
||||
err_dl_register:
|
||||
prestera_span_fini(sw);
|
||||
err_span_init:
|
||||
prestera_acl_fini(sw);
|
||||
err_acl_init:
|
||||
prestera_event_handlers_unregister(sw);
|
||||
@ -883,6 +890,7 @@ static void prestera_switch_fini(struct prestera_switch *sw)
|
||||
prestera_destroy_ports(sw);
|
||||
prestera_lag_fini(sw);
|
||||
prestera_devlink_unregister(sw);
|
||||
prestera_span_fini(sw);
|
||||
prestera_acl_fini(sw);
|
||||
prestera_event_handlers_unregister(sw);
|
||||
prestera_rxtx_switch_fini(sw);
|
||||
|
239
drivers/net/ethernet/marvell/prestera/prestera_span.c
Normal file
239
drivers/net/ethernet/marvell/prestera/prestera_span.c
Normal file
@ -0,0 +1,239 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
|
||||
/* Copyright (c) 2020 Marvell International Ltd. All rights reserved */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include "prestera.h"
|
||||
#include "prestera_hw.h"
|
||||
#include "prestera_acl.h"
|
||||
#include "prestera_span.h"
|
||||
|
||||
struct prestera_span_entry {
|
||||
struct list_head list;
|
||||
struct prestera_port *port;
|
||||
refcount_t ref_count;
|
||||
u8 id;
|
||||
};
|
||||
|
||||
struct prestera_span {
|
||||
struct prestera_switch *sw;
|
||||
struct list_head entries;
|
||||
};
|
||||
|
||||
static struct prestera_span_entry *
|
||||
prestera_span_entry_create(struct prestera_port *port, u8 span_id)
|
||||
{
|
||||
struct prestera_span_entry *entry;
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
refcount_set(&entry->ref_count, 1);
|
||||
entry->port = port;
|
||||
entry->id = span_id;
|
||||
list_add_tail(&entry->list, &port->sw->span->entries);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void prestera_span_entry_del(struct prestera_span_entry *entry)
|
||||
{
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
static struct prestera_span_entry *
|
||||
prestera_span_entry_find_by_id(struct prestera_span *span, u8 span_id)
|
||||
{
|
||||
struct prestera_span_entry *entry;
|
||||
|
||||
list_for_each_entry(entry, &span->entries, list) {
|
||||
if (entry->id == span_id)
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct prestera_span_entry *
|
||||
prestera_span_entry_find_by_port(struct prestera_span *span,
|
||||
struct prestera_port *port)
|
||||
{
|
||||
struct prestera_span_entry *entry;
|
||||
|
||||
list_for_each_entry(entry, &span->entries, list) {
|
||||
if (entry->port == port)
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int prestera_span_get(struct prestera_port *port, u8 *span_id)
|
||||
{
|
||||
u8 new_span_id;
|
||||
struct prestera_switch *sw = port->sw;
|
||||
struct prestera_span_entry *entry;
|
||||
int err;
|
||||
|
||||
entry = prestera_span_entry_find_by_port(sw->span, port);
|
||||
if (entry) {
|
||||
refcount_inc(&entry->ref_count);
|
||||
*span_id = entry->id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = prestera_hw_span_get(port, &new_span_id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
entry = prestera_span_entry_create(port, new_span_id);
|
||||
if (IS_ERR(entry)) {
|
||||
prestera_hw_span_release(sw, new_span_id);
|
||||
return PTR_ERR(entry);
|
||||
}
|
||||
|
||||
*span_id = new_span_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prestera_span_put(struct prestera_switch *sw, u8 span_id)
|
||||
{
|
||||
struct prestera_span_entry *entry;
|
||||
int err;
|
||||
|
||||
entry = prestera_span_entry_find_by_id(sw->span, span_id);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
if (!refcount_dec_and_test(&entry->ref_count))
|
||||
return 0;
|
||||
|
||||
err = prestera_hw_span_release(sw, span_id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
prestera_span_entry_del(entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prestera_span_rule_add(struct prestera_flow_block_binding *binding,
|
||||
struct prestera_port *to_port)
|
||||
{
|
||||
struct prestera_switch *sw = binding->port->sw;
|
||||
u8 span_id;
|
||||
int err;
|
||||
|
||||
if (binding->span_id != PRESTERA_SPAN_INVALID_ID)
|
||||
/* port already in mirroring */
|
||||
return -EEXIST;
|
||||
|
||||
err = prestera_span_get(to_port, &span_id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = prestera_hw_span_bind(binding->port, span_id);
|
||||
if (err) {
|
||||
prestera_span_put(sw, span_id);
|
||||
return err;
|
||||
}
|
||||
|
||||
binding->span_id = span_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prestera_span_rule_del(struct prestera_flow_block_binding *binding)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = prestera_hw_span_unbind(binding->port);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = prestera_span_put(binding->port->sw, binding->span_id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
binding->span_id = PRESTERA_SPAN_INVALID_ID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prestera_span_replace(struct prestera_flow_block *block,
|
||||
struct tc_cls_matchall_offload *f)
|
||||
{
|
||||
struct prestera_flow_block_binding *binding;
|
||||
__be16 protocol = f->common.protocol;
|
||||
struct flow_action_entry *act;
|
||||
struct prestera_port *port;
|
||||
int err;
|
||||
|
||||
if (!flow_offload_has_one_action(&f->rule->action)) {
|
||||
NL_SET_ERR_MSG(f->common.extack,
|
||||
"Only singular actions are supported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
act = &f->rule->action.entries[0];
|
||||
|
||||
if (!prestera_netdev_check(act->dev)) {
|
||||
NL_SET_ERR_MSG(f->common.extack,
|
||||
"Only Marvell Prestera port is supported");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!tc_cls_can_offload_and_chain0(act->dev, &f->common))
|
||||
return -EOPNOTSUPP;
|
||||
if (act->id != FLOW_ACTION_MIRRED)
|
||||
return -EOPNOTSUPP;
|
||||
if (protocol != htons(ETH_P_ALL))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
port = netdev_priv(act->dev);
|
||||
|
||||
list_for_each_entry(binding, &block->binding_list, list) {
|
||||
err = prestera_span_rule_add(binding, port);
|
||||
if (err)
|
||||
goto rollback;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
rollback:
|
||||
list_for_each_entry_continue_reverse(binding,
|
||||
&block->binding_list, list)
|
||||
prestera_span_rule_del(binding);
|
||||
return err;
|
||||
}
|
||||
|
||||
void prestera_span_destroy(struct prestera_flow_block *block)
|
||||
{
|
||||
struct prestera_flow_block_binding *binding;
|
||||
|
||||
list_for_each_entry(binding, &block->binding_list, list)
|
||||
prestera_span_rule_del(binding);
|
||||
}
|
||||
|
||||
int prestera_span_init(struct prestera_switch *sw)
|
||||
{
|
||||
struct prestera_span *span;
|
||||
|
||||
span = kzalloc(sizeof(*span), GFP_KERNEL);
|
||||
if (!span)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&span->entries);
|
||||
|
||||
sw->span = span;
|
||||
span->sw = sw;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void prestera_span_fini(struct prestera_switch *sw)
|
||||
{
|
||||
struct prestera_span *span = sw->span;
|
||||
|
||||
WARN_ON(!list_empty(&span->entries));
|
||||
kfree(span);
|
||||
}
|
20
drivers/net/ethernet/marvell/prestera/prestera_span.h
Normal file
20
drivers/net/ethernet/marvell/prestera/prestera_span.h
Normal file
@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
|
||||
/* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved. */
|
||||
|
||||
#ifndef _PRESTERA_SPAN_H_
|
||||
#define _PRESTERA_SPAN_H_
|
||||
|
||||
#include <net/pkt_cls.h>
|
||||
|
||||
#define PRESTERA_SPAN_INVALID_ID -1
|
||||
|
||||
struct prestera_switch;
|
||||
struct prestera_flow_block;
|
||||
|
||||
int prestera_span_init(struct prestera_switch *sw);
|
||||
void prestera_span_fini(struct prestera_switch *sw);
|
||||
int prestera_span_replace(struct prestera_flow_block *block,
|
||||
struct tc_cls_matchall_offload *f);
|
||||
void prestera_span_destroy(struct prestera_flow_block *block);
|
||||
|
||||
#endif /* _PRESTERA_SPAN_H_ */
|
Loading…
x
Reference in New Issue
Block a user