net: ethernet: ti: cpsw: allow untagged traffic on host port
authorGrygorii Strashko <grygorii.strashko@ti.com>
Tue, 19 Nov 2019 22:19:14 +0000 (00:19 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 20 Nov 2019 19:25:23 +0000 (11:25 -0800)
Now untagged vlan traffic is not support on Host P0 port. This patch adds
in ALE context bitmap of VLANs for which Host P0 port bit set in Force
Untagged Packet Egress bitmask in VLANs ALE entries, and adds corresponding
check in VLAN incapsulation header parsing function cpsw_rx_vlan_encap().

Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpsw_ale.c
drivers/net/ethernet/ti/cpsw_ale.h

index 329671e66fe43ed0d754513b42aec74e49a20ac5..15a76d3842c5aeefcbf1c0dd2b22642f4b576043 100644 (file)
@@ -428,17 +428,16 @@ static void cpsw_rx_vlan_encap(struct sk_buff *skb)
        /* Ignore vid 0 and pass packet as is */
        if (!vid)
                return;
-       /* Ignore default vlans in dual mac mode */
-       if (cpsw->data.dual_emac &&
-           vid == cpsw->slaves[priv->emac_port].port_vlan)
-               return;
 
-       prio = (rx_vlan_encap_hdr >>
-               CPSW_RX_VLAN_ENCAP_HDR_PRIO_SHIFT) &
-               CPSW_RX_VLAN_ENCAP_HDR_PRIO_MSK;
+       /* Untag P0 packets if set for vlan */
+       if (!cpsw_ale_get_vlan_p0_untag(cpsw->ale, vid)) {
+               prio = (rx_vlan_encap_hdr >>
+                       CPSW_RX_VLAN_ENCAP_HDR_PRIO_SHIFT) &
+                       CPSW_RX_VLAN_ENCAP_HDR_PRIO_MSK;
 
-       vtag = (prio << VLAN_PRIO_SHIFT) | vid;
-       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag);
+               vtag = (prio << VLAN_PRIO_SHIFT) | vid;
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag);
+       }
 
        /* strip vlan tag for VLAN-tagged packet */
        if (pkt_type == CPSW_RX_VLAN_ENCAP_HDR_PKT_VLAN_TAG) {
index e7c24396933e93150fba7526b4722737c6cd2aa0..723f742e6437e3d7c30349ad7aeae016b15cd44e 100644 (file)
@@ -5,6 +5,8 @@
  * Copyright (C) 2012 Texas Instruments
  *
  */
+#include <linux/bitmap.h>
+#include <linux/if_vlan.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
@@ -415,6 +417,17 @@ static void cpsw_ale_set_vlan_mcast(struct cpsw_ale *ale, u32 *ale_entry,
        writel(unreg_mcast, ale->params.ale_regs + ALE_VLAN_MASK_MUX(idx));
 }
 
+static void cpsw_ale_set_vlan_untag(struct cpsw_ale *ale, u32 *ale_entry,
+                                   u16 vid, int untag_mask)
+{
+       cpsw_ale_set_vlan_untag_force(ale_entry,
+                                     untag_mask, ale->vlan_field_bits);
+       if (untag_mask & ALE_PORT_HOST)
+               bitmap_set(ale->p0_untag_vid_mask, vid, 1);
+       else
+               bitmap_clear(ale->p0_untag_vid_mask, vid, 1);
+}
+
 int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
                      int reg_mcast, int unreg_mcast)
 {
@@ -427,8 +440,8 @@ int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
 
        cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN);
        cpsw_ale_set_vlan_id(ale_entry, vid);
+       cpsw_ale_set_vlan_untag(ale, ale_entry, vid, untag);
 
-       cpsw_ale_set_vlan_untag_force(ale_entry, untag, ale->vlan_field_bits);
        if (!ale->params.nu_switch_ale) {
                cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast,
                                            ale->vlan_field_bits);
@@ -460,6 +473,7 @@ int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask)
                return -ENOENT;
 
        cpsw_ale_read(ale, idx, ale_entry);
+       cpsw_ale_set_vlan_untag(ale, ale_entry, vid, 0);
 
        if (port_mask)
                cpsw_ale_set_vlan_member_list(ale_entry, port_mask,
@@ -792,6 +806,13 @@ struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
        if (!ale)
                return NULL;
 
+       ale->p0_untag_vid_mask =
+               devm_kmalloc_array(params->dev, BITS_TO_LONGS(VLAN_N_VID),
+                                  sizeof(unsigned long),
+                                  GFP_KERNEL);
+       if (!ale->p0_untag_vid_mask)
+               return ERR_PTR(-ENOMEM);
+
        ale->params = *params;
        ale->ageout = ale->params.ale_ageout * HZ;
 
index 370df254eb1280dae11a43abde482a130374f058..93d6d56d12f4bc1547e9a4872e0d5fed2bcddd4c 100644 (file)
@@ -35,6 +35,7 @@ struct cpsw_ale {
        u32                     port_mask_bits;
        u32                     port_num_bits;
        u32                     vlan_field_bits;
+       unsigned long           *p0_untag_vid_mask;
 };
 
 enum cpsw_ale_control {
@@ -115,4 +116,8 @@ int cpsw_ale_control_set(struct cpsw_ale *ale, int port,
                         int control, int value);
 void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data);
 
+static inline int cpsw_ale_get_vlan_p0_untag(struct cpsw_ale *ale, u16 vid)
+{
+       return test_bit(vid, ale->p0_untag_vid_mask);
+}
 #endif