[OpenWrt-Devel] [PATCH 3/3] b53: create slave devices for ports

Alexandru Ardelean ardeleanalex at gmail.com
Wed Feb 25 10:24:24 EST 2015


Feature implemented and tested on BCM53128.

Slave devices logic copied from the Linux kernel from Marvell's DSA
driver ( linux/net/dsa/ ).
Also the logic for the Broadcom tag processing has been copied from there.

OpenWRT's eth_mangle_rx/tx() patch/code is being used to tap into
the packets to/from the ethernet chip since it's convenient.

This code will create lanX (X = 1..B53_N_PORTS) devices.
All traffic from the ethX device will be forwarded the proper lanX device.
So, sw_port0_traffic == lan1_traffic and so on.

The slave devices logic has been put into it's own file.
Should this logic be desired to be extended to swconfig or other
switch chips, it should be convenient to just move the slave.c/h files.

Note: if enable_vlan == 1, be sure to configure VLAN per lanX device
      in '/etc/config/network'

Signed-off-by: Alexandru Ardelean <ardeleanalex at gmail.com>
---
 .../generic/files/drivers/net/phy/b53/Makefile     |   2 +-
 .../generic/files/drivers/net/phy/b53/b53_common.c |   3 +
 .../generic/files/drivers/net/phy/b53/b53_hdr.c    | 114 +++++++++++-
 .../generic/files/drivers/net/phy/b53/b53_priv.h   |   2 +
 .../generic/files/drivers/net/phy/b53/slave.c      | 196 +++++++++++++++++++++
 .../generic/files/drivers/net/phy/b53/slave.h      |  38 ++++
 6 files changed, 352 insertions(+), 3 deletions(-)
 create mode 100644 target/linux/generic/files/drivers/net/phy/b53/slave.c
 create mode 100644 target/linux/generic/files/drivers/net/phy/b53/slave.h

diff --git a/target/linux/generic/files/drivers/net/phy/b53/Makefile b/target/linux/generic/files/drivers/net/phy/b53/Makefile
index 6c809f9..c74f82b 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/Makefile
+++ b/target/linux/generic/files/drivers/net/phy/b53/Makefile
@@ -1,5 +1,5 @@
 obj-$(CONFIG_B53)		+= b53_common.o
-obj-$(CONFIG_B53_HDR)		+= b53_hdr.o
+obj-$(CONFIG_B53_HDR)		+= b53_hdr.o slave.o
 
 obj-$(CONFIG_B53_PHY_FIXUP)	+= b53_phy_fixup.o
 
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
index 9459b22..3da9efe 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
@@ -1377,6 +1377,9 @@ static int b53_global_reset_switch(struct switch_dev *dev)
 	priv->enable_management = 0;
 #ifdef CONFIG_B53_HDR
 	priv->enable_brcm_hdr = 0;
+	/* Call this function before the memset on the priv->ports, 
+	 * otherwise we may leak devices */
+	b53_unregister_netdevs(priv);
 #endif
 
 	memset(priv->vlans, 0, sizeof(priv->vlans) * dev->vlans);
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_hdr.c b/target/linux/generic/files/drivers/net/phy/b53/b53_hdr.c
index 2a562a9..8fa7929 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_hdr.c
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_hdr.c
@@ -21,14 +21,32 @@
 
 #include "b53_regs.h"
 #include "b53_priv.h"
+#include "slave.h"
 
 /* This tag length is 4 bytes, older ones were 6 bytes, we do not
  * handle them
  */
 #define BRCM_HDR_LEN    4
 #define MIN_FRAME_LEN   64
+#define BRCM_IG_DSTMAP2_MASK 1
+#define BRCM_IG_DSTMAP1_MASK 0xff
+#define BRCM_EG_PID_MASK     0x1f
 
