Skip to content

Commit

Permalink
Merge branch 'dsa-bridge-tx-forwarding-offload-fixes-part-1'
Browse files Browse the repository at this point in the history
Vladimir Oltean says:

====================
DSA bridge TX forwarding offload fixes - part 1

This is part 1 of a series of fixes to the bridge TX forwarding offload
feature introduced for v5.15. Sadly, the other fixes are so intrusive
that they cannot be reasonably be sent to the "net" tree, as they also
include API changes. So they are left as part 2 for net-next.
====================

Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Jakub Kicinski <[email protected]>
  • Loading branch information
kuba-moo committed Oct 8, 2021
2 parents 1b1499a + 5bded82 commit bccf56c
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 35 deletions.
1 change: 1 addition & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -11153,6 +11153,7 @@ S: Maintained
F: Documentation/devicetree/bindings/net/dsa/marvell.txt
F: Documentation/networking/devlink/mv88e6xxx.rst
F: drivers/net/dsa/mv88e6xxx/
F: include/linux/dsa/mv88e6xxx.h
F: include/linux/platform_data/mv88e6xxx.h

MARVELL ARMADA 3700 PHY DRIVERS
Expand Down
112 changes: 97 additions & 15 deletions drivers/net/dsa/mv88e6xxx/chip.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/dsa/mv88e6xxx.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/if_bridge.h>
Expand Down Expand Up @@ -1677,6 +1678,30 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
return 0;
}

static int mv88e6xxx_port_commit_pvid(struct mv88e6xxx_chip *chip, int port)
{
struct dsa_port *dp = dsa_to_port(chip->ds, port);
struct mv88e6xxx_port *p = &chip->ports[port];
u16 pvid = MV88E6XXX_VID_STANDALONE;
bool drop_untagged = false;
int err;

if (dp->bridge_dev) {
if (br_vlan_enabled(dp->bridge_dev)) {
pvid = p->bridge_pvid.vid;
drop_untagged = !p->bridge_pvid.valid;
} else {
pvid = MV88E6XXX_VID_BRIDGED;
}
}

err = mv88e6xxx_port_set_pvid(chip, port, pvid);
if (err)
return err;

return mv88e6xxx_port_drop_untagged(chip, port, drop_untagged);
}

static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
bool vlan_filtering,
struct netlink_ext_ack *extack)
Expand All @@ -1690,7 +1715,16 @@ static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
return -EOPNOTSUPP;

mv88e6xxx_reg_lock(chip);

err = mv88e6xxx_port_set_8021q_mode(chip, port, mode);
if (err)
goto unlock;

err = mv88e6xxx_port_commit_pvid(chip, port);
if (err)
goto unlock;

unlock:
mv88e6xxx_reg_unlock(chip);

return err;
Expand Down Expand Up @@ -1725,11 +1759,15 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
u16 fid;
int err;

/* Null VLAN ID corresponds to the port private database */
/* Ports have two private address databases: one for when the port is
* standalone and one for when the port is under a bridge and the
* 802.1Q mode is disabled. When the port is standalone, DSA wants its
* address database to remain 100% empty, so we never load an ATU entry
* into a standalone port's database. Therefore, translate the null
* VLAN ID into the port's database used for VLAN-unaware bridging.
*/
if (vid == 0) {
err = mv88e6xxx_port_get_fid(chip, port, &fid);
if (err)
return err;
fid = MV88E6XXX_FID_BRIDGED;
} else {
err = mv88e6xxx_vtu_get(chip, vid, &vlan);
if (err)
Expand Down Expand Up @@ -2123,6 +2161,7 @@ static int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
struct mv88e6xxx_chip *chip = ds->priv;
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
struct mv88e6xxx_port *p = &chip->ports[port];
bool warn;
u8 member;
int err;
Expand Down Expand Up @@ -2156,13 +2195,21 @@ static int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
}

if (pvid) {
err = mv88e6xxx_port_set_pvid(chip, port, vlan->vid);
if (err) {
dev_err(ds->dev, "p%d: failed to set PVID %d\n",
port, vlan->vid);
p->bridge_pvid.vid = vlan->vid;
p->bridge_pvid.valid = true;

err = mv88e6xxx_port_commit_pvid(chip, port);
if (err)
goto out;
} else if (vlan->vid && p->bridge_pvid.vid == vlan->vid) {
/* The old pvid was reinstalled as a non-pvid VLAN */
p->bridge_pvid.valid = false;

err = mv88e6xxx_port_commit_pvid(chip, port);
if (err)
goto out;
}
}

out:
mv88e6xxx_reg_unlock(chip);

Expand Down Expand Up @@ -2212,6 +2259,7 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan)
{
struct mv88e6xxx_chip *chip = ds->priv;
struct mv88e6xxx_port *p = &chip->ports[port];
int err = 0;
u16 pvid;

Expand All @@ -2229,7 +2277,9 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
goto unlock;

if (vlan->vid == pvid) {
err = mv88e6xxx_port_set_pvid(chip, port, 0);
p->bridge_pvid.valid = false;

err = mv88e6xxx_port_commit_pvid(chip, port);
if (err)
goto unlock;
}
Expand Down Expand Up @@ -2393,7 +2443,16 @@ static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
int err;

mv88e6xxx_reg_lock(chip);

err = mv88e6xxx_bridge_map(chip, br);
if (err)
goto unlock;

err = mv88e6xxx_port_commit_pvid(chip, port);
if (err)
goto unlock;

unlock:
mv88e6xxx_reg_unlock(chip);

return err;
Expand All @@ -2403,11 +2462,20 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port,
struct net_device *br)
{
struct mv88e6xxx_chip *chip = ds->priv;
int err;

mv88e6xxx_reg_lock(chip);

if (mv88e6xxx_bridge_map(chip, br) ||
mv88e6xxx_port_vlan_map(chip, port))
dev_err(ds->dev, "failed to remap in-chip Port VLAN\n");

err = mv88e6xxx_port_commit_pvid(chip, port);
if (err)
dev_err(ds->dev,
"port %d failed to restore standalone pvid: %pe\n",
port, ERR_PTR(err));

mv88e6xxx_reg_unlock(chip);
}

Expand Down Expand Up @@ -2853,6 +2921,20 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
if (err)
return err;

/* Associate MV88E6XXX_VID_BRIDGED with MV88E6XXX_FID_BRIDGED in the
* ATU by virtue of the fact that mv88e6xxx_atu_new() will pick it as
* the first free FID after MV88E6XXX_FID_STANDALONE. This will be used
* as the private PVID on ports under a VLAN-unaware bridge.
* Shared (DSA and CPU) ports must also be members of it, to translate
* the VID from the DSA tag into MV88E6XXX_FID_BRIDGED, instead of
* relying on their port default FID.
*/
err = mv88e6xxx_port_vlan_join(chip, port, MV88E6XXX_VID_BRIDGED,
MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNTAGGED,
false);
if (err)
return err;

if (chip->info->ops->port_set_jumbo_size) {
err = chip->info->ops->port_set_jumbo_size(chip, port, 10218);
if (err)
Expand Down Expand Up @@ -2925,7 +3007,7 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
* database, and allow bidirectional communication between the
* CPU and DSA port(s), and the other ports.
*/
err = mv88e6xxx_port_set_fid(chip, port, 0);
err = mv88e6xxx_port_set_fid(chip, port, MV88E6XXX_FID_STANDALONE);
if (err)
return err;

Expand Down Expand Up @@ -3115,6 +3197,10 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
}
}

err = mv88e6xxx_vtu_setup(chip);
if (err)
goto unlock;

/* Setup Switch Port Registers */
for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
if (dsa_is_unused_port(ds, i))
Expand Down Expand Up @@ -3144,10 +3230,6 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
if (err)
goto unlock;

err = mv88e6xxx_vtu_setup(chip);
if (err)
goto unlock;

err = mv88e6xxx_pvt_setup(chip);
if (err)
goto unlock;
Expand Down
9 changes: 9 additions & 0 deletions drivers/net/dsa/mv88e6xxx/chip.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
#define EDSA_HLEN 8
#define MV88E6XXX_N_FID 4096

#define MV88E6XXX_FID_STANDALONE 0
#define MV88E6XXX_FID_BRIDGED 1

