Merge tag 'char-misc-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregk...
[linux-2.6-microblaze.git] / net / dsa / tag_sja1105.c
index 664cb80..c054f48 100644 (file)
@@ -116,9 +116,14 @@ static inline bool sja1105_is_meta_frame(const struct sk_buff *skb)
 }
 
 /* Calls sja1105_port_deferred_xmit in sja1105_main.c */
-static struct sk_buff *sja1105_defer_xmit(struct sja1105_port *sp,
+static struct sk_buff *sja1105_defer_xmit(struct dsa_port *dp,
                                          struct sk_buff *skb)
 {
+       struct sja1105_port *sp = dp->priv;
+
+       if (!dsa_port_is_sja1105(dp))
+               return skb;
+
        /* Increase refcount so the kfree_skb in dsa_slave_xmit
         * won't really free the packet.
         */
@@ -128,9 +133,44 @@ static struct sk_buff *sja1105_defer_xmit(struct sja1105_port *sp,
        return NULL;
 }
 
-static u16 sja1105_xmit_tpid(struct sja1105_port *sp)
+/* Send VLAN tags with a TPID that blends in with whatever VLAN protocol a
+ * bridge spanning ports of this switch might have.
+ */
+static u16 sja1105_xmit_tpid(struct dsa_port *dp)
 {
-       return sp->xmit_tpid;
+       struct dsa_switch *ds = dp->ds;
+       struct dsa_port *other_dp;
+       u16 proto;
+
+       /* Since VLAN awareness is global, then if this port is VLAN-unaware,
+        * all ports are. Use the VLAN-unaware TPID used for tag_8021q.
+        */
+       if (!dsa_port_is_vlan_filtering(dp))
+               return ETH_P_SJA1105;
+
+       /* Port is VLAN-aware, so there is a bridge somewhere (a single one,
+        * we're sure about that). It may not be on this port though, so we
+        * need to find it.
+        */
+       list_for_each_entry(other_dp, &ds->dst->ports, list) {
+               if (other_dp->ds != ds)
+                       continue;
+
+               if (!other_dp->bridge_dev)
+                       continue;
+
+               /* Error is returned only if CONFIG_BRIDGE_VLAN_FILTERING,
+                * which seems pointless to handle, as our port cannot become
+                * VLAN-aware in that case.
+                */
+               br_vlan_get_proto(other_dp->bridge_dev, &proto);
+
+               return proto;
+       }
+
+       WARN_ONCE(1, "Port is VLAN-aware but cannot find associated bridge!\n");
+
+       return ETH_P_SJA1105;
 }
 
 static struct sk_buff *sja1105_imprecise_xmit(struct sk_buff *skb,
@@ -155,7 +195,37 @@ static struct sk_buff *sja1105_imprecise_xmit(struct sk_buff *skb,
         */
        tx_vid = dsa_8021q_bridge_tx_fwd_offload_vid(dp->bridge_num);
 
-       return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp->priv), tx_vid);
+       return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp), tx_vid);
+}
+
+/* Transform untagged control packets into pvid-tagged control packets so that
+ * all packets sent by this tagger are VLAN-tagged and we can configure the
+ * switch to drop untagged packets coming from the DSA master.
+ */
+static struct sk_buff *sja1105_pvid_tag_control_pkt(struct dsa_port *dp,
+                                                   struct sk_buff *skb, u8 pcp)
+{
+       __be16 xmit_tpid = htons(sja1105_xmit_tpid(dp));
+       struct vlan_ethhdr *hdr;
+
+       /* If VLAN tag is in hwaccel area, move it to the payload
+        * to deal with both cases uniformly and to ensure that
+        * the VLANs are added in the right order.
+        */
+       if (unlikely(skb_vlan_tag_present(skb))) {
+               skb = __vlan_hwaccel_push_inside(skb);
+               if (!skb)
+                       return NULL;
+       }
+
+       hdr = (struct vlan_ethhdr *)skb_mac_header(skb);
+
+       /* If skb is already VLAN-tagged, leave that VLAN ID in place */
+       if (hdr->h_vlan_proto == xmit_tpid)
+               return skb;
+
+       return vlan_insert_tag(skb, xmit_tpid, (pcp << VLAN_PRIO_SHIFT) |
+                              SJA1105_DEFAULT_VLAN);
 }
 
 static struct sk_buff *sja1105_xmit(struct sk_buff *skb,
@@ -173,10 +243,15 @@ static struct sk_buff *sja1105_xmit(struct sk_buff *skb,
         * but instead SPI-installed management routes. Part 2 of this
         * is the .port_deferred_xmit driver callback.
         */
-       if (unlikely(sja1105_is_link_local(skb)))
-               return sja1105_defer_xmit(dp->priv, skb);
+       if (unlikely(sja1105_is_link_local(skb))) {
+               skb = sja1105_pvid_tag_control_pkt(dp, skb, pcp);
+               if (!skb)
+                       return NULL;
+
+               return sja1105_defer_xmit(dp, skb);
+       }
 
-       return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp->priv),
+       return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp),
                             ((pcp << VLAN_PRIO_SHIFT) | tx_vid));
 }
 