-static struct sk_buff *b53_mangle_tx(struct net_device *dev, struct sk_buff *skb)
+/* Ingress and egress opcodes */
+#define BRCM_OPCODE_SHIFT   5
+
+static inline void b53_brcm_tag_set(struct sk_buff *skb, int port_idx)
+{
+	u8 *brcm_tag = skb->data + 2 * ETH_ALEN;
+
+	brcm_tag[0] = (1 << BRCM_OPCODE_SHIFT);
+	brcm_tag[1] = 0;
+	brcm_tag[2] = (port_idx == 8) ? BRCM_IG_DSTMAP2_MASK : 0;
+	brcm_tag[3] = (1 << port_idx) & BRCM_IG_DSTMAP1_MASK;
+}
+
+static struct sk_buff *b53_mangle_tx_port(struct net_device *dev,
+			struct sk_buff *skb, struct slave_priv* p)
 {
 	if (unlikely(skb_headroom(skb) < BRCM_HDR_LEN)) {
 		if (pskb_expand_head(skb, BRCM_HDR_LEN, 0, GFP_ATOMIC) < 0)
@@ -40,7 +58,20 @@ static struct sk_buff *b53_mangle_tx(struct net_device *dev, struct sk_buff *skb
 	memmove(skb->data, skb->data + BRCM_HDR_LEN, 2 * ETH_ALEN);
 
 	/* Build the tag after the MAC Source Address */
-	memset(skb->data + 2 * ETH_ALEN, 0, BRCM_HDR_LEN);
+	if (!p) {
+		memset(skb->data + 2 * ETH_ALEN, 0, BRCM_HDR_LEN);
+	} else {
+		/* Register some TX stats for this device before
+		 * passing the skb to the parent device */
+		dev->stats.tx_packets++;
+		dev->stats.tx_bytes += skb->len;
+
+		b53_brcm_tag_set(skb, p->port_idx);
+
+		skb->dev = p->parent_dev;
+		skb->protocol = htons(ETH_P_DSA);
+	}
+
 #ifdef CONFIG_B53_HDR_TX_SW_PADDING
 	/* FIXME: we're doing some padding here for runt ( < 64 bytes) packets;
 	 *        some drivers/hw refuse to add hw padding for us after we add
@@ -64,12 +95,88 @@ out_err:
 	return NULL;
 }
 
+static struct sk_buff *b53_mangle_tx(struct net_device *dev, struct sk_buff *skb)
+{
+	/* The b53_mangle_tx() function can get called twice now that there are
+	 * slave devices: once for the lanX device, and one for the ethY device
+	 * which means that the Broadcom Header would get added twice.
+	 * Which is why the packet type has been marked by the slave lanX device
+	 * to tell us that we've tagged this packet already. */
+	if (unlikely(skb->protocol == htons(ETH_P_DSA)))
+		return skb;
+	return b53_mangle_tx_port(dev, skb, NULL);
+}
+
 static void b53_mangle_rx(struct net_device *dev, struct sk_buff *skb)
 {
+	struct b53_device *p = dev->phy_ptr;
+	u8 *brcm_tag;
+	u8 source_port;
+
+	/* We're only interested in the 4th byte of the Broadcom Header right now */
+	brcm_tag = skb->data + (2 * ETH_ALEN) + 3;
+	source_port = *brcm_tag & BRCM_EG_PID_MASK;
+
 	skb_pull(skb, BRCM_HDR_LEN);
 	memmove(skb->data,
 		skb->data - BRCM_HDR_LEN,
 		2 * ETH_ALEN);
+
+	if ((source_port < B53_N_PORTS) && (p->ports[source_port].port_dev)) {
+		skb->dev = p->ports[source_port].port_dev;
+		skb->dev->stats.rx_packets++;
+		skb->dev->stats.rx_bytes += skb->len;
+	}
+}
+
+static void b53_register_netdevs(struct b53_device *dev)
+{
+	int i;
+	bool unlock = false;
+
+	if (!rtnl_is_locked()) {
+		rtnl_lock();
+		unlock = true;
+	}
+	b53_for_each_port(dev, i) {
+		if (is_cpu_port(dev, i))
+			continue;
+		if (!(dev->enabled_ports & BIT(i))) {
+			if (dev->ports[i].port_dev) {
+				unregister_netdevice(dev->ports[i].port_dev);
+				dev->ports[i].port_dev = NULL;
+			}
+			continue;
+		}
+		/* Check if devices already exist for these devices */
+		if (!dev->ports[i].port_dev && 
+		    !(dev->ports[i].port_dev = slave_create(dev->eth_dev, i, b53_mangle_tx_port))) {
+			pr_warn("%s: can't create slave device for port %d\n",
+			        dev->sw_dev.name, i);
+			continue;
+		}
+	}
+	if (unlock)
+		rtnl_unlock();
+}
+
+void b53_unregister_netdevs(struct b53_device *dev)
+{
+	int i;
+	bool unlock = false;
+
+	if (!rtnl_is_locked()) {
+		rtnl_lock();
+		unlock = true;
+	}
+	b53_for_each_port(dev, i) {
+		if (!dev->ports[i].port_dev)
+			continue;
+		unregister_netdevice(dev->ports[i].port_dev);
+		dev->ports[i].port_dev = NULL;
+	}
+	if (unlock)
+		rtnl_unlock();
 }
 
 void b53_enable_brcm_hdr(struct b53_device *dev)
@@ -86,6 +193,7 @@ void b53_enable_brcm_hdr(struct b53_device *dev)
 	}
 	brcm_hdr_ctrl &= ~B53_BRCM_HDR_EN;
 	if (!dev->eth_dev) {
+		b53_unregister_netdevs(dev);
 		goto out;
 	}
 
@@ -93,6 +201,8 @@ void b53_enable_brcm_hdr(struct b53_device *dev)
 	if (!dev->enable_management)
 		goto out;
 
+	pr_info("%s: registering lan devices\n", dev->sw_dev.name);
+	b53_register_netdevs(dev);
 	dev->eth_dev->phy_ptr = dev;
 	dev->eth_dev->priv_flags |= IFF_NO_IP_ALIGN;
 	dev->eth_dev->eth_mangle_rx = b53_mangle_rx;
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
index f487bf2..167e042 100644
--- a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
@@ -359,8 +359,10 @@ int b53_global_get_brcm_hdr(struct switch_dev *dev,
 int b53_global_set_brcm_hdr(struct switch_dev *dev,
 		const struct switch_attr *attr,
 		struct switch_val *val);
+void b53_unregister_netdevs(struct b53_device *dev);
 #else
 #define b53_enable_brcm_hdr(x)
+#define b53_unregister_netdevs(x)
 #endif
 
 #ifdef CONFIG_BCM47XX
diff --git a/target/linux/generic/files/drivers/net/phy/b53/slave.c b/target/linux/generic/files/drivers/net/phy/b53/slave.c
new file mode 100644
index 0000000..b8cd2dc
--- /dev/null
+++ b/target/linux/generic/files/drivers/net/phy/b53/slave.c
@@ -0,0 +1,196 @@
+/*
+ * Slave devices logic.
+ * Adapted/copied from the Linux kernel DSA driver (net/dsa).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
+
+#include "slave.h"
+
+static int slave_init(struct net_device *dev)
+{
+	struct slave_priv *p = netdev_priv(dev);
+	dev->iflink = p->parent_dev->ifindex;
+	return 0;
+}
+
+static int slave_open(struct net_device *dev)
+{
+	struct slave_priv *p = netdev_priv(dev);
+	struct net_device *parent = p->parent_dev;
+	int err;
+
+	if (!(parent->flags & IFF_UP))
+		return -ENETDOWN;
+
+	if (!ether_addr_equal(dev->dev_addr, parent->dev_addr)) {
+		err = dev_uc_add(parent, dev->dev_addr);
+		if (err < 0)
+			goto out;
+	}
+
+	if (dev->flags & IFF_ALLMULTI) {
+		err = dev_set_allmulti(parent, 1);
+		if (err < 0)
+			goto del_unicast;
+	}
+	if (dev->flags & IFF_PROMISC) {
+		err = dev_set_promiscuity(parent, 1);
+		if (err < 0)
+			goto clear_allmulti;
+	}
+
+	return 0;
+
+clear_allmulti:
+	if (dev->flags & IFF_ALLMULTI)
+		dev_set_allmulti(parent, -1);
+del_unicast:
+	if (!ether_addr_equal(dev->dev_addr, parent->dev_addr))
+		dev_uc_del(parent, dev->dev_addr);
+out:
+	return err;
+}
+
+static int slave_close(struct net_device *dev)
+{
+	struct slave_priv *p = netdev_priv(dev);
+	struct net_device *parent = p->parent_dev;
+
+	dev_mc_unsync(parent, dev);
+	dev_uc_unsync(parent, dev);
+	if (dev->flags & IFF_ALLMULTI)
+		dev_set_allmulti(parent, -1);
+	if (dev->flags & IFF_PROMISC)
+		dev_set_promiscuity(parent, -1);
+
+	if (!ether_addr_equal(dev->dev_addr, parent->dev_addr))
+		dev_uc_del(parent, dev->dev_addr);
+
+	return 0;
+}
+
+static void slave_change_rx_flags(struct net_device *dev, int change)
+{
+    struct slave_priv *p = netdev_priv(dev);
+	struct net_device *parent = p->parent_dev;
+
+	if (change & IFF_ALLMULTI)
+		dev_set_allmulti(parent, dev->flags & IFF_ALLMULTI ? 1 : -1);
+	if (change & IFF_PROMISC)
+		dev_set_promiscuity(parent, dev->flags & IFF_PROMISC ? 1 : -1);
+}
+
+static void slave_set_rx_mode(struct net_device *dev)
+{
+	struct slave_priv *p = netdev_priv(dev);
+	struct net_device *parent = p->parent_dev;
+
+	dev_mc_sync(parent, dev);
+	dev_uc_sync(parent, dev);
+}
+
+static int slave_set_mac_address(struct net_device *dev, void *a)
+{
+	struct slave_priv *p = netdev_priv(dev);
+	struct net_device *parent = p->parent_dev;
+	struct sockaddr *addr = a;
+	int err;
+
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	if (!(dev->flags & IFF_UP))
+		goto out;
+
+	if (!ether_addr_equal(addr->sa_data, parent->dev_addr)) {
+		err = dev_uc_add(parent, addr->sa_data);
+		if (err < 0)
+			return err;
+	}
+
+	if (!ether_addr_equal(dev->dev_addr, parent->dev_addr))
+		dev_uc_del(parent, dev->dev_addr);
+
+out:
+	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+
+	return 0;
+}
+
+static netdev_tx_t slave_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct slave_priv *p = netdev_priv(dev);
+
+	skb->dev = p->parent_dev;
+	skb = p->eth_mangle_tx_port(dev, skb, p);
+	dev_queue_xmit(skb);
+
+	return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops slave_netdev_ops = {
+	.ndo_init		= slave_init,
+	.ndo_open		= slave_open,
+	.ndo_stop		= slave_close,
+	.ndo_start_xmit		= slave_xmit,
+	.ndo_change_rx_flags	= slave_change_rx_flags,
+	.ndo_set_rx_mode	= slave_set_rx_mode,
+	.ndo_set_mac_address	= slave_set_mac_address,
+};
+
+struct net_device *slave_create(struct net_device *parent, int port,
+		eth_mangle_tx_port_t eth_mangle_tx_port)
+{
+	/* Parent dev should not be null according to the code path below */
+	struct net_device *slave_dev = NULL;
+	struct slave_priv *p;
+	char port_name[] = "lan1000";
+
+	snprintf(port_name, sizeof(port_name), "lan%d", (port + 1));
+	slave_dev = alloc_netdev(sizeof(struct slave_priv),
+	            port_name, ether_setup);
+	if (!slave_dev) {
+		pr_err("Failed to allocate memory for slave device");
+		goto out;
+	}
+
+	slave_dev->features = parent->vlan_features;
+	eth_hw_addr_inherit(slave_dev, parent);
+	slave_dev->tx_queue_len = 0;
+	slave_dev->netdev_ops = &slave_netdev_ops;
+
+	SET_NETDEV_DEV(slave_dev, &parent->dev);
+	slave_dev->vlan_features = parent->vlan_features;
+
+	p = netdev_priv(slave_dev);
+	p->parent_dev = parent;
+	p->port_dev   = slave_dev;
+	p->port_idx   = port;
+	p->eth_mangle_tx_port = eth_mangle_tx_port;
+
+	if (register_netdevice(slave_dev)) {
+		free_netdev(slave_dev);
+		slave_dev = NULL;
+		goto out;
+	}
+
+	netif_carrier_on(slave_dev);
+
+out:
+	return slave_dev;
+}
+
diff --git a/target/linux/generic/files/drivers/net/phy/b53/slave.h b/target/linux/generic/files/drivers/net/phy/b53/slave.h
new file mode 100644
index 0000000..3cfe7c7
--- /dev/null
+++ b/target/linux/generic/files/drivers/net/phy/b53/slave.h
@@ -0,0 +1,38 @@
+/*
+ * Slave devices logic.
+ * Adapted/copied from the Linux kernel DSA driver (net/dsa).
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __SLAVE_H__
+#define __SLAVE_H__
+
+#include <linux/etherdevice.h>
+
+struct slave_priv;
+typedef struct sk_buff *(*eth_mangle_tx_port_t)(struct net_device *dev,
+		struct sk_buff *skb, struct slave_priv* p);
+
+struct slave_priv {
+	struct net_device *parent_dev;
+	struct net_device *port_dev;
+	eth_mangle_tx_port_t eth_mangle_tx_port;
+	int port_idx;
+};
+
+/* For the moment, the rtnl_lock() needs to be called by the caller */
+struct net_device *slave_create(struct net_device *parent, int port,
+		eth_mangle_tx_port_t eth_mangle_tx_port);
+
+#endif /* __SLAVE_H__ */
-- 
2.1.2
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel



More information about the openwrt-devel mailing list