/* PVT limits for 4-bit port and 5-bit switch */
#define MV88E6XXX_MAX_PVT_SWITCHES 32
#define MV88E6XXX_MAX_PVT_PORTS 16
Expand Down Expand Up @@ -246,9 +249,15 @@ struct mv88e6xxx_policy {
u16 vid;
};

struct mv88e6xxx_vlan {
u16 vid;
bool valid;
};

struct mv88e6xxx_port {
struct mv88e6xxx_chip *chip;
int port;
struct mv88e6xxx_vlan bridge_pvid;
u64 serdes_stats[2];
u64 atu_member_violation;
u64 atu_miss_violation;
Expand Down
21 changes: 21 additions & 0 deletions drivers/net/dsa/mv88e6xxx/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,27 @@ int mv88e6xxx_port_set_8021q_mode(struct mv88e6xxx_chip *chip, int port,
return 0;
}

int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port,
bool drop_untagged)
{
u16 old, new;
int err;

err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL2, &old);
if (err)
return err;

if (drop_untagged)
new = old | MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED;
else
new = old & ~MV88E6XXX_PORT_CTL2_DISCARD_UNTAGGED;

if (new == old)
return 0;

return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL2, new);
}

int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port)
{
u16 reg;
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/dsa/mv88e6xxx/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,8 @@ int mv88e6393x_port_set_cmode(struct mv88e6xxx_chip *chip, int port,
phy_interface_t mode);
int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
int mv88e6xxx_port_drop_untagged(struct mv88e6xxx_chip *chip, int port,
bool drop_untagged);
int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);
int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port,
int upstream_port);
Expand Down
13 changes: 13 additions & 0 deletions include/linux/dsa/mv88e6xxx.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: GPL-2.0
* Copyright 2021 NXP
*/

#ifndef _NET_DSA_TAG_MV88E6XXX_H
#define _NET_DSA_TAG_MV88E6XXX_H

#include <linux/if_vlan.h>

#define MV88E6XXX_VID_STANDALONE 0
#define MV88E6XXX_VID_BRIDGED (VLAN_N_VID - 1)

#endif
2 changes: 1 addition & 1 deletion net/dsa/dsa2.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ void dsa_bridge_num_put(const struct net_device *bridge_dev, int bridge_num)
/* Check if the bridge is still in use, otherwise it is time
* to clean it up so we can reuse this bridge_num later.
*/
if (!dsa_bridge_num_find(bridge_dev))
if (dsa_bridge_num_find(bridge_dev) < 0)
clear_bit(bridge_num, &dsa_fwd_offloading_bridges);
}

Expand Down
28 changes: 9 additions & 19 deletions net/dsa/tag_dsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
* 6 6 2 2 4 2 N
*/

#include <linux/dsa/mv88e6xxx.h>
#include <linux/etherdevice.h>
#include <linux/list.h>
#include <linux/slab.h>
Expand Down Expand Up @@ -129,12 +130,9 @@ static struct sk_buff *dsa_xmit_ll(struct sk_buff *skb, struct net_device *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;

Expand All @@ -144,19 +142,6 @@ static struct sk_buff *dsa_xmit_ll(struct sk_buff *skb, struct net_device *dev,
*/
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;
Expand All @@ -180,16 +165,21 @@ static struct sk_buff *dsa_xmit_ll(struct sk_buff *skb, struct net_device *dev,
dsa_header[2] &= ~0x10;
}
} else {
struct net_device *br = dp->bridge_dev;
u16 vid;

vid = br ? MV88E6XXX_VID_BRIDGED : MV88E6XXX_VID_STANDALONE;

skb_push(skb, DSA_HLEN + extra);
dsa_alloc_etype_header(skb, DSA_HLEN + extra);

/* Construct untagged DSA tag. */
/* Construct DSA header from untagged frame. */
dsa_header = dsa_etype_header_pos_tx(skb) + extra;

dsa_header[0] = (cmd << 6) | tag_dev;
dsa_header[1] = tag_port << 3;
dsa_header[2] = pvid >> 8;
dsa_header[3] = pvid & 0xff;
dsa_header[2] = vid >> 8;
dsa_header[3] = vid & 0xff;
}

return skb;
Expand Down

0 comments on commit bccf56c

Please sign in to comment.