Commit 1930b603 authored by David S. Miller's avatar David S. Miller

Merge branch 'dsa-port-mirroring'

Florian Fainelli says:

====================
net: dsa: Port mirroring support

This patch series adds support for port mirroring in the two
Broadcom switch drivers. The major part of the functional are actually with
the plumbing between tc and the drivers.

Changes in v5:

- Added Jiri's Reviewed-by tag to first patch
- rebase against latest net-next/master after bcm_sf2 CFP series

Changes in v4:

- rebased against latest net-next/master after Vivien's changes

Changes in v3:

- removed multiline comments from added structures
- simplify error handling in dsa_slave_add_cls_matchall

Changes in v2:

- fixed filter removal logic to disable the ingress or egress mirroring
  when there are no longer ports being monitored in ingress or egress

- removed a stray list_head in dsa_port structure that is not used

Tested using the two iproute2 examples:

      tc qdisc  add dev eth1 handle ffff: ingress
      tc filter add dev eth1 parent ffff:           \
               matchall skip_sw                      \
               action mirred egress mirror           \
               dev eth2
      tc qdisc add dev eth1 handle 1: root prio
      tc filter add dev eth1 parent 1:               \
               matchall skip_sw                       \
               action mirred egress mirror            \
               dev eth2
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4be99934 ec960de6
...@@ -1450,6 +1450,71 @@ static enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds) ...@@ -1450,6 +1450,71 @@ static enum dsa_tag_protocol b53_get_tag_protocol(struct dsa_switch *ds)
return DSA_TAG_PROTO_NONE; return DSA_TAG_PROTO_NONE;
} }
int b53_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror, bool ingress)
{
struct b53_device *dev = ds->priv;
u16 reg, loc;
if (ingress)
loc = B53_IG_MIR_CTL;
else
loc = B53_EG_MIR_CTL;
b53_read16(dev, B53_MGMT_PAGE, loc, &reg);
reg &= ~MIRROR_MASK;
reg |= BIT(port);
b53_write16(dev, B53_MGMT_PAGE, loc, reg);
b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &reg);
reg &= ~CAP_PORT_MASK;
reg |= mirror->to_local_port;
reg |= MIRROR_EN;
b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);
return 0;
}
EXPORT_SYMBOL(b53_mirror_add);
void b53_mirror_del(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror)
{
struct b53_device *dev = ds->priv;
bool loc_disable = false, other_loc_disable = false;
u16 reg, loc;
if (mirror->ingress)
loc = B53_IG_MIR_CTL;
else
loc = B53_EG_MIR_CTL;
/* Update the desired ingress/egress register */
b53_read16(dev, B53_MGMT_PAGE, loc, &reg);
reg &= ~BIT(port);
if (!(reg & MIRROR_MASK))
loc_disable = true;
b53_write16(dev, B53_MGMT_PAGE, loc, reg);
/* Now look at the other one to know if we can disable mirroring
* entirely
*/
if (mirror->ingress)
b53_read16(dev, B53_MGMT_PAGE, B53_EG_MIR_CTL, &reg);
else
b53_read16(dev, B53_MGMT_PAGE, B53_IG_MIR_CTL, &reg);
if (!(reg & MIRROR_MASK))
other_loc_disable = true;
b53_read16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, &reg);
/* Both no longer have ports, let's disable mirroring */
if (loc_disable && other_loc_disable) {
reg &= ~MIRROR_EN;
reg &= ~mirror->to_local_port;
}
b53_write16(dev, B53_MGMT_PAGE, B53_MIR_CAP_CTL, reg);
}
EXPORT_SYMBOL(b53_mirror_del);
static const struct dsa_switch_ops b53_switch_ops = { static const struct dsa_switch_ops b53_switch_ops = {
.get_tag_protocol = b53_get_tag_protocol, .get_tag_protocol = b53_get_tag_protocol,
.setup = b53_setup, .setup = b53_setup,
...@@ -1474,6 +1539,8 @@ static const struct dsa_switch_ops b53_switch_ops = { ...@@ -1474,6 +1539,8 @@ static const struct dsa_switch_ops b53_switch_ops = {
.port_fdb_dump = b53_fdb_dump, .port_fdb_dump = b53_fdb_dump,
.port_fdb_add = b53_fdb_add, .port_fdb_add = b53_fdb_add,
.port_fdb_del = b53_fdb_del, .port_fdb_del = b53_fdb_del,
.port_mirror_add = b53_mirror_add,
.port_mirror_del = b53_mirror_del,
}; };
struct b53_chip_data { struct b53_chip_data {
......
...@@ -407,5 +407,9 @@ int b53_fdb_del(struct dsa_switch *ds, int port, ...@@ -407,5 +407,9 @@ int b53_fdb_del(struct dsa_switch *ds, int port,
int b53_fdb_dump(struct dsa_switch *ds, int port, int b53_fdb_dump(struct dsa_switch *ds, int port,
struct switchdev_obj_port_fdb *fdb, struct switchdev_obj_port_fdb *fdb,
int (*cb)(struct switchdev_obj *obj)); int (*cb)(struct switchdev_obj *obj));
int b53_mirror_add(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror, bool ingress);
void b53_mirror_del(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror);
#endif #endif
...@@ -206,6 +206,38 @@ ...@@ -206,6 +206,38 @@
#define BRCM_HDR_P8_EN BIT(0) /* Enable tagging on port 8 */ #define BRCM_HDR_P8_EN BIT(0) /* Enable tagging on port 8 */
#define BRCM_HDR_P5_EN BIT(1) /* Enable tagging on port 5 */ #define BRCM_HDR_P5_EN BIT(1) /* Enable tagging on port 5 */
/* Mirror capture control register (16 bit) */
#define B53_MIR_CAP_CTL 0x10
#define CAP_PORT_MASK 0xf
#define BLK_NOT_MIR BIT(14)
#define MIRROR_EN BIT(15)
/* Ingress mirror control register (16 bit) */
#define B53_IG_MIR_CTL 0x12
#define MIRROR_MASK 0x1ff
#define DIV_EN BIT(13)
#define MIRROR_FILTER_MASK 0x3
#define MIRROR_FILTER_SHIFT 14
#define MIRROR_ALL 0
#define MIRROR_DA 1
#define MIRROR_SA 2
/* Ingress mirror divider register (16 bit) */
#define B53_IG_MIR_DIV 0x14
#define IN_MIRROR_DIV_MASK 0x3ff
/* Ingress mirror MAC address register (48 bit) */
#define B53_IG_MIR_MAC 0x16
/* Egress mirror control register (16 bit) */
#define B53_EG_MIR_CTL 0x1C
/* Egress mirror divider register (16 bit) */
#define B53_EG_MIR_DIV 0x1E
/* Egress mirror MAC address register (48 bit) */
#define B53_EG_MIR_MAC 0x20
/* Device ID register (8 or 32 bit) */ /* Device ID register (8 or 32 bit) */
#define B53_DEVICE_ID 0x30 #define B53_DEVICE_ID 0x30
......
...@@ -1047,6 +1047,8 @@ static const struct dsa_switch_ops bcm_sf2_ops = { ...@@ -1047,6 +1047,8 @@ static const struct dsa_switch_ops bcm_sf2_ops = {
.port_fdb_del = b53_fdb_del, .port_fdb_del = b53_fdb_del,
.get_rxnfc = bcm_sf2_get_rxnfc, .get_rxnfc = bcm_sf2_get_rxnfc,
.set_rxnfc = bcm_sf2_set_rxnfc, .set_rxnfc = bcm_sf2_set_rxnfc,
.port_mirror_add = b53_mirror_add,
.port_mirror_del = b53_mirror_del,
}; };
struct bcm_sf2_of_data { struct bcm_sf2_of_data {
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
#include <linux/phy_fixed.h> #include <linux/phy_fixed.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
struct tc_action;
enum dsa_tag_protocol { enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = 0, DSA_TAG_PROTO_NONE = 0,
DSA_TAG_PROTO_DSA, DSA_TAG_PROTO_DSA,
...@@ -139,6 +141,28 @@ struct dsa_switch_tree { ...@@ -139,6 +141,28 @@ struct dsa_switch_tree {
const struct dsa_device_ops *tag_ops; const struct dsa_device_ops *tag_ops;
}; };
/* TC matchall action types, only mirroring for now */
enum dsa_port_mall_action_type {
DSA_PORT_MALL_MIRROR,
};
/* TC mirroring entry */
struct dsa_mall_mirror_tc_entry {
u8 to_local_port;
bool ingress;
};
/* TC matchall entry */
struct dsa_mall_tc_entry {
struct list_head list;
unsigned long cookie;
enum dsa_port_mall_action_type type;
union {
struct dsa_mall_mirror_tc_entry mirror;
};
};
struct dsa_port { struct dsa_port {
struct dsa_switch *ds; struct dsa_switch *ds;
unsigned int index; unsigned int index;
...@@ -385,6 +409,15 @@ struct dsa_switch_ops { ...@@ -385,6 +409,15 @@ struct dsa_switch_ops {
struct ethtool_rxnfc *nfc, u32 *rule_locs); struct ethtool_rxnfc *nfc, u32 *rule_locs);
int (*set_rxnfc)(struct dsa_switch *ds, int port, int (*set_rxnfc)(struct dsa_switch *ds, int port,
struct ethtool_rxnfc *nfc); struct ethtool_rxnfc *nfc);
/*
* TC integration
*/
int (*port_mirror_add)(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror,
bool ingress);
void (*port_mirror_del)(struct dsa_switch *ds, int port,
struct dsa_mall_mirror_tc_entry *mirror);
}; };
struct dsa_switch_driver { struct dsa_switch_driver {
......
...@@ -41,6 +41,9 @@ struct dsa_slave_priv { ...@@ -41,6 +41,9 @@ struct dsa_slave_priv {
#ifdef CONFIG_NET_POLL_CONTROLLER #ifdef CONFIG_NET_POLL_CONTROLLER
struct netpoll *netpoll; struct netpoll *netpoll;
#endif #endif
/* TC context */
struct list_head mall_tc_list;
}; };
/* dsa.c */ /* dsa.c */
......
...@@ -16,12 +16,17 @@ ...@@ -16,12 +16,17 @@
#include <linux/of_net.h> #include <linux/of_net.h>
#include <linux/of_mdio.h> #include <linux/of_mdio.h>
#include <linux/mdio.h> #include <linux/mdio.h>
#include <linux/list.h>
#include <net/rtnetlink.h> #include <net/rtnetlink.h>
#include <net/switchdev.h> #include <net/switchdev.h>
#include <net/pkt_cls.h>
#include <net/tc_act/tc_mirred.h>
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
#include <linux/netpoll.h> #include <linux/netpoll.h>
#include "dsa_priv.h" #include "dsa_priv.h"
static bool dsa_slave_dev_check(struct net_device *dev);
/* slave mii_bus handling ***************************************************/ /* slave mii_bus handling ***************************************************/
static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg) static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
{ {
...@@ -995,6 +1000,133 @@ static int dsa_slave_get_phys_port_name(struct net_device *dev, ...@@ -995,6 +1000,133 @@ static int dsa_slave_get_phys_port_name(struct net_device *dev,
return 0; return 0;
} }
static struct dsa_mall_tc_entry *
dsa_slave_mall_tc_entry_find(struct dsa_slave_priv *p,
unsigned long cookie)
{
struct dsa_mall_tc_entry *mall_tc_entry;
list_for_each_entry(mall_tc_entry, &p->mall_tc_list, list)
if (mall_tc_entry->cookie == cookie)
return mall_tc_entry;
return NULL;
}
static int dsa_slave_add_cls_matchall(struct net_device *dev,
__be16 protocol,
struct tc_cls_matchall_offload *cls,
bool ingress)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_mall_tc_entry *mall_tc_entry;
struct dsa_switch *ds = p->dp->ds;
struct net *net = dev_net(dev);
struct dsa_slave_priv *to_p;
struct net_device *to_dev;
const struct tc_action *a;
int err = -EOPNOTSUPP;
LIST_HEAD(actions);
int ifindex;
if (!ds->ops->port_mirror_add)
return err;
if (!tc_single_action(cls->exts))
return err;
tcf_exts_to_list(cls->exts, &actions);
a = list_first_entry(&actions, struct tc_action, list);
if (is_tcf_mirred_egress_mirror(a) && protocol == htons(ETH_P_ALL)) {
struct dsa_mall_mirror_tc_entry *mirror;
ifindex = tcf_mirred_ifindex(a);
to_dev = __dev_get_by_index(net, ifindex);
if (!to_dev)
return -EINVAL;
if (!dsa_slave_dev_check(to_dev))
return -EOPNOTSUPP;
mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL);
if (!mall_tc_entry)
return -ENOMEM;
mall_tc_entry->cookie = cls->cookie;
mall_tc_entry->type = DSA_PORT_MALL_MIRROR;
mirror = &mall_tc_entry->mirror;
to_p = netdev_priv(to_dev);
mirror->to_local_port = to_p->dp->index;
mirror->ingress = ingress;
err = ds->ops->port_mirror_add(ds, p->dp->index, mirror,
ingress);
if (err) {
kfree(mall_tc_entry);
return err;
}
list_add_tail(&mall_tc_entry->list, &p->mall_tc_list);
}
return 0;
}
static void dsa_slave_del_cls_matchall(struct net_device *dev,
struct tc_cls_matchall_offload *cls)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct dsa_mall_tc_entry *mall_tc_entry;
struct dsa_switch *ds = p->dp->ds;
if (!ds->ops->port_mirror_del)
return;
mall_tc_entry = dsa_slave_mall_tc_entry_find(p, cls->cookie);
if (!mall_tc_entry)
return;
list_del(&mall_tc_entry->list);
switch (mall_tc_entry->type) {
case DSA_PORT_MALL_MIRROR:
ds->ops->port_mirror_del(ds, p->dp->index,
&mall_tc_entry->mirror);
break;
default:
WARN_ON(1);
}
kfree(mall_tc_entry);
}
static int dsa_slave_setup_tc(struct net_device *dev, u32 handle,
__be16 protocol, struct tc_to_netdev *tc)
{
bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS);
int ret = -EOPNOTSUPP;
switch (tc->type) {
case TC_SETUP_MATCHALL:
switch (tc->cls_mall->command) {
case TC_CLSMATCHALL_REPLACE:
return dsa_slave_add_cls_matchall(dev, protocol,
tc->cls_mall,
ingress);
case TC_CLSMATCHALL_DESTROY:
dsa_slave_del_cls_matchall(dev, tc->cls_mall);
return 0;
}
default:
break;
}
return ret;
}
void dsa_cpu_port_ethtool_init(struct ethtool_ops *ops) void dsa_cpu_port_ethtool_init(struct ethtool_ops *ops)
{ {
ops->get_sset_count = dsa_cpu_port_get_sset_count; ops->get_sset_count = dsa_cpu_port_get_sset_count;
...@@ -1069,6 +1201,7 @@ static const struct net_device_ops dsa_slave_netdev_ops = { ...@@ -1069,6 +1201,7 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
.ndo_bridge_setlink = switchdev_port_bridge_setlink, .ndo_bridge_setlink = switchdev_port_bridge_setlink,
.ndo_bridge_dellink = switchdev_port_bridge_dellink, .ndo_bridge_dellink = switchdev_port_bridge_dellink,
.ndo_get_phys_port_name = dsa_slave_get_phys_port_name, .ndo_get_phys_port_name = dsa_slave_get_phys_port_name,
.ndo_setup_tc = dsa_slave_setup_tc,
}; };
static const struct switchdev_ops dsa_slave_switchdev_ops = { static const struct switchdev_ops dsa_slave_switchdev_ops = {
...@@ -1285,7 +1418,8 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, ...@@ -1285,7 +1418,8 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
if (slave_dev == NULL) if (slave_dev == NULL)
return -ENOMEM; return -ENOMEM;
slave_dev->features = master->vlan_features; slave_dev->features = master->vlan_features | NETIF_F_HW_TC;
slave_dev->hw_features |= NETIF_F_HW_TC;
slave_dev->ethtool_ops = &dsa_slave_ethtool_ops; slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
eth_hw_addr_inherit(slave_dev, master); eth_hw_addr_inherit(slave_dev, master);
slave_dev->priv_flags |= IFF_NO_QUEUE; slave_dev->priv_flags |= IFF_NO_QUEUE;
...@@ -1304,6 +1438,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, ...@@ -1304,6 +1438,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
p = netdev_priv(slave_dev); p = netdev_priv(slave_dev);
p->dp = &ds->ports[port]; p->dp = &ds->ports[port];
INIT_LIST_HEAD(&p->mall_tc_list);
p->xmit = dst->tag_ops->xmit; p->xmit = dst->tag_ops->xmit;
p->old_pause = -1; p->old_pause = -1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment