Skip to content

Commit f50f212

Browse files
ffainellidavem330
authored andcommitted
net: dsa: Add plumbing for port mirroring
Add necessary plumbing at the slave network device level to have switch drivers implement ndo_setup_tc() and most particularly the cls_matchall classifier. We add support for two switch operations: port_add_mirror and port_del_mirror() which configure, on a per-port basis the mirror parameters requested from the cls_matchall classifier. Code is largely borrowed from the Mellanox Spectrum switch driver. Reviewed-by: Jiri Pirko <[email protected]> Signed-off-by: Florian Fainelli <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 4be9993 commit f50f212

File tree

3 files changed

+172
-1
lines changed

3 files changed

+172
-1
lines changed

include/net/dsa.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include <linux/phy_fixed.h>
2121
#include <linux/ethtool.h>
2222

23+
struct tc_action;
24+
2325
enum dsa_tag_protocol {
2426
DSA_TAG_PROTO_NONE = 0,
2527
DSA_TAG_PROTO_DSA,
@@ -139,6 +141,28 @@ struct dsa_switch_tree {
139141
const struct dsa_device_ops *tag_ops;
140142
};
141143

144+
/* TC matchall action types, only mirroring for now */
145+
enum dsa_port_mall_action_type {
146+
DSA_PORT_MALL_MIRROR,
147+
};
148+
149+
/* TC mirroring entry */
150+
struct dsa_mall_mirror_tc_entry {
151+
u8 to_local_port;
152+
bool ingress;
153+
};
154+
155+
/* TC matchall entry */
156+
struct dsa_mall_tc_entry {
157+
struct list_head list;
158+
unsigned long cookie;
159+
enum dsa_port_mall_action_type type;
160+
union {
161+
struct dsa_mall_mirror_tc_entry mirror;
162+
};
163+
};
164+
165+
142166
struct dsa_port {
143167
struct dsa_switch *ds;
144168
unsigned int index;
@@ -385,6 +409,15 @@ struct dsa_switch_ops {
385409
struct ethtool_rxnfc *nfc, u32 *rule_locs);
386410
int (*set_rxnfc)(struct dsa_switch *ds, int port,
387411
struct ethtool_rxnfc *nfc);
412+
413+
/*
414+
* TC integration
415+
*/
416+
int (*port_mirror_add)(struct dsa_switch *ds, int port,
417+
struct dsa_mall_mirror_tc_entry *mirror,
418+
bool ingress);
419+
void (*port_mirror_del)(struct dsa_switch *ds, int port,
420+
struct dsa_mall_mirror_tc_entry *mirror);
388421
};
389422

390423
struct dsa_switch_driver {

net/dsa/dsa_priv.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ struct dsa_slave_priv {
4141
#ifdef CONFIG_NET_POLL_CONTROLLER
4242
struct netpoll *netpoll;
4343
#endif
44+
45+
/* TC context */
46+
struct list_head mall_tc_list;
4447
};
4548

4649
/* dsa.c */

net/dsa/slave.c

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@
1616
#include <linux/of_net.h>
1717
#include <linux/of_mdio.h>
1818
#include <linux/mdio.h>
19+
#include <linux/list.h>
1920
#include <net/rtnetlink.h>
2021
#include <net/switchdev.h>
22+
#include <net/pkt_cls.h>
23+
#include <net/tc_act/tc_mirred.h>
2124
#include <linux/if_bridge.h>
2225
#include <linux/netpoll.h>
2326
#include "dsa_priv.h"
2427

28+
static bool dsa_slave_dev_check(struct net_device *dev);
29+
2530
/* slave mii_bus handling ***************************************************/
2631
static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
2732
{
@@ -995,6 +1000,133 @@ static int dsa_slave_get_phys_port_name(struct net_device *dev,
9951000
return 0;
9961001
}
9971002

1003+
static struct dsa_mall_tc_entry *
1004+
dsa_slave_mall_tc_entry_find(struct dsa_slave_priv *p,
1005+
unsigned long cookie)
1006+
{
1007+
struct dsa_mall_tc_entry *mall_tc_entry;
1008+
1009+
list_for_each_entry(mall_tc_entry, &p->mall_tc_list, list)
1010+
if (mall_tc_entry->cookie == cookie)
1011+
return mall_tc_entry;
1012+
1013+
return NULL;
1014+
}
1015+
1016+
static int dsa_slave_add_cls_matchall(struct net_device *dev,
1017+
__be16 protocol,
1018+
struct tc_cls_matchall_offload *cls,
1019+
bool ingress)
1020+
{
1021+
struct dsa_slave_priv *p = netdev_priv(dev);
1022+
struct dsa_mall_tc_entry *mall_tc_entry;
1023+
struct dsa_switch *ds = p->dp->ds;
1024+
struct net *net = dev_net(dev);
1025+
struct dsa_slave_priv *to_p;
1026+
struct net_device *to_dev;
1027+
const struct tc_action *a;
1028+
int err = -EOPNOTSUPP;
1029+
LIST_HEAD(actions);
1030+
int ifindex;
1031+
1032+
if (!ds->ops->port_mirror_add)
1033+
return err;
1034+
1035+
if (!tc_single_action(cls->exts))
1036+
return err;
1037+
1038+
tcf_exts_to_list(cls->exts, &actions);
1039+
a = list_first_entry(&actions, struct tc_action, list);
1040+
1041+
if (is_tcf_mirred_egress_mirror(a) && protocol == htons(ETH_P_ALL)) {
1042+
struct dsa_mall_mirror_tc_entry *mirror;
1043+
1044+
ifindex = tcf_mirred_ifindex(a);
1045+
to_dev = __dev_get_by_index(net, ifindex);
1046+
if (!to_dev)
1047+
return -EINVAL;
1048+
1049+
if (!dsa_slave_dev_check(to_dev))
1050+
return -EOPNOTSUPP;
1051+
1052+
mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL);
1053+
if (!mall_tc_entry)
1054+
return -ENOMEM;
1055+
1056+
mall_tc_entry->cookie = cls->cookie;
1057+
mall_tc_entry->type = DSA_PORT_MALL_MIRROR;
1058+
mirror = &mall_tc_entry->mirror;
1059+
1060+
to_p = netdev_priv(to_dev);
1061+
1062+
mirror->to_local_port = to_p->dp->index;
1063+
mirror->ingress = ingress;
1064+
1065+
err = ds->ops->port_mirror_add(ds, p->dp->index, mirror,
1066+
ingress);
1067+
if (err) {
1068+
kfree(mall_tc_entry);
1069+
return err;
1070+
}
1071+
1072+
list_add_tail(&mall_tc_entry->list, &p->mall_tc_list);
1073+
}
1074+
1075+
return 0;
1076+
}
1077+
1078+
static void dsa_slave_del_cls_matchall(struct net_device *dev,
1079+
struct tc_cls_matchall_offload *cls)
1080+
{
1081+
struct dsa_slave_priv *p = netdev_priv(dev);
1082+
struct dsa_mall_tc_entry *mall_tc_entry;
1083+
struct dsa_switch *ds = p->dp->ds;
1084+
1085+
if (!ds->ops->port_mirror_del)
1086+
return;
1087+
1088+
mall_tc_entry = dsa_slave_mall_tc_entry_find(p, cls->cookie);
1089+
if (!mall_tc_entry)
1090+
return;
1091+
1092+
list_del(&mall_tc_entry->list);
1093+
1094+
switch (mall_tc_entry->type) {
1095+
case DSA_PORT_MALL_MIRROR:
1096+
ds->ops->port_mirror_del(ds, p->dp->index,
1097+
&mall_tc_entry->mirror);
1098+
break;
1099+
default:
1100+
WARN_ON(1);
1101+
}
1102+
1103+
kfree(mall_tc_entry);
1104+
}
1105+
1106+
static int dsa_slave_setup_tc(struct net_device *dev, u32 handle,
1107+
__be16 protocol, struct tc_to_netdev *tc)
1108+
{
1109+
bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS);
1110+
int ret = -EOPNOTSUPP;
1111+
1112+
switch (tc->type) {
1113+
case TC_SETUP_MATCHALL:
1114+
switch (tc->cls_mall->command) {
1115+
case TC_CLSMATCHALL_REPLACE:
1116+
return dsa_slave_add_cls_matchall(dev, protocol,
1117+
tc->cls_mall,
1118+
ingress);
1119+
case TC_CLSMATCHALL_DESTROY:
1120+
dsa_slave_del_cls_matchall(dev, tc->cls_mall);
1121+
return 0;
1122+
}
1123+
default:
1124+
break;
1125+
}
1126+
1127+
return ret;
1128+
}
1129+
9981130
void dsa_cpu_port_ethtool_init(struct ethtool_ops *ops)
9991131
{
10001132
ops->get_sset_count = dsa_cpu_port_get_sset_count;
@@ -1069,6 +1201,7 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
10691201
.ndo_bridge_setlink = switchdev_port_bridge_setlink,
10701202
.ndo_bridge_dellink = switchdev_port_bridge_dellink,
10711203
.ndo_get_phys_port_name = dsa_slave_get_phys_port_name,
1204+
.ndo_setup_tc = dsa_slave_setup_tc,
10721205
};
10731206

10741207
static const struct switchdev_ops dsa_slave_switchdev_ops = {
@@ -1285,7 +1418,8 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
12851418
if (slave_dev == NULL)
12861419
return -ENOMEM;
12871420

1288-
slave_dev->features = master->vlan_features;
1421+
slave_dev->features = master->vlan_features | NETIF_F_HW_TC;
1422+
slave_dev->hw_features |= NETIF_F_HW_TC;
12891423
slave_dev->ethtool_ops = &dsa_slave_ethtool_ops;
12901424
eth_hw_addr_inherit(slave_dev, master);
12911425
slave_dev->priv_flags |= IFF_NO_QUEUE;
@@ -1304,6 +1438,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent,
13041438

13051439
p = netdev_priv(slave_dev);
13061440
p->dp = &ds->ports[port];
1441+
INIT_LIST_HEAD(&p->mall_tc_list);
13071442
p->xmit = dst->tag_ops->xmit;
13081443

13091444
p->old_pause = -1;

0 commit comments

Comments
 (0)