Browse Source

Merge branch 'dsa-lan9303-IGMP-handling'

Egil Hjelmeland says:

====================
net: dsa: lan9303: IGMP handling

Set up the HW switch to trap IGMP packets to CPU port.
And make sure skb->offload_fwd_mark is cleared for incoming IGMP packets.

skb->offload_fwd_mark calculation is a candidate for consolidation into the
DSA core. The calculation can probably be more polished when done at a point
where DSA has updated skb.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
David S. Miller 7 years ago
parent
commit
f444ef5103
2 changed files with 39 additions and 0 deletions
  1. 26 0
      drivers/net/dsa/lan9303-core.c
  2. 13 0
      net/dsa/tag_lan9303.c

+ 26 - 0
drivers/net/dsa/lan9303-core.c

@@ -153,6 +153,8 @@
 # define LAN9303_SWE_VLAN_UNTAG_PORT0 BIT(12)
 #define LAN9303_SWE_VLAN_CMD_STS 0x1810
 #define LAN9303_SWE_GLB_INGRESS_CFG 0x1840
+# define LAN9303_SWE_GLB_INGR_IGMP_TRAP BIT(7)
+# define LAN9303_SWE_GLB_INGR_IGMP_PORT(p) BIT(10 + p)
 #define LAN9303_SWE_PORT_STATE 0x1843
 # define LAN9303_SWE_PORT_STATE_FORWARDING_PORT2 (0)
 # define LAN9303_SWE_PORT_STATE_LEARNING_PORT2 BIT(5)
@@ -450,6 +452,21 @@ on_error:
 	return ret;
 }
 
+static int lan9303_write_switch_reg_mask(struct lan9303 *chip, u16 regnum,
+					 u32 val, u32 mask)
+{
+	int ret;
+	u32 reg;
+
+	ret = lan9303_read_switch_reg(chip, regnum, &reg);
+	if (ret)
+		return ret;
+
+	reg = (reg & ~mask) | val;
+
+	return lan9303_write_switch_reg(chip, regnum, reg);
+}
+
 static int lan9303_write_switch_port(struct lan9303 *chip, int port,
 				     u16 regnum, u32 val)
 {
@@ -905,6 +922,15 @@ static int lan9303_setup(struct dsa_switch *ds)
 	if (ret)
 		dev_err(chip->dev, "failed to re-enable switching %d\n", ret);
 
+	/* Trap IGMP to port 0 */
+	ret = lan9303_write_switch_reg_mask(chip, LAN9303_SWE_GLB_INGRESS_CFG,
+					    LAN9303_SWE_GLB_INGR_IGMP_TRAP |
+					    LAN9303_SWE_GLB_INGR_IGMP_PORT(0),
+					    LAN9303_SWE_GLB_INGR_IGMP_PORT(1) |
+					    LAN9303_SWE_GLB_INGR_IGMP_PORT(2));
+	if (ret)
+		dev_err(chip->dev, "failed to setup IGMP trap %d\n", ret);
+
 	return 0;
 }
 

+ 13 - 0
net/dsa/tag_lan9303.c

@@ -92,6 +92,8 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev,
 {
 	u16 *lan9303_tag;
 	unsigned int source_port;
+	u16 ether_type_nw;
+	u8 ip_protocol;
 
 	if (unlikely(!pskb_may_pull(skb, LAN9303_TAG_LEN))) {
 		dev_warn_ratelimited(&dev->dev,
@@ -129,6 +131,17 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev,
 	skb->offload_fwd_mark = !ether_addr_equal(skb->data - ETH_HLEN,
 						  eth_stp_addr);
 
+	/* We also need IGMP packets to have skb->offload_fwd_mark = 0.
+	 * Solving this for all conceivable situations would add more cost to
+	 * every packet. Instead we handle just the common case:
+	 * No VLAN tag + Ethernet II framing.
+	 * Test least probable term first.
+	 */
+	ether_type_nw = lan9303_tag[2];
+	ip_protocol = *(skb->data + 9);
+	if (ip_protocol == IPPROTO_IGMP && ether_type_nw == htons(ETH_P_IP))
+		skb->offload_fwd_mark = 0;
+
 	return skb;
 }