linux-stable/net/dsa/tag_dsa.c

405 lines
11 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0+
/*
* Regular and Ethertype DSA tagging
dsa: add switch chip cascading support The initial version of the DSA driver only supported a single switch chip per network interface, while DSA-capable switch chips can be interconnected to form a tree of switch chips. This patch adds support for multiple switch chips on a network interface. An example topology for a 16-port device with an embedded CPU is as follows: +-----+ +--------+ +--------+ | |eth0 10| switch |9 10| switch | | CPU +----------+ +-------+ | | | | chip 0 | | chip 1 | +-----+ +---++---+ +---++---+ || || || || ||1000baseT ||1000baseT ||ports 1-8 ||ports 9-16 This requires a couple of interdependent changes in the DSA layer: - The dsa platform driver data needs to be extended: there is still only one netdevice per DSA driver instance (eth0 in the example above), but each of the switch chips in the tree needs its own mii_bus device pointer, MII management bus address, and port name array. (include/net/dsa.h) The existing in-tree dsa users need some small changes to deal with this. (arch/arm) - The DSA and Ethertype DSA tagging modules need to be extended to use the DSA device ID field on receive and demultiplex the packet accordingly, and fill in the DSA device ID field on transmit according to which switch chip the packet is heading to. (net/dsa/tag_{dsa,edsa}.c) - The concept of "CPU port", which is the switch chip port that the CPU is connected to (port 10 on switch chip 0 in the example), needs to be extended with the concept of "upstream port", which is the port on the switch chip that will bring us one hop closer to the CPU (port 10 for both switch chips in the example above). - The dsa platform data needs to specify which ports on which switch chips are links to other switch chips, so that we can enable DSA tagging mode on them. (For inter-switch links, we always use non-EtherType DSA tagging, since it has lower overhead. The CPU link uses dsa or edsa tagging depending on what the 'root' switch chip supports.) This is done by specifying "dsa" for the given port in the port array. - The dsa platform data needs to be extended with information on via which port to reach any given switch chip from any given switch chip. This info is specified via the per-switch chip data struct ->rtable[] array, which gives the nexthop ports for each of the other switches in the tree. For the example topology above, the dsa platform data would look something like this: static struct dsa_chip_data sw[2] = { { .mii_bus = &foo, .sw_addr = 1, .port_names[0] = "p1", .port_names[1] = "p2", .port_names[2] = "p3", .port_names[3] = "p4", .port_names[4] = "p5", .port_names[5] = "p6", .port_names[6] = "p7", .port_names[7] = "p8", .port_names[9] = "dsa", .port_names[10] = "cpu", .rtable = (s8 []){ -1, 9, }, }, { .mii_bus = &foo, .sw_addr = 2, .port_names[0] = "p9", .port_names[1] = "p10", .port_names[2] = "p11", .port_names[3] = "p12", .port_names[4] = "p13", .port_names[5] = "p14", .port_names[6] = "p15", .port_names[7] = "p16", .port_names[10] = "dsa", .rtable = (s8 []){ 10, -1, }, }, }, static struct dsa_platform_data pd = { .netdev = &foo, .nr_switches = 2, .sw = sw, }; Signed-off-by: Lennert Buytenhek <buytenh@marvell.com> Tested-by: Gary Thomas <gary@mlbassoc.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2009-03-20 09:52:09 +00:00
* Copyright (c) 2008-2009 Marvell Semiconductor
*
* Regular DSA
* -----------
* For untagged (in 802.1Q terms) packets, the switch will splice in
* the tag between the SA and the ethertype of the original
* packet. Tagged frames will instead have their outermost .1Q tag
* converted to a DSA tag. It expects the same layout when receiving
* packets from the CPU.
*
* Example:
*
* .----.----.----.---------
* Pu: | DA | SA | ET | Payload ...
* '----'----'----'---------
* 6 6 2 N
* .----.----.--------.-----.----.---------
* Pt: | DA | SA | 0x8100 | TCI | ET | Payload ...
* '----'----'--------'-----'----'---------
* 6 6 2 2 2 N
* .----.----.-----.----.---------
* Pd: | DA | SA | DSA | ET | Payload ...
* '----'----'-----'----'---------
* 6 6 4 2 N
*
* No matter if a packet is received untagged (Pu) or tagged (Pt),
* they will both have the same layout (Pd) when they are sent to the
* CPU. This is done by ignoring 802.3, replacing the ethertype field
* with more metadata, among which is a bit to signal if the original
* packet was tagged or not.
*
* Ethertype DSA
* -------------
* Uses the exact same tag format as regular DSA, but also includes a
* proper ethertype field (which the mv88e6xxx driver sets to
* ETH_P_EDSA/0xdada) followed by two zero bytes:
*
* .----.----.--------.--------.-----.----.---------
* | DA | SA | 0xdada | 0x0000 | DSA | ET | Payload ...
* '----'----'--------'--------'-----'----'---------
* 6 6 2 2 4 2 N
*/
#include <linux/etherdevice.h>
#include <linux/list.h>
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
#include <linux/slab.h>
#include "dsa_priv.h"
#define DSA_HLEN 4
/**
* enum dsa_cmd - DSA Command
* @DSA_CMD_TO_CPU: Set on packets that were trapped or mirrored to
* the CPU port. This is needed to implement control protocols,
* e.g. STP and LLDP, that must not allow those control packets to
* be switched according to the normal rules.
* @DSA_CMD_FROM_CPU: Used by the CPU to send a packet to a specific
* port, ignoring all the barriers that the switch normally
* enforces (VLANs, STP port states etc.). No source address
* learning takes place. "sudo send packet"
* @DSA_CMD_TO_SNIFFER: Set on the copies of packets that matched some
* user configured ingress or egress monitor criteria. These are
* forwarded by the switch tree to the user configured ingress or
* egress monitor port, which can be set to the CPU port or a
* regular port. If the destination is a regular port, the tag
* will be removed before egressing the port. If the destination
* is the CPU port, the tag will not be removed.
* @DSA_CMD_FORWARD: This tag is used on all bulk traffic passing
* through the switch tree, including the flows that are directed
* towards the CPU. Its device/port tuple encodes the original
* source port on which the packet ingressed. It can also be used
* on transmit by the CPU to defer the forwarding decision to the
* hardware, based on the current config of PVT/VTU/ATU
* etc. Source address learning takes places if enabled on the
* receiving DSA/CPU port.
*/
enum dsa_cmd {
DSA_CMD_TO_CPU = 0,
DSA_CMD_FROM_CPU = 1,
DSA_CMD_TO_SNIFFER = 2,
DSA_CMD_FORWARD = 3
};
/**
* enum dsa_code - TO_CPU Code
*
* @DSA_CODE_MGMT_TRAP: DA was classified as a management
* address. Typical examples include STP BPDUs and LLDP.
* @DSA_CODE_FRAME2REG: Response to a "remote management" request.
* @DSA_CODE_IGMP_MLD_TRAP: IGMP/MLD signaling.
* @DSA_CODE_POLICY_TRAP: Frame matched some policy configuration on
* the device. Typical examples are matching on DA/SA/VID and DHCP
* snooping.
* @DSA_CODE_ARP_MIRROR: The name says it all really.
* @DSA_CODE_POLICY_MIRROR: Same as @DSA_CODE_POLICY_TRAP, but the
* particular policy was set to trigger a mirror instead of a
* trap.
* @DSA_CODE_RESERVED_6: Unused on all devices up to at least 6393X.
* @DSA_CODE_RESERVED_7: Unused on all devices up to at least 6393X.
*
* A 3-bit code is used to relay why a particular frame was sent to
* the CPU. We only use this to determine if the packet was mirrored
* or trapped, i.e. whether the packet has been forwarded by hardware
* or not.
*
* This is the superset of all possible codes. Any particular device
* may only implement a subset.
*/
enum dsa_code {
DSA_CODE_MGMT_TRAP = 0,
DSA_CODE_FRAME2REG = 1,
DSA_CODE_IGMP_MLD_TRAP = 2,
DSA_CODE_POLICY_TRAP = 3,
DSA_CODE_ARP_MIRROR = 4,
DSA_CODE_POLICY_MIRROR = 5,
DSA_CODE_RESERVED_6 = 6,
DSA_CODE_RESERVED_7 = 7
};
static struct sk_buff *dsa_xmit_ll(struct sk_buff *skb, struct net_device *dev,
u8 extra)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
u8 tag_dev, tag_port;
enum dsa_cmd cmd;
u8 *dsa_header;
u16 pvid = 0;
int err;
if (skb->offload_fwd_mark) {
struct dsa_switch_tree *dst = dp->ds->dst;
struct net_device *br = dp->bridge_dev;
cmd = DSA_CMD_FORWARD;
/* When offloading forwarding for a bridge, inject FORWARD
* packets on behalf of a virtual switch device with an index
* past the physical switches.
*/
tag_dev = dst->last_switch + 1 + dp->bridge_num;
tag_port = 0;
/* If we are offloading forwarding for a VLAN-unaware bridge,
* inject packets to hardware using the bridge's pvid, since
* that's where the packets ingressed from.
*/
if (!br_vlan_enabled(br)) {
/* Safe because __dev_queue_xmit() runs under
* rcu_read_lock_bh()
*/
err = br_vlan_get_pvid_rcu(br, &pvid);
if (err)
return NULL;
}
} else {
cmd = DSA_CMD_FROM_CPU;
tag_dev = dp->ds->index;
tag_port = dp->index;
}
if (skb->protocol == htons(ETH_P_8021Q)) {
if (extra) {
skb_push(skb, extra);
dsa_alloc_etype_header(skb, extra);
}
/* Construct tagged DSA tag from 802.1Q tag. */
dsa_header = skb->data + 2 * ETH_ALEN + extra;
dsa_header[0] = (cmd << 6) | 0x20 | tag_dev;
dsa_header[1] = tag_port << 3;
/* Move CFI field from byte 2 to byte 1. */
if (dsa_header[2] & 0x10) {
dsa_header[1] |= 0x01;
dsa_header[2] &= ~0x10;
}
} else {
skb_push(skb, DSA_HLEN + extra);
dsa_alloc_etype_header(skb, DSA_HLEN + extra);
/* Construct untagged DSA tag. */
dsa_header = skb->data + 2 * ETH_ALEN + extra;
dsa_header[0] = (cmd << 6) | tag_dev;
dsa_header[1] = tag_port << 3;
dsa_header[2] = pvid >> 8;
dsa_header[3] = pvid & 0xff;
}
return skb;
}
static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev,
u8 extra)
{
bool trap = false, trunk = false;
int source_device, source_port;
enum dsa_code code;
enum dsa_cmd cmd;
u8 *dsa_header;
/* The ethertype field is part of the DSA header. */
dsa_header = skb->data - 2;
cmd = dsa_header[0] >> 6;
switch (cmd) {
case DSA_CMD_FORWARD:
trunk = !!(dsa_header[1] & 7);
break;
case DSA_CMD_TO_CPU:
code = (dsa_header[1] & 0x6) | ((dsa_header[2] >> 4) & 1);
switch (code) {
case DSA_CODE_FRAME2REG:
/* Remote management is not implemented yet,
* drop.
*/
return NULL;
case DSA_CODE_ARP_MIRROR:
case DSA_CODE_POLICY_MIRROR:
/* Mark mirrored packets to notify any upper
* device (like a bridge) that forwarding has
* already been done by hardware.
*/
break;
case DSA_CODE_MGMT_TRAP:
case DSA_CODE_IGMP_MLD_TRAP:
case DSA_CODE_POLICY_TRAP:
/* Traps have, by definition, not been
* forwarded by hardware, so don't mark them.
*/
trap = true;
break;
default:
/* Reserved code, this could be anything. Drop
* seems like the safest option.
*/
return NULL;
}
break;
default:
return NULL;
}
dsa: add switch chip cascading support The initial version of the DSA driver only supported a single switch chip per network interface, while DSA-capable switch chips can be interconnected to form a tree of switch chips. This patch adds support for multiple switch chips on a network interface. An example topology for a 16-port device with an embedded CPU is as follows: +-----+ +--------+ +--------+ | |eth0 10| switch |9 10| switch | | CPU +----------+ +-------+ | | | | chip 0 | | chip 1 | +-----+ +---++---+ +---++---+ || || || || ||1000baseT ||1000baseT ||ports 1-8 ||ports 9-16 This requires a couple of interdependent changes in the DSA layer: - The dsa platform driver data needs to be extended: there is still only one netdevice per DSA driver instance (eth0 in the example above), but each of the switch chips in the tree needs its own mii_bus device pointer, MII management bus address, and port name array. (include/net/dsa.h) The existing in-tree dsa users need some small changes to deal with this. (arch/arm) - The DSA and Ethertype DSA tagging modules need to be extended to use the DSA device ID field on receive and demultiplex the packet accordingly, and fill in the DSA device ID field on transmit according to which switch chip the packet is heading to. (net/dsa/tag_{dsa,edsa}.c) - The concept of "CPU port", which is the switch chip port that the CPU is connected to (port 10 on switch chip 0 in the example), needs to be extended with the concept of "upstream port", which is the port on the switch chip that will bring us one hop closer to the CPU (port 10 for both switch chips in the example above). - The dsa platform data needs to specify which ports on which switch chips are links to other switch chips, so that we can enable DSA tagging mode on them. (For inter-switch links, we always use non-EtherType DSA tagging, since it has lower overhead. The CPU link uses dsa or edsa tagging depending on what the 'root' switch chip supports.) This is done by specifying "dsa" for the given port in the port array. - The dsa platform data needs to be extended with information on via which port to reach any given switch chip from any given switch chip. This info is specified via the per-switch chip data struct ->rtable[] array, which gives the nexthop ports for each of the other switches in the tree. For the example topology above, the dsa platform data would look something like this: static struct dsa_chip_data sw[2] = { { .mii_bus = &foo, .sw_addr = 1, .port_names[0] = "p1", .port_names[1] = "p2", .port_names[2] = "p3", .port_names[3] = "p4", .port_names[4] = "p5", .port_names[5] = "p6", .port_names[6] = "p7", .port_names[7] = "p8", .port_names[9] = "dsa", .port_names[10] = "cpu", .rtable = (s8 []){ -1, 9, }, }, { .mii_bus = &foo, .sw_addr = 2, .port_names[0] = "p9", .port_names[1] = "p10", .port_names[2] = "p11", .port_names[3] = "p12", .port_names[4] = "p13", .port_names[5] = "p14", .port_names[6] = "p15", .port_names[7] = "p16", .port_names[10] = "dsa", .rtable = (s8 []){ 10, -1, }, }, }, static struct dsa_platform_data pd = { .netdev = &foo, .nr_switches = 2, .sw = sw, }; Signed-off-by: Lennert Buytenhek <buytenh@marvell.com> Tested-by: Gary Thomas <gary@mlbassoc.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2009-03-20 09:52:09 +00:00
source_device = dsa_header[0] & 0x1f;
source_port = (dsa_header[1] >> 3) & 0x1f;
dsa: add switch chip cascading support The initial version of the DSA driver only supported a single switch chip per network interface, while DSA-capable switch chips can be interconnected to form a tree of switch chips. This patch adds support for multiple switch chips on a network interface. An example topology for a 16-port device with an embedded CPU is as follows: +-----+ +--------+ +--------+ | |eth0 10| switch |9 10| switch | | CPU +----------+ +-------+ | | | | chip 0 | | chip 1 | +-----+ +---++---+ +---++---+ || || || || ||1000baseT ||1000baseT ||ports 1-8 ||ports 9-16 This requires a couple of interdependent changes in the DSA layer: - The dsa platform driver data needs to be extended: there is still only one netdevice per DSA driver instance (eth0 in the example above), but each of the switch chips in the tree needs its own mii_bus device pointer, MII management bus address, and port name array. (include/net/dsa.h) The existing in-tree dsa users need some small changes to deal with this. (arch/arm) - The DSA and Ethertype DSA tagging modules need to be extended to use the DSA device ID field on receive and demultiplex the packet accordingly, and fill in the DSA device ID field on transmit according to which switch chip the packet is heading to. (net/dsa/tag_{dsa,edsa}.c) - The concept of "CPU port", which is the switch chip port that the CPU is connected to (port 10 on switch chip 0 in the example), needs to be extended with the concept of "upstream port", which is the port on the switch chip that will bring us one hop closer to the CPU (port 10 for both switch chips in the example above). - The dsa platform data needs to specify which ports on which switch chips are links to other switch chips, so that we can enable DSA tagging mode on them. (For inter-switch links, we always use non-EtherType DSA tagging, since it has lower overhead. The CPU link uses dsa or edsa tagging depending on what the 'root' switch chip supports.) This is done by specifying "dsa" for the given port in the port array. - The dsa platform data needs to be extended with information on via which port to reach any given switch chip from any given switch chip. This info is specified via the per-switch chip data struct ->rtable[] array, which gives the nexthop ports for each of the other switches in the tree. For the example topology above, the dsa platform data would look something like this: static struct dsa_chip_data sw[2] = { { .mii_bus = &foo, .sw_addr = 1, .port_names[0] = "p1", .port_names[1] = "p2", .port_names[2] = "p3", .port_names[3] = "p4", .port_names[4] = "p5", .port_names[5] = "p6", .port_names[6] = "p7", .port_names[7] = "p8", .port_names[9] = "dsa", .port_names[10] = "cpu", .rtable = (s8 []){ -1, 9, }, }, { .mii_bus = &foo, .sw_addr = 2, .port_names[0] = "p9", .port_names[1] = "p10", .port_names[2] = "p11", .port_names[3] = "p12", .port_names[4] = "p13", .port_names[5] = "p14", .port_names[6] = "p15", .port_names[7] = "p16", .port_names[10] = "dsa", .rtable = (s8 []){ 10, -1, }, }, }, static struct dsa_platform_data pd = { .netdev = &foo, .nr_switches = 2, .sw = sw, }; Signed-off-by: Lennert Buytenhek <buytenh@marvell.com> Tested-by: Gary Thomas <gary@mlbassoc.com> Signed-off-by: David S. Miller <davem@davemloft.net>
2009-03-20 09:52:09 +00:00
if (trunk) {
struct dsa_port *cpu_dp = dev->dsa_ptr;
/* The exact source port is not available in the tag,
* so we inject the frame directly on the upper
* team/bond.
*/
skb->dev = dsa_lag_dev(cpu_dp->dst, source_port);
} else {
skb->dev = dsa_master_find_slave(dev, source_device,
source_port);
}
if (!skb->dev)
return NULL;
/* When using LAG offload, skb->dev is not a DSA slave interface,
* so we cannot call dsa_default_offload_fwd_mark and we need to
* special-case it.
*/
if (trunk)
skb->offload_fwd_mark = true;
else if (!trap)
dsa_default_offload_fwd_mark(skb);
/* If the 'tagged' bit is set; convert the DSA tag to a 802.1Q
* tag, and delete the ethertype (extra) if applicable. If the
* 'tagged' bit is cleared; delete the DSA tag, and ethertype
* if applicable.
*/
if (dsa_header[0] & 0x20) {
u8 new_header[4];
/* Insert 802.1Q ethertype and copy the VLAN-related
* fields, but clear the bit that will hold CFI (since
* DSA uses that bit location for another purpose).
*/
new_header[0] = (ETH_P_8021Q >> 8) & 0xff;
new_header[1] = ETH_P_8021Q & 0xff;
new_header[2] = dsa_header[2] & ~0x10;
new_header[3] = dsa_header[3];
/* Move CFI bit from its place in the DSA header to
* its 802.1Q-designated place.
*/
if (dsa_header[1] & 0x01)
new_header[2] |= 0x10;
/* Update packet checksum if skb is CHECKSUM_COMPLETE. */
if (skb->ip_summed == CHECKSUM_COMPLETE) {
__wsum c = skb->csum;
c = csum_add(c, csum_partial(new_header + 2, 2, 0));
c = csum_sub(c, csum_partial(dsa_header + 2, 2, 0));
skb->csum = c;
}
memcpy(dsa_header, new_header, DSA_HLEN);
if (extra)
dsa_strip_etype_header(skb, extra);
} else {
skb_pull_rcsum(skb, DSA_HLEN);
dsa_strip_etype_header(skb, DSA_HLEN + extra);
}
return skb;
}
#if IS_ENABLED(CONFIG_NET_DSA_TAG_DSA)
static struct sk_buff *dsa_xmit(struct sk_buff *skb, struct net_device *dev)
{
return dsa_xmit_ll(skb, dev, 0);
}
static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev)
{
if (unlikely(!pskb_may_pull(skb, DSA_HLEN)))
return NULL;
return dsa_rcv_ll(skb, dev, 0);
}
static const struct dsa_device_ops dsa_netdev_ops = {
.name = "dsa",
.proto = DSA_TAG_PROTO_DSA,
.xmit = dsa_xmit,
.rcv = dsa_rcv,
.needed_headroom = DSA_HLEN,
};
DSA_TAG_DRIVER(dsa_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_DSA);
#endif /* CONFIG_NET_DSA_TAG_DSA */
#if IS_ENABLED(CONFIG_NET_DSA_TAG_EDSA)
#define EDSA_HLEN 8
static struct sk_buff *edsa_xmit(struct sk_buff *skb, struct net_device *dev)
{
u8 *edsa_header;
skb = dsa_xmit_ll(skb, dev, EDSA_HLEN - DSA_HLEN);
if (!skb)
return NULL;
edsa_header = skb->data + 2 * ETH_ALEN;
edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff;
edsa_header[1] = ETH_P_EDSA & 0xff;
edsa_header[2] = 0x00;
edsa_header[3] = 0x00;
return skb;
}
static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev)
{
if (unlikely(!pskb_may_pull(skb, EDSA_HLEN)))
return NULL;
skb_pull_rcsum(skb, EDSA_HLEN - DSA_HLEN);
return dsa_rcv_ll(skb, dev, EDSA_HLEN - DSA_HLEN);
}
static const struct dsa_device_ops edsa_netdev_ops = {
.name = "edsa",
.proto = DSA_TAG_PROTO_EDSA,
.xmit = edsa_xmit,
.rcv = edsa_rcv,
.needed_headroom = EDSA_HLEN,
};
DSA_TAG_DRIVER(edsa_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_EDSA);
#endif /* CONFIG_NET_DSA_TAG_EDSA */
static struct dsa_tag_driver *dsa_tag_drivers[] = {
#if IS_ENABLED(CONFIG_NET_DSA_TAG_DSA)
&DSA_TAG_DRIVER_NAME(dsa_netdev_ops),
#endif
#if IS_ENABLED(CONFIG_NET_DSA_TAG_EDSA)
&DSA_TAG_DRIVER_NAME(edsa_netdev_ops),
#endif
};
module_dsa_tag_drivers(dsa_tag_drivers);
MODULE_LICENSE("GPL");