mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-09-02 15:02:51 +08:00
doc: move the L2 multicast and tag word details out of the code
Review asked for this directly: the hardware layout above port_l2mc_set() would be better as documentation than as a comment, keeping only the two lines that say what the function does. doc/l2.md gains a section on static multicast entries, why delivery uses the forward action rather than the trap, and the SMI layout of the entry. doc/CpuPort.md gains the layout of the tag's flags and pmask words, with the byte order trap that cost an afternoon: writing the flags constant raw instead of through HTONS puts 0x0020 on the wire as 0x2000, which is EFID rather than LEARN_DIS, and the ASIC then leaves the 0x8899 header on the frame. The comments those paragraphs came from are replaced by a pointer to the file that now holds them.
This commit is contained in:
@@ -64,3 +64,33 @@ Writing 0x1 to register 0x7850 will transmit the frame. The Ethernet frame
|
|||||||
checksum and the TCP checksum are automatically calculated (offloaded) by the
|
checksum and the TCP checksum are automatically calculated (offloaded) by the
|
||||||
ASIC before transmitting on the wire.
|
ASIC before transmitting on the wire.
|
||||||
|
|
||||||
|
|
||||||
|
## The RTL tag words
|
||||||
|
|
||||||
|
The frame header uses the Realtek Remote Control Protocol (RRCP) format or
|
||||||
|
the like.
|
||||||
|
|
||||||
|
The `flags` word:
|
||||||
|
|
||||||
|
```
|
||||||
|
bit15 EFID_EN | 14:12 EFID | 11 PRI_EN | 10:8 PRI |
|
||||||
|
bit7 KEEP | 6 VSEL | 5 LEARN_DIS | 4:0 VIDX
|
||||||
|
```
|
||||||
|
|
||||||
|
All fields are in network byte order.
|
||||||
|
|
||||||
|
* `EFID_EN`, `EFID`: look the destination up under this filtering ID
|
||||||
|
instead of the port's own
|
||||||
|
* `PRI_EN`, `PRI`: force the given priority on the frame
|
||||||
|
* `KEEP`: keep the 802.1Q tagging of the frame exactly as injected,
|
||||||
|
bypassing the egress tagging rules of the port
|
||||||
|
* `VSEL`, `VIDX`: classify the frame into the VLAN at this index of the
|
||||||
|
VLAN table
|
||||||
|
* `LEARN_DIS`: do not learn the source address from this frame
|
||||||
|
|
||||||
|
The `pmask` word: bit 15 is `ALLOW`, bits 14 to 0 are a port mask.
|
||||||
|
|
||||||
|
* `ALLOW` clear: the mask is the egress set, the frame goes to exactly
|
||||||
|
the ports given
|
||||||
|
* `ALLOW` set: the ASIC looks the destination up as usual and the mask
|
||||||
|
only limits which ports the result may use
|
||||||
|
|||||||
@@ -65,3 +65,36 @@ ASIC and flushing the table in order to quickly forget the learned entries.
|
|||||||
3c:18:a0:7e:11:00 0x0001 learned 5
|
3c:18:a0:7e:11:00 0x0001 learned 5
|
||||||
1c:2a:a3:23:00:02 0x0001 learned 7
|
1c:2a:a3:23:00:02 0x0001 learned 7
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Static multicast entries
|
||||||
|
|
||||||
|
Slow-protocol frames such as LACPDUs and STP BPDUs have to reach the CPU
|
||||||
|
without being flooded to the other ports. No bridge relays these frames:
|
||||||
|
their addresses are in the set that 802.1D-2004 clause 7.12.6 forbids a
|
||||||
|
bridge to forward, and what travels the network is the information, with
|
||||||
|
every bridge regenerating BPDUs of its own on its designated ports. The reserved-multicast *trap* action
|
||||||
|
cannot do that on this hardware, because its destination is an external CPU
|
||||||
|
attached to a physical port, which these boards do not populate. The protocol
|
||||||
|
modules therefore leave the reserved-multicast action at *forward* and constrain
|
||||||
|
the egress with a static L2 multicast entry instead: the lookup hits the entry's
|
||||||
|
own port mask rather than the VLAN flood mask. Verified on a SWTGW218AS both
|
||||||
|
ways, with the CPU bit cleared, where delivery stops, and with the CPU bit alone,
|
||||||
|
where nothing egresses.
|
||||||
|
|
||||||
|
`port_l2mc_set()` writes one such entry. The SMI layout is the L2 multicast
|
||||||
|
variant of the table entry:
|
||||||
|
|
||||||
|
```
|
||||||
|
DATA_IN_A = MAC bytes 5..2 -> c2 00 00 <mac_last>
|
||||||
|
DATA_IN_B = MAC[1..0] | vid<<16 | IVL<<29 | pmask[1:0]<<30
|
||||||
|
DATA_IN_C = pmask[9:2]
|
||||||
|
```
|
||||||
|
|
||||||
|
Lookups are IVL, so an entry made for VID 0 is never matched and a caller adds
|
||||||
|
one entry per PVID in use. The write goes through the table access register
|
||||||
|
with the table selector set to the L2 lookup table, `TBL_L2_UNICAST` in the
|
||||||
|
code, a name that despite appearances covers the multicast entries as well.
|
||||||
|
The hardware hashes MAC and VID to pick the bucket slot by itself.
|
||||||
|
Writing the same MAC and VID again replaces the entry rather than adding a
|
||||||
|
second one, so a caller can retarget the mask at will, for instance back to all
|
||||||
|
ports to restore flooding.
|
||||||
|
|||||||
+2
-12
@@ -71,20 +71,10 @@ struct vlan_tag {
|
|||||||
#define VLAN_TAG_SIZE (sizeof (struct vlan_tag))
|
#define VLAN_TAG_SIZE (sizeof (struct vlan_tag))
|
||||||
#define RTL_FRAME_TAG_ID 0x8899
|
#define RTL_FRAME_TAG_ID 0x8899
|
||||||
#define RTL_FRAME_TAG_VERSION 0x04
|
#define RTL_FRAME_TAG_VERSION 0x04
|
||||||
/* Bits of the tag's `flags` word (word2), per Linux DSA tag_rtl8_4:
|
/* Bits of the tag's `flags` word, see doc/CpuPort.md. */
|
||||||
* bit15 EFID_EN | 14:12 EFID | 11 PRI_EN | 10:8 PRI |
|
|
||||||
* bit7 KEEP | 6 VSEL | 5 LEARN_DIS | 4:0 VIDX
|
|
||||||
* NOTE: this word must be written through HTONS like every other tag field -
|
|
||||||
* writing the constant raw puts the bits in the wrong byte (0x0020 raw lands on
|
|
||||||
* the wire as 0x2000 = EFID, not LEARN_DIS), the ASIC then fails to parse the
|
|
||||||
* tag and forwards the frame with the 0x8899 header still on it. */
|
|
||||||
#define RTL_TAG_LEARN_DIS 0x0020 /* do not learn the CPU's SA on the egress port */
|
#define RTL_TAG_LEARN_DIS 0x0020 /* do not learn the CPU's SA on the egress port */
|
||||||
#define RTL_TAG_KEEP 0x0080 /* keep the frame's 802.1Q tag format as injected */
|
#define RTL_TAG_KEEP 0x0080 /* keep the frame's 802.1Q tag format as injected */
|
||||||
/* The `pmask` word (word3): bit15 ALLOW selects how 14:0 is interpreted.
|
/* The `pmask` word, see doc/CpuPort.md. */
|
||||||
* ALLOW=0 -> forwarding port mask (directed egress: frame goes exactly to the
|
|
||||||
* ports set). ALLOW=1 -> allowance mask (permission filter on a normal lookup),
|
|
||||||
* which for a one-hot mask yields an empty egress set - the frame disappears.
|
|
||||||
* Directed egress therefore requires ALLOW cleared, as mainline does. */
|
|
||||||
|
|
||||||
// For TX, an 8 byte (plus 4 byte padding when when VLAN is enabled)
|
// For TX, an 8 byte (plus 4 byte padding when when VLAN is enabled)
|
||||||
// header describing the frame to be moved to the Asic is used
|
// header describing the frame to be moved to the Asic is used
|
||||||
|
|||||||
+1
-26
@@ -28,8 +28,6 @@ extern __xdata struct machine_runtime machine_detected;
|
|||||||
|
|
||||||
__xdata uint32_t l2_head;
|
__xdata uint32_t l2_head;
|
||||||
|
|
||||||
/* Bounded-wait counter for the L2 table helpers; xdata because the 8051
|
|
||||||
* internal-RAM overlay (OSEG) is full. */
|
|
||||||
__xdata uint8_t l2mc_guard;
|
__xdata uint8_t l2mc_guard;
|
||||||
|
|
||||||
__xdata struct vlan_settings vlan_settings;
|
__xdata struct vlan_settings vlan_settings;
|
||||||
@@ -320,11 +318,6 @@ void vlan_setup(void) __banked
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Forget the dynamic L2 entries learned on one port.
|
* Forget the dynamic L2 entries learned on one port.
|
||||||
*
|
|
||||||
* Same flush engine as port_l2_forget(), but with a single-port mask so a
|
|
||||||
* topology change only ages out the affected port instead of the whole
|
|
||||||
* table. Bounded wait (cf. port_l2mc_set): this runs from the STP tick, and
|
|
||||||
* an unbounded poll on a stuck engine would freeze the main loop.
|
|
||||||
*/
|
*/
|
||||||
void port_l2_forget_port(uint8_t port) __banked
|
void port_l2_forget_port(uint8_t port) __banked
|
||||||
{
|
{
|
||||||
@@ -429,30 +422,12 @@ void port_l2_learned(void) __banked
|
|||||||
/*
|
/*
|
||||||
* Static L2 multicast entry for the link-local group 01:80:C2:00:00:<mac_last>
|
* Static L2 multicast entry for the link-local group 01:80:C2:00:00:<mac_last>
|
||||||
* in VLAN `vid`, with member portmask `pmask` (bit 9 = CPU port).
|
* in VLAN `vid`, with member portmask `pmask` (bit 9 = CPU port).
|
||||||
*
|
|
||||||
* Slow-protocol frames (LACP, STP BPDUs) must reach the CPU without being
|
|
||||||
* flooded to other ports. The RMA "trap" action cannot deliver to the
|
|
||||||
* internal NIC on this hardware (its destination is an external CPU on a
|
|
||||||
* physical port), so the protocol modules keep the RMA action at "forward"
|
|
||||||
* and constrain the egress with this entry instead: the lookup hits the
|
|
||||||
* entry's portmask rather than the VLAN flood mask (hardware-verified with
|
|
||||||
* both the CPU bit cleared - delivery stops - and CPU-only - no egress).
|
|
||||||
*
|
|
||||||
* SMI layout (vendor SDK, L2-multicast entry variant):
|
|
||||||
* DATA_IN_A = MAC bytes 5..2 -> c2 00 00 <mac_last>
|
|
||||||
* DATA_IN_B = MAC[1..0] | vid<<16 | IVL<<29 | pmask[1:0]<<30
|
|
||||||
* DATA_IN_C = pmask[9:2]
|
|
||||||
* Lookups are IVL (a VID-0 entry is not matched), so callers add one entry
|
|
||||||
* per PVID in use. The write command (table 4 = the whole L2 LUT) hashes
|
|
||||||
* MAC+VID and picks the bucket slot itself; TBL_EXECUTE self-clears.
|
|
||||||
* Overwriting the same MAC+VID replaces the entry, so a caller can retarget
|
|
||||||
* the mask at will (e.g. back to all ports to restore flooding).
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void port_l2mc_set(uint8_t mac_last, __xdata uint16_t vid, __xdata uint16_t pmask) __banked
|
void port_l2mc_set(uint8_t mac_last, __xdata uint16_t vid, __xdata uint16_t pmask) __banked
|
||||||
{
|
{
|
||||||
l2mc_guard = 0;
|
l2mc_guard = 0;
|
||||||
do { /* wait out any previous table op (bounded, cf. the IGMP guards) */
|
do {
|
||||||
reg_read_m(RTL837X_TBL_CTRL);
|
reg_read_m(RTL837X_TBL_CTRL);
|
||||||
} while ((sfr_data[3] & TBL_EXECUTE) && ++l2mc_guard);
|
} while ((sfr_data[3] & TBL_EXECUTE) && ++l2mc_guard);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user