linux/net/dsa/tag_ocelot.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Copyright 2019 NXP Semiconductors
   3 */
   4#include <linux/dsa/ocelot.h>
   5#include <soc/mscc/ocelot.h>
   6#include "dsa_priv.h"
   7
   8static void ocelot_xmit_common(struct sk_buff *skb, struct net_device *netdev,
   9                               __be32 ifh_prefix, void **ifh)
  10{
  11        struct dsa_port *dp = dsa_slave_to_port(netdev);
  12        struct dsa_switch *ds = dp->ds;
  13        void *injection;
  14        __be32 *prefix;
  15        u32 rew_op = 0;
  16
  17        injection = skb_push(skb, OCELOT_TAG_LEN);
  18        prefix = skb_push(skb, OCELOT_SHORT_PREFIX_LEN);
  19
  20        *prefix = ifh_prefix;
  21        memset(injection, 0, OCELOT_TAG_LEN);
  22        ocelot_ifh_set_bypass(injection, 1);
  23        ocelot_ifh_set_src(injection, ds->num_ports);
  24        ocelot_ifh_set_qos_class(injection, skb->priority);
  25
  26        rew_op = ocelot_ptp_rew_op(skb);
  27        if (rew_op)
  28                ocelot_ifh_set_rew_op(injection, rew_op);
  29
  30        *ifh = injection;
  31}
  32
  33static struct sk_buff *ocelot_xmit(struct sk_buff *skb,
  34                                   struct net_device *netdev)
  35{
  36        struct dsa_port *dp = dsa_slave_to_port(netdev);
  37        void *injection;
  38
  39        ocelot_xmit_common(skb, netdev, cpu_to_be32(0x8880000a), &injection);
  40        ocelot_ifh_set_dest(injection, BIT_ULL(dp->index));
  41
  42        return skb;
  43}
  44
  45static struct sk_buff *seville_xmit(struct sk_buff *skb,
  46                                    struct net_device *netdev)
  47{
  48        struct dsa_port *dp = dsa_slave_to_port(netdev);
  49        void *injection;
  50
  51        ocelot_xmit_common(skb, netdev, cpu_to_be32(0x88800005), &injection);
  52        seville_ifh_set_dest(injection, BIT_ULL(dp->index));
  53
  54        return skb;
  55}
  56
  57static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
  58                                  struct net_device *netdev,
  59                                  struct packet_type *pt)
  60{
  61        u64 src_port, qos_class;
  62        u64 vlan_tci, tag_type;
  63        u8 *start = skb->data;
  64        struct dsa_port *dp;
  65        u8 *extraction;
  66        u16 vlan_tpid;
  67
  68        /* Revert skb->data by the amount consumed by the DSA master,
  69         * so it points to the beginning of the frame.
  70         */
  71        skb_push(skb, ETH_HLEN);
  72        /* We don't care about the short prefix, it is just for easy entrance
  73         * into the DSA master's RX filter. Discard it now by moving it into
  74         * the headroom.
  75         */
  76        skb_pull(skb, OCELOT_SHORT_PREFIX_LEN);
  77        /* And skb->data now points to the extraction frame header.
  78         * Keep a pointer to it.
  79         */
  80        extraction = skb->data;
  81        /* Now the EFH is part of the headroom as well */
  82        skb_pull(skb, OCELOT_TAG_LEN);
  83        /* Reset the pointer to the real MAC header */
  84        skb_reset_mac_header(skb);
  85        skb_reset_mac_len(skb);
  86        /* And move skb->data to the correct location again */
  87        skb_pull(skb, ETH_HLEN);
  88
  89        /* Remove from inet csum the extraction header */
  90        skb_postpull_rcsum(skb, start, OCELOT_TOTAL_TAG_LEN);
  91
  92        ocelot_xfh_get_src_port(extraction, &src_port);
  93        ocelot_xfh_get_qos_class(extraction, &qos_class);
  94        ocelot_xfh_get_tag_type(extraction, &tag_type);
  95        ocelot_xfh_get_vlan_tci(extraction, &vlan_tci);
  96
  97        skb->dev = dsa_master_find_slave(netdev, 0, src_port);
  98        if (!skb->dev)
  99                /* The switch will reflect back some frames sent through
 100                 * sockets opened on the bare DSA master. These will come back
 101                 * with src_port equal to the index of the CPU port, for which
 102                 * there is no slave registered. So don't print any error
 103                 * message here (ignore and drop those frames).
 104                 */
 105                return NULL;
 106
 107        skb->offload_fwd_mark = 1;
 108        skb->priority = qos_class;
 109
 110        /* Ocelot switches copy frames unmodified to the CPU. However, it is
 111         * possible for the user to request a VLAN modification through
 112         * VCAP_IS1_ACT_VID_REPLACE_ENA. In this case, what will happen is that
 113         * the VLAN ID field from the Extraction Header gets updated, but the
 114         * 802.1Q header does not (the classified VLAN only becomes visible on
 115         * egress through the "port tag" of front-panel ports).
 116         * So, for traffic extracted by the CPU, we want to pick up the
 117         * classified VLAN and manually replace the existing 802.1Q header from
 118         * the packet with it, so that the operating system is always up to
 119         * date with the result of tc-vlan actions.
 120         * NOTE: In VLAN-unaware mode, we don't want to do that, we want the
 121         * frame to remain unmodified, because the classified VLAN is always
 122         * equal to the pvid of the ingress port and should not be used for
 123         * processing.
 124         */
 125        dp = dsa_slave_to_port(skb->dev);
 126        vlan_tpid = tag_type ? ETH_P_8021AD : ETH_P_8021Q;
 127
 128        if (dsa_port_is_vlan_filtering(dp) &&
 129            eth_hdr(skb)->h_proto == htons(vlan_tpid)) {
 130                u16 dummy_vlan_tci;
 131
 132                skb_push_rcsum(skb, ETH_HLEN);
 133                __skb_vlan_pop(skb, &dummy_vlan_tci);
 134                skb_pull_rcsum(skb, ETH_HLEN);
 135                __vlan_hwaccel_put_tag(skb, htons(vlan_tpid), vlan_tci);
 136        }
 137
 138        return skb;
 139}
 140
 141static const struct dsa_device_ops ocelot_netdev_ops = {
 142        .name                   = "ocelot",
 143        .proto                  = DSA_TAG_PROTO_OCELOT,
 144        .xmit                   = ocelot_xmit,
 145        .rcv                    = ocelot_rcv,
 146        .needed_headroom        = OCELOT_TOTAL_TAG_LEN,
 147        .promisc_on_master      = true,
 148};
 149
 150DSA_TAG_DRIVER(ocelot_netdev_ops);
 151MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_OCELOT);
 152
 153static const struct dsa_device_ops seville_netdev_ops = {
 154        .name                   = "seville",
 155        .proto                  = DSA_TAG_PROTO_SEVILLE,
 156        .xmit                   = seville_xmit,
 157        .rcv                    = ocelot_rcv,
 158        .needed_headroom        = OCELOT_TOTAL_TAG_LEN,
 159        .promisc_on_master      = true,
 160};
 161
 162DSA_TAG_DRIVER(seville_netdev_ops);
 163MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_SEVILLE);
 164
 165static struct dsa_tag_driver *ocelot_tag_driver_array[] = {
 166        &DSA_TAG_DRIVER_NAME(ocelot_netdev_ops),
 167        &DSA_TAG_DRIVER_NAME(seville_netdev_ops),
 168};
 169
 170module_dsa_tag_drivers(ocelot_tag_driver_array);
 171
 172MODULE_LICENSE("GPL v2");
 173