@@ -188,7 +263,6 @@ static struct sk_buff *sja1110_xmit(struct sk_buff *skb,
        u16 tx_vid = dsa_8021q_tx_vid(dp->ds, dp->index);
        u16 queue_mapping = skb_get_queue_mapping(skb);
        u8 pcp = netdev_txq_to_tc(netdev, queue_mapping);
-       struct ethhdr *eth_hdr;
        __be32 *tx_trailer;
        __be16 *tx_header;
        int trailer_pos;
@@ -201,33 +275,33 @@ static struct sk_buff *sja1110_xmit(struct sk_buff *skb,
         * tag_8021q TX VLANs.
         */
        if (likely(!sja1105_is_link_local(skb)))
-               return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp->priv),
+               return dsa_8021q_xmit(skb, netdev, sja1105_xmit_tpid(dp),
                                     ((pcp << VLAN_PRIO_SHIFT) | tx_vid));
 
+       skb = sja1105_pvid_tag_control_pkt(dp, skb, pcp);
+       if (!skb)
+               return NULL;
+
        skb_push(skb, SJA1110_HEADER_LEN);
 
-       /* Move Ethernet header to the left, making space for DSA tag */
-       memmove(skb->data, skb->data + SJA1110_HEADER_LEN, 2 * ETH_ALEN);
+       dsa_alloc_etype_header(skb, SJA1110_HEADER_LEN);
 
        trailer_pos = skb->len;
 
-       /* On TX, skb->data points to skb_mac_header(skb) */
-       eth_hdr = (struct ethhdr *)skb->data;
-       tx_header = (__be16 *)(eth_hdr + 1);
+       tx_header = dsa_etype_header_pos_tx(skb);
        tx_trailer = skb_put(skb, SJA1110_TX_TRAILER_LEN);
 
-       eth_hdr->h_proto = htons(ETH_P_SJA1110);
-
-       *tx_header = htons(SJA1110_HEADER_HOST_TO_SWITCH |
-                          SJA1110_TX_HEADER_HAS_TRAILER |
-                          SJA1110_TX_HEADER_TRAILER_POS(trailer_pos));
+       tx_header[0] = htons(ETH_P_SJA1110);
+       tx_header[1] = htons(SJA1110_HEADER_HOST_TO_SWITCH |
+                            SJA1110_TX_HEADER_HAS_TRAILER |
+                            SJA1110_TX_HEADER_TRAILER_POS(trailer_pos));
        *tx_trailer = cpu_to_be32(SJA1110_TX_TRAILER_PRIO(pcp) |
                                  SJA1110_TX_TRAILER_SWITCHID(dp->ds->index) |
                                  SJA1110_TX_TRAILER_DESTPORTS(BIT(dp->index)));
        if (clone) {
                u8 ts_id = SJA1105_SKB_CB(clone)->ts_id;
 
-               *tx_header |= htons(SJA1110_TX_HEADER_TAKE_TS);
+               tx_header[1] |= htons(SJA1110_TX_HEADER_TAKE_TS);
                *tx_trailer |= cpu_to_be32(SJA1110_TX_TRAILER_TSTAMP_ID(ts_id));
        }
 
@@ -270,16 +344,16 @@ static struct sk_buff
                                bool is_link_local,
                                bool is_meta)
 {
-       struct sja1105_port *sp;
-       struct dsa_port *dp;
-
-       dp = dsa_slave_to_port(skb->dev);
-       sp = dp->priv;
-
        /* Step 1: A timestampable frame was received.
         * Buffer it until we get its meta frame.
         */
        if (is_link_local) {
+               struct dsa_port *dp = dsa_slave_to_port(skb->dev);
+               struct sja1105_port *sp = dp->priv;
+
+               if (unlikely(!dsa_port_is_sja1105(dp)))
+                       return skb;
+
                if (!test_bit(SJA1105_HWTS_RX_EN, &sp->data->state))
                        /* Do normal processing. */
                        return skb;
@@ -312,8 +386,13 @@ static struct sk_buff
         * frame, which serves no further purpose).
         */
        } else if (is_meta) {
+               struct dsa_port *dp = dsa_slave_to_port(skb->dev);
+               struct sja1105_port *sp = dp->priv;
                struct sk_buff *stampable_skb;
 
+               if (unlikely(!dsa_port_is_sja1105(dp)))
+                       return skb;
+
                /* Drop the meta frame if we're not in the right state
                 * to process it.
                 */
@@ -391,8 +470,7 @@ static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port,
 }
 
 static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
-                                  struct net_device *netdev,
-                                  struct packet_type *pt)
+                                  struct net_device *netdev)
 {
        int source_port = -1, switch_id = -1;
        struct sja1105_meta meta = {0};
@@ -444,11 +522,11 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
 
 static struct sk_buff *sja1110_rcv_meta(struct sk_buff *skb, u16 rx_header)
 {
+       u8 *buf = dsa_etype_header_pos_rx(skb) + SJA1110_HEADER_LEN;
        int switch_id = SJA1110_RX_HEADER_SWITCH_ID(rx_header);
        int n_ts = SJA1110_RX_HEADER_N_TS(rx_header);
        struct net_device *master = skb->dev;
        struct dsa_port *cpu_dp;
-       u8 *buf = skb->data + 2;
        struct dsa_switch *ds;
        int i;
 
@@ -533,9 +611,7 @@ static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb,
        /* Advance skb->data past the DSA header */
        skb_pull_rcsum(skb, SJA1110_HEADER_LEN);
 
-       /* Remove the DSA header */
-       memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - SJA1110_HEADER_LEN,
-               2 * ETH_ALEN);
+       dsa_strip_etype_header(skb, SJA1110_HEADER_LEN);
 
        /* With skb->data in its final place, update the MAC header
         * so that eth_hdr() continues to works properly.
@@ -546,12 +622,11 @@ static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb,
 }
 
 static struct sk_buff *sja1110_rcv(struct sk_buff *skb,
-                                  struct net_device *netdev,
-                                  struct packet_type *pt)
+                                  struct net_device *netdev)
 {
        int source_port = -1, switch_id = -1;
        bool host_only = false;
-       u16 vid;
+       u16 vid = 0;
 
        if (sja1110_skb_has_inband_control_extension(skb)) {
                skb = sja1110_rcv_inband_control_extension(skb, &source_port,