diff mbox series

[RFC,net-next,10/14] net: vlan: Propagate MC addresses with VID through switchdev

Message ID 20190115194130.19896-11-f.fainelli@gmail.com
State RFC
Delegated to: David Miller
Headers show
Series net: dsa: management mode for bcm_sf2 | expand

Commit Message

Florian Fainelli Jan. 15, 2019, 7:41 p.m. UTC
The VLAN real device could be an Ethernet switch port and that switch
might have VLAN filtering globally enabled (because of a bridge
requesting VLAN filtering on the switch on another port) and so when
programming multicast addresses, we need the multicast filter
programming to be aware of the correct VLAN ID as well.

Ethernet drivers that do not implement switchdev_port_{add,del}
operations and do not specifically check for SWITCHDEV_OBJ_ID_HOST_MDB
are not affected by that change.

Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
 net/8021q/vlan_dev.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
diff mbox series

Patch

diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index b2d9c8f27cd7..ea2ef9d78dcb 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -312,6 +312,43 @@  static int vlan_dev_open(struct net_device *dev)
 	return err;
 }
 
+static int vlan_dev_sync_unsync_mc_addr(struct net_device *dev,
+                                        const unsigned char *addr,
+                                        bool add)
+{
+	struct net_device *real_dev = vlan_dev_priv(dev)->real_dev;
+	struct switchdev_obj_port_mdb mdb = {
+		.obj = {
+			.orig_dev = dev,
+			.id = SWITCHDEV_OBJ_ID_HOST_MDB,
+			.flags = SWITCHDEV_F_DEFER,
+		},
+		.vid = vlan_dev_vlan_id(dev),
+	};
+	int ret = -EOPNOTSUPP;
+
+	ether_addr_copy(mdb.addr, addr);
+        if (add)
+		ret = switchdev_port_obj_add(real_dev, &mdb.obj, NULL);
+        else
+		ret = switchdev_port_obj_del(real_dev, &mdb.obj);
+
+	return ret;
+}
+
+static int vlan_dev_sync_mc_addr(struct net_device *dev,
+                                 const unsigned char *addr)
+{
+	return vlan_dev_sync_unsync_mc_addr(dev, addr, true);
+}
+
+static int vlan_dev_unsync_mc_addr(struct net_device *dev,
+                                   const unsigned char *addr)
+{
+	return vlan_dev_sync_unsync_mc_addr(dev, addr, false);
+}
+
+
 static int vlan_dev_stop(struct net_device *dev)
 {
 	struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
@@ -319,6 +356,7 @@  static int vlan_dev_stop(struct net_device *dev)
 
 	dev_mc_unsync(real_dev, dev);
 	dev_uc_unsync(real_dev, dev);
+	__hw_addr_unsync_dev(&dev->mc, dev, vlan_dev_unsync_mc_addr);
 	if (dev->flags & IFF_ALLMULTI)
 		dev_set_allmulti(real_dev, -1);
 	if (dev->flags & IFF_PROMISC)
@@ -483,6 +521,8 @@  static void vlan_dev_change_rx_flags(struct net_device *dev, int change)
 
 static void vlan_dev_set_rx_mode(struct net_device *vlan_dev)
 {
+	__hw_addr_sync_dev(&vlan_dev->mc, vlan_dev, vlan_dev_sync_mc_addr,
+			   vlan_dev_unsync_mc_addr);
 	dev_mc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev);
 	dev_uc_sync(vlan_dev_priv(vlan_dev)->real_dev, vlan_dev);
 }