2019-04-28 17:37:10 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2008-10-07 13:45:02 +00:00
|
|
|
/*
|
2020-11-14 23:45:57 +00:00
|
|
|
* 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
|
2020-11-14 23:45:57 +00:00
|
|
|
*
|
|
|
|
* 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
|
2008-10-07 13:45:02 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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>
|
2017-05-17 19:46:03 +00:00
|
|
|
|
2008-10-07 13:45:02 +00:00
|
|
|
#include "dsa_priv.h"
|
|
|
|
|
|
|
|
#define DSA_HLEN 4
|
|
|
|
|
2020-11-14 23:45:57 +00:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
};
|
2020-11-14 23:45:56 +00:00
|
|
|
|
2020-11-14 23:45:57 +00:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
};
|
2020-11-14 23:45:56 +00:00
|
|
|
|
2020-11-14 23:45:57 +00:00
|
|
|
static struct sk_buff *dsa_xmit_ll(struct sk_buff *skb, struct net_device *dev,
|
|
|
|
u8 extra)
|
2008-10-07 13:45:02 +00:00
|
|
|
{
|
2017-10-16 15:12:15 +00:00
|
|
|
struct dsa_port *dp = dsa_slave_to_port(dev);
|
2008-10-07 13:45:02 +00:00
|
|
|
u8 *dsa_header;
|
|
|
|
|
|
|
|
if (skb->protocol == htons(ETH_P_8021Q)) {
|
2020-11-14 23:45:57 +00:00
|
|
|
if (extra) {
|
|
|
|
skb_push(skb, extra);
|
|
|
|
memmove(skb->data, skb->data + extra, 2 * ETH_ALEN);
|
|
|
|
}
|
|
|
|
|
2020-11-14 23:45:58 +00:00
|
|
|
/* Construct tagged FROM_CPU DSA tag from 802.1Q tag. */
|
2020-11-14 23:45:57 +00:00
|
|
|
dsa_header = skb->data + 2 * ETH_ALEN + extra;
|
|
|
|
dsa_header[0] = (DSA_CMD_FROM_CPU << 6) | 0x20 | dp->ds->index;
|
2017-10-16 15:12:15 +00:00
|
|
|
dsa_header[1] = dp->index << 3;
|
2008-10-07 13:45:02 +00:00
|
|
|
|
2020-11-14 23:45:58 +00:00
|
|
|
/* Move CFI field from byte 2 to byte 1. */
|
2008-10-07 13:45:02 +00:00
|
|
|
if (dsa_header[2] & 0x10) {
|
|
|
|
dsa_header[1] |= 0x01;
|
|
|
|
dsa_header[2] &= ~0x10;
|
|
|
|
}
|
|
|
|
} else {
|
2020-11-14 23:45:57 +00:00
|
|
|
skb_push(skb, DSA_HLEN + extra);
|
|
|
|
memmove(skb->data, skb->data + DSA_HLEN + extra, 2 * ETH_ALEN);
|
2008-10-07 13:45:02 +00:00
|
|
|
|
2020-11-14 23:45:58 +00:00
|
|
|
/* Construct untagged FROM_CPU DSA tag. */
|
2020-11-14 23:45:57 +00:00
|
|
|
dsa_header = skb->data + 2 * ETH_ALEN + extra;
|
|
|
|
dsa_header[0] = (DSA_CMD_FROM_CPU << 6) | dp->ds->index;
|
2017-10-16 15:12:15 +00:00
|
|
|
dsa_header[1] = dp->index << 3;
|
2008-10-07 13:45:02 +00:00
|
|
|
dsa_header[2] = 0x00;
|
|
|
|
dsa_header[3] = 0x00;
|
|
|
|
}
|
|
|
|
|
2015-07-31 18:42:56 +00:00
|
|
|
return skb;
|
2008-10-07 13:45:02 +00:00
|
|
|
}
|
|
|
|
|
2020-11-14 23:45:57 +00:00
|
|
|
static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev,
|
|
|
|
u8 extra)
|
2008-10-07 13:45:02 +00:00
|
|
|
{
|
2020-11-14 23:45:57 +00:00
|
|
|
int source_device, source_port;
|
|
|
|
enum dsa_code code;
|
|
|
|
enum dsa_cmd cmd;
|
2008-10-07 13:45:02 +00:00
|
|
|
u8 *dsa_header;
|
|
|
|
|
2020-11-14 23:45:58 +00:00
|
|
|
/* The ethertype field is part of the DSA header. */
|
2008-10-07 13:45:02 +00:00
|
|
|
dsa_header = skb->data - 2;
|
|
|
|
|
2020-11-14 23:45:57 +00:00
|
|
|
cmd = dsa_header[0] >> 6;
|
|
|
|
switch (cmd) {
|
|
|
|
case DSA_CMD_FORWARD:
|
|
|
|
skb->offload_fwd_mark = 1;
|
|
|
|
break;
|
2020-11-14 23:45:56 +00:00
|
|
|
|
2020-11-14 23:45:57 +00:00
|
|
|
case DSA_CMD_TO_CPU:
|
2020-11-14 23:45:56 +00:00
|
|
|
code = (dsa_header[1] & 0x6) | ((dsa_header[2] >> 4) & 1);
|
|
|
|
|
2020-11-14 23:45:57 +00:00
|
|
|
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.
|
|
|
|
*/
|
2020-11-14 23:45:56 +00:00
|
|
|
skb->offload_fwd_mark = 1;
|
2020-11-14 23:45:57 +00:00
|
|
|
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.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Reserved code, this could be anything. Drop
|
|
|
|
* seems like the safest option.
|
|
|
|
*/
|
|
|
|
return NULL;
|
|
|
|
}
|
2020-11-14 23:45:56 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2017-06-01 20:07:14 +00:00
|
|
|
return NULL;
|
2020-11-14 23:45:56 +00:00
|
|
|
}
|
2008-10-07 13:45:02 +00:00
|
|
|
|
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;
|
2008-10-07 13:45:02 +00:00
|
|
|
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
|
|
|
|
2017-10-16 15:12:17 +00:00
|
|
|
skb->dev = dsa_master_find_slave(dev, source_device, source_port);
|
2017-09-29 21:19:15 +00:00
|
|
|
if (!skb->dev)
|
2017-06-01 20:07:14 +00:00
|
|
|
return NULL;
|
2008-10-07 13:45:02 +00:00
|
|
|
|
2020-11-14 23:45:57 +00:00
|
|
|
/* 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.
|
2008-10-07 13:45:02 +00:00
|
|
|
*/
|
|
|
|
if (dsa_header[0] & 0x20) {
|
|
|
|
u8 new_header[4];
|
|
|
|
|
2020-11-14 23:45:58 +00:00
|
|
|
/* Insert 802.1Q ethertype and copy the VLAN-related
|
2008-10-07 13:45:02 +00:00
|
|
|
* 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];
|
|
|
|
|
2020-11-14 23:45:58 +00:00
|
|
|
/* Move CFI bit from its place in the DSA header to
|
|
|
|
* its 802.1Q-designated place.
|
2008-10-07 13:45:02 +00:00
|
|
|
*/
|
|
|
|
if (dsa_header[1] & 0x01)
|
|
|
|
new_header[2] |= 0x10;
|
|
|
|
|
2020-11-14 23:45:58 +00:00
|
|
|
/* Update packet checksum if skb is CHECKSUM_COMPLETE. */
|
2008-10-07 13:45:02 +00:00
|
|
|
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);
|
2020-11-14 23:45:57 +00:00
|
|
|
|
|
|
|
if (extra)
|
|
|
|
memmove(skb->data - ETH_HLEN,
|
|
|
|
skb->data - ETH_HLEN - extra,
|
|
|
|
2 * ETH_ALEN);
|
2008-10-07 13:45:02 +00:00
|
|
|
} else {
|
|
|
|
skb_pull_rcsum(skb, DSA_HLEN);
|
|
|
|
memmove(skb->data - ETH_HLEN,
|
2020-11-14 23:45:57 +00:00
|
|
|
skb->data - ETH_HLEN - DSA_HLEN - extra,
|
2008-10-07 13:45:02 +00:00
|
|
|
2 * ETH_ALEN);
|
|
|
|
}
|
|
|
|
|
2017-04-08 15:55:23 +00:00
|
|
|
return skb;
|
2008-10-07 13:45:02 +00:00
|
|
|
}
|
|
|
|
|
2020-11-14 23:45:57 +00:00
|
|
|
#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,
|
|
|
|
struct packet_type *pt)
|
|
|
|
{
|
|
|
|
if (unlikely(!pskb_may_pull(skb, DSA_HLEN)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return dsa_rcv_ll(skb, dev, 0);
|
|
|
|
}
|
|
|
|
|
2019-04-28 17:37:21 +00:00
|
|
|
static const struct dsa_device_ops dsa_netdev_ops = {
|
2020-11-14 23:45:57 +00:00
|
|
|
.name = "dsa",
|
|
|
|
.proto = DSA_TAG_PROTO_DSA,
|
|
|
|
.xmit = dsa_xmit,
|
|
|
|
.rcv = dsa_rcv,
|
2018-12-06 10:36:04 +00:00
|
|
|
.overhead = DSA_HLEN,
|
2008-10-07 13:45:02 +00:00
|
|
|
};
|
2019-04-28 17:37:12 +00:00
|
|
|
|
2020-11-14 23:45:57 +00:00
|
|
|
DSA_TAG_DRIVER(dsa_netdev_ops);
|
2019-04-28 17:37:12 +00:00
|
|
|
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_DSA);
|
2020-11-14 23:45:57 +00:00
|
|
|
#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;
|
2019-04-28 17:37:15 +00:00
|
|
|
|
2020-11-14 23:45:57 +00:00
|
|
|
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,
|
|
|
|
struct packet_type *pt)
|
|
|
|
{
|
|
|
|
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,
|
|
|
|
.overhead = 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");
|