Skip to content

Commit

Permalink
realtek: 6.6: fix VLAN handling
Browse files Browse the repository at this point in the history
The CPU port of realtek switches needs some proper PVID set to handle
untagged packets. Because the ethernet driver does no special VLAN
handling (see CPU tag RVID/RVID_SEL) as of now we can only steer
untagged packets by setting PVID for the CPU port. VLAN handling has
never been perfect but 3 events made things worse.

- Commit a376508 ("rtl83xx: dsa: Do nothing when vid 0")
- Commit e691e2b ("rtl83xx: dsa: reset PVID to 1 instead of 0")
- Upgrade to kernel 6.6

Reasons are:

- Rejecting VID 0 disabled Linux initialization routines
- Initialization for PVID forgot to set priv->ports[port].pvid
- Kernel 6.6 does no longer clarify CPU port as untagged

To fix this prepare the VID 0 setup inside the driver. Join all ports
to VID 0 and let no one from outsinde interfere with this setup.
Especially ignore PVID settings for the CPU port for all further
VLAN commands.

Signed-off-by: Markus Stockhausen <[email protected]>
Suggested-by: Bjørn Mork <[email protected]>
  • Loading branch information
plappermaul committed Sep 9, 2024
1 parent 0df0d6a commit 4c6fd91
Showing 1 changed file with 45 additions and 26 deletions.
71 changes: 45 additions & 26 deletions target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,20 @@ static enum dsa_tag_protocol rtl83xx_get_tag_protocol(struct dsa_switch *ds,
return DSA_TAG_PROTO_TRAILER;
}

static void rtl83xx_vlan_set_pvid(struct rtl838x_switch_priv *priv,
int port, int pvid)
{
/* Set both inner and outer PVID of the port */
priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, pvid);
priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, pvid);
priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER,
PBVLAN_MODE_UNTAG_AND_PRITAG);
priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER,
PBVLAN_MODE_UNTAG_AND_PRITAG);

priv->ports[port].pvid = pvid;
}

/* Initialize all VLANS */
static void rtl83xx_vlan_setup(struct rtl838x_switch_priv *priv)
{
Expand All @@ -132,17 +146,22 @@ static void rtl83xx_vlan_setup(struct rtl838x_switch_priv *priv)
info.l2_tunnel_list_id = -1;
}

/* Initialize all vlans 0-4095 */
for (int i = 0; i < MAX_VLANS; i ++)
/* Initialize normal VLANs 1-4095 */
for (int i = 1; i < MAX_VLANS; i ++)
priv->r->vlan_set_tagged(i, &info);

/* reset PVIDs; defaults to 1 on reset */
/*
* Initialize the special VLAN 0 and reset PVIDs. The CPU port PVID
* is applied to packets from the CPU for untagged destinations,
* regardless if the actual ingress VID. Any port with untagged
* egress VLAN(s) must therefore be a member of VLAN 0 to support
* CPU port as ingress when VLAN filtering is enabled.
*/
for (int i = 0; i <= priv->cpu_port; i++) {
priv->r->vlan_port_pvid_set(i, PBVLAN_TYPE_INNER, 1);
priv->r->vlan_port_pvid_set(i, PBVLAN_TYPE_OUTER, 1);
priv->r->vlan_port_pvidmode_set(i, PBVLAN_TYPE_INNER, PBVLAN_MODE_UNTAG_AND_PRITAG);
priv->r->vlan_port_pvidmode_set(i, PBVLAN_TYPE_OUTER, PBVLAN_MODE_UNTAG_AND_PRITAG);
rtl83xx_vlan_set_pvid(priv, i, 0);
info.tagged_ports |= BIT_ULL(i);
}
priv->r->vlan_set_tagged(0, &info);

/* Set forwarding action based on inner VLAN tag */
for (int i = 0; i < priv->cpu_port; i++)
Expand Down Expand Up @@ -1418,20 +1437,6 @@ static int rtl83xx_vlan_prepare(struct dsa_switch *ds, int port,
return 0;
}

static void rtl83xx_vlan_set_pvid(struct rtl838x_switch_priv *priv,
int port, int pvid)
{
/* Set both inner and outer PVID of the port */
priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, pvid);
priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, pvid);
priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER,
PBVLAN_MODE_UNTAG_AND_PRITAG);
priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER,
PBVLAN_MODE_UNTAG_AND_PRITAG);

priv->ports[port].pvid = pvid;
}

static int rtl83xx_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct netlink_ext_ack *extack)
Expand All @@ -1443,7 +1448,8 @@ static int rtl83xx_vlan_add(struct dsa_switch *ds, int port,
pr_debug("%s port %d, vid %d, flags %x\n",
__func__, port, vlan->vid, vlan->flags);

if(!vlan->vid) return 0;
/* Let no one mess with our special VLAN 0 */
if (!vlan->vid) return 0;

if (vlan->vid > 4095) {
dev_err(priv->dev, "VLAN out of range: %d", vlan->vid);
Expand All @@ -1456,10 +1462,20 @@ static int rtl83xx_vlan_add(struct dsa_switch *ds, int port,

mutex_lock(&priv->reg_mutex);

if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
rtl83xx_vlan_set_pvid(priv, port, vlan->vid);
else if (priv->ports[port].pvid == vlan->vid)
rtl83xx_vlan_set_pvid(priv, port, 0);
/*
* Realtek switches copy frames as-is to/from the CPU. For a proper
* VLAN handling the 12 bit RVID field (= VLAN id) for incoming traffic
* and the 1 bit RVID_SEL field (0 = use inner tag, 1 = use outer tag)
* for outgoing traffic of the CPU tag structure need to be handled. As
* of now no such logic is in place. So for the CPU port keep the fixed
* PVID=0 from initial setup in place and ignore all subsequent settings.
*/
if (port != priv->cpu_port) {
if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
rtl83xx_vlan_set_pvid(priv, port, vlan->vid);
else if (priv->ports[port].pvid == vlan->vid)
rtl83xx_vlan_set_pvid(priv, port, 0);
}

/* Get port memberships of this vlan */
priv->r->vlan_tables_read(vlan->vid, &info);
Expand Down Expand Up @@ -1503,6 +1519,9 @@ static int rtl83xx_vlan_del(struct dsa_switch *ds, int port,
pr_debug("%s: port %d, vid %d, flags %x\n",
__func__, port, vlan->vid, vlan->flags);

/* Let no one mess with our special VLAN 0 */
if (!vlan->vid) return 0;

if (vlan->vid > 4095) {
dev_err(priv->dev, "VLAN out of range: %d", vlan->vid);
return -ENOTSUPP;
Expand Down

0 comments on commit 4c6fd91

Please sign in to comment.