Commit c49379dc authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'net-dsa-vsc73xx-implement-vlan-operations'

Pawel Dembicki says:

====================
net: dsa: vsc73xx: Implement VLAN operations

This patch series is a result of splitting a larger patch series [0],
where some parts was merged before.

The first patch implements port state configuration, which is required
for bridge functionality. STP frames are not forwarded at this moment.
BPDU frames are only forwarded from/to the PI/SI interface.
For more information, see chapter 2.7.1 (CPU Forwarding) in the
datasheet.

Patches 2, 7-9 and 11 provide a basic implementation of tag_8021q
functionality with QinQ support, without VLAN filtering in
the bridge and simple VLAN awareness in VLAN filtering mode.

Patches 3-6 came from Vladimir Oltean. They prepare for making
tag8021q more common. VSC73XX uses very similar tag recognition,
and some code from tag_sja1105 could be moved to tag_8021q for
common use.

Patch 10 is preparation for use tag_8021q bridge functions as generic
implementation of the 'ds->ops->port_bridge_*()'.

Patch 12 is required to avoid problem with learning on standalone ports.

[0] https://patchwork.kernel.org/project/netdevbpf/list/?series=841034&state=%2A&archive=both
====================

Link: https://patch.msgid.link/20240713211620.1125910-1-paweldembicki@gmail.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents a8ea8d53 259a7061
......@@ -127,7 +127,7 @@ config NET_DSA_SMSC_LAN9303_MDIO
config NET_DSA_VITESSE_VSC73XX
tristate
select NET_DSA_TAG_NONE
select NET_DSA_TAG_VSC73XX_8021Q
select FIXED_PHY
select VITESSE_PHY
select GPIOLIB
......
......@@ -2133,14 +2133,13 @@ static int sja1105_bridge_join(struct dsa_switch *ds, int port,
if (rc)
return rc;
rc = dsa_tag_8021q_bridge_join(ds, port, bridge);
rc = dsa_tag_8021q_bridge_join(ds, port, bridge, tx_fwd_offload,
extack);
if (rc) {
sja1105_bridge_member(ds, port, bridge, false);
return rc;
}
*tx_fwd_offload = true;
return 0;
}
......@@ -3167,8 +3166,7 @@ static int sja1105_setup(struct dsa_switch *ds)
ds->vlan_filtering_is_global = true;
ds->untag_bridge_pvid = true;
ds->fdb_isolation = true;
/* tag_8021q has 3 bits for the VBID, and the value 0 is reserved */
ds->max_num_bridges = 7;
ds->max_num_bridges = DSA_TAG_8021Q_MAX_NUM_BRIDGES;
/* Advertise the 8 egress queues */
ds->num_tx_queues = SJA1105_NUM_TC;
......
......@@ -22,9 +22,11 @@
#include <linux/of_mdio.h>
#include <linux/bitops.h>
#include <linux/if_bridge.h>
#include <linux/if_vlan.h>
#include <linux/etherdevice.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/dsa/8021q.h>
#include <linux/random.h>
#include <net/dsa.h>
......@@ -62,6 +64,8 @@
#define VSC73XX_CAT_DROP 0x6e
#define VSC73XX_CAT_PR_MISC_L2 0x6f
#define VSC73XX_CAT_PR_USR_PRIO 0x75
#define VSC73XX_CAT_VLAN_MISC 0x79
#define VSC73XX_CAT_PORT_VLAN 0x7a
#define VSC73XX_Q_MISC_CONF 0xdf
/* MAC_CFG register bits */
......@@ -122,6 +126,17 @@
#define VSC73XX_ADVPORTM_IO_LOOPBACK BIT(1)
#define VSC73XX_ADVPORTM_HOST_LOOPBACK BIT(0)
/* TXUPDCFG transmit modify setup bits */
#define VSC73XX_TXUPDCFG_DSCP_REWR_MODE GENMASK(20, 19)
#define VSC73XX_TXUPDCFG_DSCP_REWR_ENA BIT(18)
#define VSC73XX_TXUPDCFG_TX_INT_TO_USRPRIO_ENA BIT(17)
#define VSC73XX_TXUPDCFG_TX_UNTAGGED_VID GENMASK(15, 4)
#define VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_ENA BIT(3)
#define VSC73XX_TXUPDCFG_TX_UPDATE_CRC_CPU_ENA BIT(1)
#define VSC73XX_TXUPDCFG_TX_INSERT_TAG BIT(0)
#define VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_SHIFT 4
/* CAT_DROP categorizer frame dropping register bits */
#define VSC73XX_CAT_DROP_DROP_MC_SMAC_ENA BIT(6)
#define VSC73XX_CAT_DROP_FWD_CTRL_ENA BIT(4)
......@@ -135,6 +150,15 @@
#define VSC73XX_Q_MISC_CONF_EARLY_TX_512 (1 << 1)
#define VSC73XX_Q_MISC_CONF_MAC_PAUSE_MODE BIT(0)
/* CAT_VLAN_MISC categorizer VLAN miscellaneous bits */
#define VSC73XX_CAT_VLAN_MISC_VLAN_TCI_IGNORE_ENA BIT(8)
#define VSC73XX_CAT_VLAN_MISC_VLAN_KEEP_TAG_ENA BIT(7)
/* CAT_PORT_VLAN categorizer port VLAN */
#define VSC73XX_CAT_PORT_VLAN_VLAN_CFI BIT(15)
#define VSC73XX_CAT_PORT_VLAN_VLAN_USR_PRIO GENMASK(14, 12)
#define VSC73XX_CAT_PORT_VLAN_VLAN_VID GENMASK(11, 0)
/* Frame analyzer block 2 registers */
#define VSC73XX_STORMLIMIT 0x02
#define VSC73XX_ADVLEARN 0x03
......@@ -164,6 +188,10 @@
#define VSC73XX_AGENCTRL 0xf0
#define VSC73XX_CAPRST 0xff
#define VSC73XX_SRCMASKS_CPU_COPY BIT(27)
#define VSC73XX_SRCMASKS_MIRROR BIT(26)
#define VSC73XX_SRCMASKS_PORTS_MASK GENMASK(7, 0)
#define VSC73XX_MACACCESS_CPU_COPY BIT(14)
#define VSC73XX_MACACCESS_FWD_KILL BIT(13)
#define VSC73XX_MACACCESS_IGNORE_VLAN BIT(12)
......@@ -185,7 +213,8 @@
#define VSC73XX_VLANACCESS_VLAN_MIRROR BIT(29)
#define VSC73XX_VLANACCESS_VLAN_SRC_CHECK BIT(28)
#define VSC73XX_VLANACCESS_VLAN_PORT_MASK GENMASK(9, 2)
#define VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK GENMASK(2, 0)
#define VSC73XX_VLANACCESS_VLAN_PORT_MASK_SHIFT 2
#define VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK GENMASK(1, 0)
#define VSC73XX_VLANACCESS_VLAN_TBL_CMD_IDLE 0
#define VSC73XX_VLANACCESS_VLAN_TBL_CMD_READ_ENTRY 1
#define VSC73XX_VLANACCESS_VLAN_TBL_CMD_WRITE_ENTRY 2
......@@ -343,6 +372,17 @@ static const struct vsc73xx_counter vsc73xx_tx_counters[] = {
{ 29, "TxQoSClass3" }, /* non-standard counter */
};
struct vsc73xx_vlan_summary {
size_t num_tagged;
size_t num_untagged;
};
enum vsc73xx_port_vlan_conf {
VSC73XX_VLAN_FILTER,
VSC73XX_VLAN_FILTER_UNTAG_ALL,
VSC73XX_VLAN_IGNORE,
};
int vsc73xx_is_addr_valid(u8 block, u8 subblock)
{
switch (block) {
......@@ -557,16 +597,103 @@ static enum dsa_tag_protocol vsc73xx_get_tag_protocol(struct dsa_switch *ds,
* cannot access the tag. (See "Internal frame header" section
* 3.9.1 in the manual.)
*/
return DSA_TAG_PROTO_NONE;
return DSA_TAG_PROTO_VSC73XX_8021Q;
}
static int vsc73xx_wait_for_vlan_table_cmd(struct vsc73xx *vsc)
{
int ret, err;
u32 val;
ret = read_poll_timeout(vsc73xx_read, err,
err < 0 ||
((val & VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK) ==
VSC73XX_VLANACCESS_VLAN_TBL_CMD_IDLE),
VSC73XX_POLL_SLEEP_US, VSC73XX_POLL_TIMEOUT_US,
false, vsc, VSC73XX_BLOCK_ANALYZER,
0, VSC73XX_VLANACCESS, &val);
if (ret)
return ret;
return err;
}
static int
vsc73xx_read_vlan_table_entry(struct vsc73xx *vsc, u16 vid, u8 *portmap)
{
u32 val;
int ret;
vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANTIDX, vid);
ret = vsc73xx_wait_for_vlan_table_cmd(vsc);
if (ret)
return ret;
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANACCESS,
VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK,
VSC73XX_VLANACCESS_VLAN_TBL_CMD_READ_ENTRY);
ret = vsc73xx_wait_for_vlan_table_cmd(vsc);
if (ret)
return ret;
vsc73xx_read(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANACCESS, &val);
*portmap = (val & VSC73XX_VLANACCESS_VLAN_PORT_MASK) >>
VSC73XX_VLANACCESS_VLAN_PORT_MASK_SHIFT;
return 0;
}
static int
vsc73xx_write_vlan_table_entry(struct vsc73xx *vsc, u16 vid, u8 portmap)
{
int ret;
vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANTIDX, vid);
ret = vsc73xx_wait_for_vlan_table_cmd(vsc);
if (ret)
return ret;
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANACCESS,
VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK |
VSC73XX_VLANACCESS_VLAN_SRC_CHECK |
VSC73XX_VLANACCESS_VLAN_PORT_MASK,
VSC73XX_VLANACCESS_VLAN_TBL_CMD_WRITE_ENTRY |
VSC73XX_VLANACCESS_VLAN_SRC_CHECK |
(portmap << VSC73XX_VLANACCESS_VLAN_PORT_MASK_SHIFT));
return vsc73xx_wait_for_vlan_table_cmd(vsc);
}
static int
vsc73xx_update_vlan_table(struct vsc73xx *vsc, int port, u16 vid, bool set)
{
u8 portmap;
int ret;
ret = vsc73xx_read_vlan_table_entry(vsc, vid, &portmap);
if (ret)
return ret;
if (set)
portmap |= BIT(port);
else
portmap &= ~BIT(port);
return vsc73xx_write_vlan_table_entry(vsc, vid, portmap);
}
static int vsc73xx_setup(struct dsa_switch *ds)
{
struct vsc73xx *vsc = ds->priv;
int i;
int i, ret;
dev_info(vsc->dev, "set up the switch\n");
ds->untag_bridge_pvid = true;
ds->max_num_bridges = DSA_TAG_8021Q_MAX_NUM_BRIDGES;
/* Issue RESET */
vsc73xx_write(vsc, VSC73XX_BLOCK_SYSTEM, 0, VSC73XX_GLORESET,
VSC73XX_GLORESET_MASTER_RESET);
......@@ -594,7 +721,7 @@ static int vsc73xx_setup(struct dsa_switch *ds)
VSC73XX_MACACCESS,
VSC73XX_MACACCESS_CMD_CLEAR_TABLE);
/* Clear VLAN table */
/* Set VLAN table to default values */
vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0,
VSC73XX_VLANACCESS,
VSC73XX_VLANACCESS_VLAN_TBL_CMD_CLEAR_TABLE);
......@@ -623,9 +750,9 @@ static int vsc73xx_setup(struct dsa_switch *ds)
vsc73xx_write(vsc, VSC73XX_BLOCK_SYSTEM, 0, VSC73XX_GMIIDELAY,
VSC73XX_GMIIDELAY_GMII0_GTXDELAY_2_0_NS |
VSC73XX_GMIIDELAY_GMII0_RXDELAY_2_0_NS);
/* Enable reception of frames on all ports */
vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_RECVMASK,
0x5f);
/* Ingess VLAN reception mask (table 145) */
vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANMASK,
0xff);
/* IP multicast flood mask (table 144) */
vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_IFLODMSK,
0xff);
......@@ -638,7 +765,24 @@ static int vsc73xx_setup(struct dsa_switch *ds)
udelay(4);
return 0;
/* Clear VLAN table */
for (i = 0; i < VLAN_N_VID; i++)
vsc73xx_write_vlan_table_entry(vsc, i, 0);
INIT_LIST_HEAD(&vsc->vlans);
rtnl_lock();
ret = dsa_tag_8021q_register(ds, htons(ETH_P_8021Q));
rtnl_unlock();
return ret;
}
static void vsc73xx_teardown(struct dsa_switch *ds)
{
rtnl_lock();
dsa_tag_8021q_unregister(ds);
rtnl_unlock();
}
static void vsc73xx_init_port(struct vsc73xx *vsc, int port)
......@@ -788,10 +932,6 @@ static void vsc73xx_mac_link_down(struct phylink_config *config,
/* Allow backward dropping of frames from this port */
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
VSC73XX_SBACKWDROP, BIT(port), BIT(port));
/* Receive mask (disable forwarding) */
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
VSC73XX_RECVMASK, BIT(port), 0);
}
static void vsc73xx_mac_link_up(struct phylink_config *config,
......@@ -828,6 +968,12 @@ static void vsc73xx_mac_link_up(struct phylink_config *config,
val |= seed << VSC73XX_MAC_CFG_SEED_OFFSET;
val |= VSC73XX_MAC_CFG_SEED_LOAD;
val |= VSC73XX_MAC_CFG_WEXC_DIS;
/* Those bits are responsible for MTU only. Kernel takes care about MTU,
* let's enable +8 bytes frame length unconditionally.
*/
val |= VSC73XX_MAC_CFG_VLAN_AWR | VSC73XX_MAC_CFG_VLAN_DBLAWR;
vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_MAC_CFG, val);
/* Flow control for the PHY facing ports:
......@@ -844,10 +990,6 @@ static void vsc73xx_mac_link_up(struct phylink_config *config,
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
VSC73XX_ARBDISC, BIT(port), 0);
/* Enable port (forwarding) in the receive mask */
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
VSC73XX_RECVMASK, BIT(port), BIT(port));
/* Disallow backward dropping of frames from this port */
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ARBITER, 0,
VSC73XX_SBACKWDROP, BIT(port), 0);
......@@ -860,6 +1002,257 @@ static void vsc73xx_mac_link_up(struct phylink_config *config,
VSC73XX_MAC_CFG_TX_EN | VSC73XX_MAC_CFG_RX_EN);
}
static bool vsc73xx_tag_8021q_active(struct dsa_port *dp)
{
return !dsa_port_is_vlan_filtering(dp);
}
static struct vsc73xx_bridge_vlan *
vsc73xx_bridge_vlan_find(struct vsc73xx *vsc, u16 vid)
{
struct vsc73xx_bridge_vlan *vlan;
list_for_each_entry(vlan, &vsc->vlans, list)
if (vlan->vid == vid)
return vlan;
return NULL;
}
static void
vsc73xx_bridge_vlan_remove_port(struct vsc73xx_bridge_vlan *vsc73xx_vlan,
int port)
{
vsc73xx_vlan->portmask &= ~BIT(port);
if (vsc73xx_vlan->portmask)
return;
list_del(&vsc73xx_vlan->list);
kfree(vsc73xx_vlan);
}
static void vsc73xx_bridge_vlan_summary(struct vsc73xx *vsc, int port,
struct vsc73xx_vlan_summary *summary,
u16 ignored_vid)
{
size_t num_tagged = 0, num_untagged = 0;
struct vsc73xx_bridge_vlan *vlan;
list_for_each_entry(vlan, &vsc->vlans, list) {
if (!(vlan->portmask & BIT(port)) || vlan->vid == ignored_vid)
continue;
if (vlan->untagged & BIT(port))
num_untagged++;
else
num_tagged++;
}
summary->num_untagged = num_untagged;
summary->num_tagged = num_tagged;
}
static u16 vsc73xx_find_first_vlan_untagged(struct vsc73xx *vsc, int port)
{
struct vsc73xx_bridge_vlan *vlan;
list_for_each_entry(vlan, &vsc->vlans, list)
if ((vlan->portmask & BIT(port)) &&
(vlan->untagged & BIT(port)))
return vlan->vid;
return VLAN_N_VID;
}
static int vsc73xx_set_vlan_conf(struct vsc73xx *vsc, int port,
enum vsc73xx_port_vlan_conf port_vlan_conf)
{
u32 val = 0;
int ret;
if (port_vlan_conf == VSC73XX_VLAN_IGNORE)
val = VSC73XX_CAT_VLAN_MISC_VLAN_TCI_IGNORE_ENA |
VSC73XX_CAT_VLAN_MISC_VLAN_KEEP_TAG_ENA;
ret = vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port,
VSC73XX_CAT_VLAN_MISC,
VSC73XX_CAT_VLAN_MISC_VLAN_TCI_IGNORE_ENA |
VSC73XX_CAT_VLAN_MISC_VLAN_KEEP_TAG_ENA, val);
if (ret)
return ret;
val = (port_vlan_conf == VSC73XX_VLAN_FILTER) ?
VSC73XX_TXUPDCFG_TX_INSERT_TAG : 0;
return vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port,
VSC73XX_TXUPDCFG,
VSC73XX_TXUPDCFG_TX_INSERT_TAG, val);
}
/**
* vsc73xx_vlan_commit_conf - Update VLAN configuration of a port
* @vsc: Switch private data structure
* @port: Port index on which to operate
*
* Update the VLAN behavior of a port to make sure that when it is under
* a VLAN filtering bridge, the port is either filtering with tag
* preservation, or filtering with all VLANs egress-untagged. Otherwise,
* the port ignores VLAN tags from packets and applies the port-based
* VID.
*
* Must be called when changes are made to:
* - the bridge VLAN filtering state of the port
* - the number or attributes of VLANs from the bridge VLAN table,
* while the port is currently VLAN-aware
*
* Return: 0 on success, or negative errno on error.
*/
static int vsc73xx_vlan_commit_conf(struct vsc73xx *vsc, int port)
{
enum vsc73xx_port_vlan_conf port_vlan_conf = VSC73XX_VLAN_IGNORE;
struct dsa_port *dp = dsa_to_port(vsc->ds, port);
if (port == CPU_PORT) {
port_vlan_conf = VSC73XX_VLAN_FILTER;
} else if (dsa_port_is_vlan_filtering(dp)) {
struct vsc73xx_vlan_summary summary;
port_vlan_conf = VSC73XX_VLAN_FILTER;
vsc73xx_bridge_vlan_summary(vsc, port, &summary, VLAN_N_VID);
if (summary.num_tagged == 0)
port_vlan_conf = VSC73XX_VLAN_FILTER_UNTAG_ALL;
}
return vsc73xx_set_vlan_conf(vsc, port, port_vlan_conf);
}
static int
vsc73xx_vlan_change_untagged(struct vsc73xx *vsc, int port, u16 vid, bool set)
{
u32 val = 0;
if (set)
val = VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_ENA |
((vid << VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_SHIFT) &
VSC73XX_TXUPDCFG_TX_UNTAGGED_VID);
return vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port,
VSC73XX_TXUPDCFG,
VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_ENA |
VSC73XX_TXUPDCFG_TX_UNTAGGED_VID, val);
}
/**
* vsc73xx_vlan_commit_untagged - Update native VLAN of a port
* @vsc: Switch private data structure
* @port: Port index on which to operate
*
* Update the native VLAN of a port (the one VLAN which is transmitted
* as egress-tagged on a trunk port) when port is in VLAN filtering mode and
* only one untagged vid is configured.
* In other cases no need to configure it because switch can untag all vlans on
* the port.
*
* Return: 0 on success, or negative errno on error.
*/
static int vsc73xx_vlan_commit_untagged(struct vsc73xx *vsc, int port)
{
struct dsa_port *dp = dsa_to_port(vsc->ds, port);
struct vsc73xx_vlan_summary summary;
u16 vid = 0;
bool valid;
if (!dsa_port_is_vlan_filtering(dp))
/* Port is configured to untag all vlans in that case.
* No need to commit untagged config change.
*/
return 0;
vsc73xx_bridge_vlan_summary(vsc, port, &summary, VLAN_N_VID);
if (summary.num_untagged > 1)
/* Port must untag all vlans in that case.
* No need to commit untagged config change.
*/
return 0;
valid = (summary.num_untagged == 1);
if (valid)
vid = vsc73xx_find_first_vlan_untagged(vsc, port);
return vsc73xx_vlan_change_untagged(vsc, port, vid, valid);
}
static int
vsc73xx_vlan_change_pvid(struct vsc73xx *vsc, int port, u16 vid, bool set)
{
u32 val = 0;
int ret;
val = set ? 0 : VSC73XX_CAT_DROP_UNTAGGED_ENA;
ret = vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port,
VSC73XX_CAT_DROP,
VSC73XX_CAT_DROP_UNTAGGED_ENA, val);
if (!set || ret)
return ret;
return vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port,
VSC73XX_CAT_PORT_VLAN,
VSC73XX_CAT_PORT_VLAN_VLAN_VID,
vid & VSC73XX_CAT_PORT_VLAN_VLAN_VID);
}
/**
* vsc73xx_vlan_commit_pvid - Update port-based default VLAN of a port
* @vsc: Switch private data structure
* @port: Port index on which to operate
*
* Update the PVID of a port so that it follows either the bridge PVID
* configuration, when the bridge is currently VLAN-aware, or the PVID
* from tag_8021q, when the port is standalone or under a VLAN-unaware
* bridge. A port with no PVID drops all untagged and VID 0 tagged
* traffic.
*
* Must be called when changes are made to:
* - the bridge VLAN filtering state of the port
* - the number or attributes of VLANs from the bridge VLAN table,
* while the port is currently VLAN-aware
*
* Return: 0 on success, or negative errno on error.
*/
static int vsc73xx_vlan_commit_pvid(struct vsc73xx *vsc, int port)
{
struct vsc73xx_portinfo *portinfo = &vsc->portinfo[port];
bool valid = portinfo->pvid_tag_8021q_configured;
struct dsa_port *dp = dsa_to_port(vsc->ds, port);
u16 vid = portinfo->pvid_tag_8021q;
if (dsa_port_is_vlan_filtering(dp)) {
vid = portinfo->pvid_vlan_filtering;
valid = portinfo->pvid_vlan_filtering_configured;
}
return vsc73xx_vlan_change_pvid(vsc, port, vid, valid);
}
static int vsc73xx_vlan_commit_settings(struct vsc73xx *vsc, int port)
{
int ret;
ret = vsc73xx_vlan_commit_untagged(vsc, port);
if (ret)
return ret;
ret = vsc73xx_vlan_commit_pvid(vsc, port);
if (ret)
return ret;
return vsc73xx_vlan_commit_conf(vsc, port);
}
static int vsc73xx_port_enable(struct dsa_switch *ds, int port,
struct phy_device *phy)
{
......@@ -868,7 +1261,7 @@ static int vsc73xx_port_enable(struct dsa_switch *ds, int port,
dev_info(vsc->dev, "enable port %d\n", port);
vsc73xx_init_port(vsc, port);
return 0;
return vsc73xx_vlan_commit_settings(vsc, port);
}
static void vsc73xx_port_disable(struct dsa_switch *ds, int port)
......@@ -1039,6 +1432,303 @@ static void vsc73xx_phylink_get_caps(struct dsa_switch *dsa, int port,
config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000;
}
static int
vsc73xx_port_vlan_filtering(struct dsa_switch *ds, int port,
bool vlan_filtering, struct netlink_ext_ack *extack)
{
struct vsc73xx *vsc = ds->priv;
/* The commit to hardware processed below is required because vsc73xx
* is using tag_8021q. When vlan_filtering is disabled, tag_8021q uses
* pvid/untagged vlans for port recognition. The values configured for
* vlans and pvid/untagged states are stored in portinfo structure.
* When vlan_filtering is enabled, we need to restore pvid/untagged from
* portinfo structure. Analogous routine is processed when
* vlan_filtering is disabled, but values used for tag_8021q are
* restored.
*/
return vsc73xx_vlan_commit_settings(vsc, port);
}
static int vsc73xx_port_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
struct netlink_ext_ack *extack)
{
bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
struct dsa_port *dp = dsa_to_port(ds, port);
struct vsc73xx_bridge_vlan *vsc73xx_vlan;
struct vsc73xx_vlan_summary summary;
struct vsc73xx_portinfo *portinfo;
struct vsc73xx *vsc = ds->priv;
bool commit_to_hardware;
int ret = 0;
/* Be sure to deny alterations to the configuration done by tag_8021q.
*/
if (vid_is_dsa_8021q(vlan->vid)) {
NL_SET_ERR_MSG_MOD(extack,
"Range 3072-4095 reserved for dsa_8021q operation");
return -EBUSY;
}
/* The processed vlan->vid is excluded from the search because the VLAN
* can be re-added with a different set of flags, so it's easiest to
* ignore its old flags from the VLAN database software copy.
*/
vsc73xx_bridge_vlan_summary(vsc, port, &summary, vlan->vid);
/* VSC73XX allows only three untagged states: none, one or all */
if ((untagged && summary.num_tagged > 0 && summary.num_untagged > 0) ||
(!untagged && summary.num_untagged > 1)) {
NL_SET_ERR_MSG_MOD(extack,
"Port can have only none, one or all untagged vlan");
return -EBUSY;
}
vsc73xx_vlan = vsc73xx_bridge_vlan_find(vsc, vlan->vid);
if (!vsc73xx_vlan) {
vsc73xx_vlan = kzalloc(sizeof(*vsc73xx_vlan), GFP_KERNEL);
if (!vsc73xx_vlan)
return -ENOMEM;
vsc73xx_vlan->vid = vlan->vid;
list_add_tail(&vsc73xx_vlan->list, &vsc->vlans);
}
vsc73xx_vlan->portmask |= BIT(port);
/* CPU port must be always tagged because source port identification is
* based on tag_8021q.
*/
if (port == CPU_PORT)
goto update_vlan_table;
if (untagged)
vsc73xx_vlan->untagged |= BIT(port);
else
vsc73xx_vlan->untagged &= ~BIT(port);
portinfo = &vsc->portinfo[port];
if (pvid) {
portinfo->pvid_vlan_filtering_configured = true;
portinfo->pvid_vlan_filtering = vlan->vid;
} else if (portinfo->pvid_vlan_filtering_configured &&
portinfo->pvid_vlan_filtering == vlan->vid) {
portinfo->pvid_vlan_filtering_configured = false;
}
commit_to_hardware = !vsc73xx_tag_8021q_active(dp);
if (commit_to_hardware) {
ret = vsc73xx_vlan_commit_settings(vsc, port);
if (ret)
goto err;
}
update_vlan_table:
ret = vsc73xx_update_vlan_table(vsc, port, vlan->vid, true);
if (!ret)
return 0;
err:
vsc73xx_bridge_vlan_remove_port(vsc73xx_vlan, port);
return ret;
}
static int vsc73xx_port_vlan_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan)
{
struct vsc73xx_bridge_vlan *vsc73xx_vlan;
struct vsc73xx_portinfo *portinfo;
struct vsc73xx *vsc = ds->priv;
bool commit_to_hardware;
int ret;
ret = vsc73xx_update_vlan_table(vsc, port, vlan->vid, false);
if (ret)
return ret;
portinfo = &vsc->portinfo[port];
if (portinfo->pvid_vlan_filtering_configured &&
portinfo->pvid_vlan_filtering == vlan->vid)
portinfo->pvid_vlan_filtering_configured = false;
vsc73xx_vlan = vsc73xx_bridge_vlan_find(vsc, vlan->vid);
if (vsc73xx_vlan)
vsc73xx_bridge_vlan_remove_port(vsc73xx_vlan, port);
commit_to_hardware = !vsc73xx_tag_8021q_active(dsa_to_port(ds, port));
if (commit_to_hardware)
return vsc73xx_vlan_commit_settings(vsc, port);
return 0;
}
static int vsc73xx_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
u16 flags)
{
bool pvid = flags & BRIDGE_VLAN_INFO_PVID;
struct vsc73xx_portinfo *portinfo;
struct vsc73xx *vsc = ds->priv;
bool commit_to_hardware;
int ret;
portinfo = &vsc->portinfo[port];
if (pvid) {
portinfo->pvid_tag_8021q_configured = true;
portinfo->pvid_tag_8021q = vid;
}
commit_to_hardware = vsc73xx_tag_8021q_active(dsa_to_port(ds, port));
if (commit_to_hardware) {
ret = vsc73xx_vlan_commit_settings(vsc, port);
if (ret)
return ret;
}
return vsc73xx_update_vlan_table(vsc, port, vid, true);
}
static int vsc73xx_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)
{
struct vsc73xx_portinfo *portinfo;
struct vsc73xx *vsc = ds->priv;
portinfo = &vsc->portinfo[port];
if (portinfo->pvid_tag_8021q_configured &&
portinfo->pvid_tag_8021q == vid) {
struct dsa_port *dp = dsa_to_port(ds, port);
bool commit_to_hardware;
int err;
portinfo->pvid_tag_8021q_configured = false;
commit_to_hardware = vsc73xx_tag_8021q_active(dp);
if (commit_to_hardware) {
err = vsc73xx_vlan_commit_settings(vsc, port);
if (err)
return err;
}
}
return vsc73xx_update_vlan_table(vsc, port, vid, false);
}
static int vsc73xx_port_pre_bridge_flags(struct dsa_switch *ds, int port,
struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack)
{
if (flags.mask & ~BR_LEARNING)
return -EINVAL;
return 0;
}
static int vsc73xx_port_bridge_flags(struct dsa_switch *ds, int port,
struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack)
{
if (flags.mask & BR_LEARNING) {
u32 val = flags.val & BR_LEARNING ? BIT(port) : 0;
struct vsc73xx *vsc = ds->priv;
return vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
VSC73XX_LEARNMASK, BIT(port), val);
}
return 0;
}
static void vsc73xx_refresh_fwd_map(struct dsa_switch *ds, int port, u8 state)
{
struct dsa_port *other_dp, *dp = dsa_to_port(ds, port);
struct vsc73xx *vsc = ds->priv;
u16 mask;
if (state != BR_STATE_FORWARDING) {
/* Ports that aren't in the forwarding state must not
* forward packets anywhere.
*/
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
VSC73XX_SRCMASKS + port,
VSC73XX_SRCMASKS_PORTS_MASK, 0);
dsa_switch_for_each_available_port(other_dp, ds) {
if (other_dp == dp)
continue;
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
VSC73XX_SRCMASKS + other_dp->index,
BIT(port), 0);
}
return;
}
/* Forwarding ports must forward to the CPU and to other ports
* in the same bridge
*/
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
VSC73XX_SRCMASKS + CPU_PORT, BIT(port), BIT(port));
mask = BIT(CPU_PORT);
dsa_switch_for_each_user_port(other_dp, ds) {
int other_port = other_dp->index;
if (port == other_port || !dsa_port_bridge_same(dp, other_dp) ||
other_dp->stp_state != BR_STATE_FORWARDING)
continue;
mask |= BIT(other_port);
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
VSC73XX_SRCMASKS + other_port,
BIT(port), BIT(port));
}
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
VSC73XX_SRCMASKS + port,
VSC73XX_SRCMASKS_PORTS_MASK, mask);
}
/* FIXME: STP frames aren't forwarded at this moment. BPDU frames are
* forwarded only from and to PI/SI interface. For more info see chapter
* 2.7.1 (CPU Forwarding) in datasheet.
* This function is required for tag_8021q operations.
*/
static void vsc73xx_port_stp_state_set(struct dsa_switch *ds, int port,
u8 state)
{
struct dsa_port *dp = dsa_to_port(ds, port);
struct vsc73xx *vsc = ds->priv;
u32 val = 0;
if (state == BR_STATE_LEARNING || state == BR_STATE_FORWARDING)
val = dp->learning ? BIT(port) : 0;
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
VSC73XX_LEARNMASK, BIT(port), val);
val = (state == BR_STATE_BLOCKING || state == BR_STATE_DISABLED) ?
0 : BIT(port);
vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0,
VSC73XX_RECVMASK, BIT(port), val);
/* CPU Port should always forward packets when user ports are forwarding
* so let's configure it from other ports only.
*/
if (port != CPU_PORT)
vsc73xx_refresh_fwd_map(ds, port, state);
}
static const struct phylink_mac_ops vsc73xx_phylink_mac_ops = {
.mac_config = vsc73xx_mac_config,
.mac_link_down = vsc73xx_mac_link_down,
......@@ -1048,6 +1738,7 @@ static const struct phylink_mac_ops vsc73xx_phylink_mac_ops = {
static const struct dsa_switch_ops vsc73xx_ds_ops = {
.get_tag_protocol = vsc73xx_get_tag_protocol,
.setup = vsc73xx_setup,
.teardown = vsc73xx_teardown,
.phy_read = vsc73xx_phy_read,
.phy_write = vsc73xx_phy_write,
.get_strings = vsc73xx_get_strings,
......@@ -1055,9 +1746,19 @@ static const struct dsa_switch_ops vsc73xx_ds_ops = {
.get_sset_count = vsc73xx_get_sset_count,
.port_enable = vsc73xx_port_enable,
.port_disable = vsc73xx_port_disable,
.port_pre_bridge_flags = vsc73xx_port_pre_bridge_flags,
.port_bridge_flags = vsc73xx_port_bridge_flags,
.port_bridge_join = dsa_tag_8021q_bridge_join,
.port_bridge_leave = dsa_tag_8021q_bridge_leave,
.port_change_mtu = vsc73xx_change_mtu,
.port_max_mtu = vsc73xx_get_max_mtu,
.port_stp_state_set = vsc73xx_port_stp_state_set,
.port_vlan_filtering = vsc73xx_port_vlan_filtering,
.port_vlan_add = vsc73xx_port_vlan_add,
.port_vlan_del = vsc73xx_port_vlan_del,
.phylink_get_caps = vsc73xx_phylink_get_caps,
.tag_8021q_vlan_add = vsc73xx_tag_8021q_vlan_add,
.tag_8021q_vlan_del = vsc73xx_tag_8021q_vlan_del,
};
static int vsc73xx_gpio_get(struct gpio_chip *chip, unsigned int offset)
......
......@@ -14,6 +14,22 @@
*/
#define VSC73XX_MAX_NUM_PORTS 8
/**
* struct vsc73xx_portinfo - port data structure: contains storage data
* @pvid_vlan_filtering: pvid vlan number used in vlan filtering mode
* @pvid_tag_8021q: pvid vlan number used in tag_8021q mode
* @pvid_vlan_filtering_configured: informs if port has configured pvid in vlan
* filtering mode
* @pvid_tag_8021q_configured: imforms if port have configured pvid in tag_8021q
* mode
*/
struct vsc73xx_portinfo {
u16 pvid_vlan_filtering;
u16 pvid_tag_8021q;
bool pvid_vlan_filtering_configured;
bool pvid_tag_8021q_configured;
};
/**
* struct vsc73xx - VSC73xx state container: main data structure
* @dev: The device pointer
......@@ -25,6 +41,10 @@
* @addr: MAC address used in flow control frames
* @ops: Structure with hardware-dependent operations
* @priv: Pointer to the configuration interface structure
* @portinfo: Storage table portinfo structructures
* @vlans: List of configured vlans. Contains port mask and untagged status of
* every vlan configured in port vlan operation. It doesn't cover tag_8021q
* vlans.
*/
struct vsc73xx {
struct device *dev;
......@@ -35,6 +55,8 @@ struct vsc73xx {
u8 addr[ETH_ALEN];
const struct vsc73xx_ops *ops;
void *priv;
struct vsc73xx_portinfo portinfo[VSC73XX_MAX_NUM_PORTS];
struct list_head vlans;
};
/**
......@@ -49,6 +71,21 @@ struct vsc73xx_ops {
u32 val);
};
/**
* struct vsc73xx_bridge_vlan - VSC73xx driver structure which keeps vlan
* database copy
* @vid: VLAN number
* @portmask: each bit represents one port
* @untagged: each bit represents one port configured with @vid untagged
* @list: list structure
*/
struct vsc73xx_bridge_vlan {
u16 vid;
u8 portmask;
u8 untagged;
struct list_head list;
};
int vsc73xx_is_addr_valid(u8 block, u8 subblock);
int vsc73xx_probe(struct vsc73xx *vsc);
void vsc73xx_remove(struct vsc73xx *vsc);
......
......@@ -8,12 +8,18 @@
#include <net/dsa.h>
#include <linux/types.h>
/* VBID is limited to three bits only and zero is reserved.
* Only 7 bridges can be enumerated.
*/
#define DSA_TAG_8021Q_MAX_NUM_BRIDGES 7
int dsa_tag_8021q_register(struct dsa_switch *ds, __be16 proto);
void dsa_tag_8021q_unregister(struct dsa_switch *ds);
int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, int port,
struct dsa_bridge bridge);
struct dsa_bridge bridge, bool *tx_fwd_offload,
struct netlink_ext_ack *extack);
void dsa_tag_8021q_bridge_leave(struct dsa_switch *ds, int port,
struct dsa_bridge bridge);
......
......@@ -53,6 +53,7 @@ struct tc_action;
#define DSA_TAG_PROTO_RTL8_4T_VALUE 25
#define DSA_TAG_PROTO_RZN1_A5PSW_VALUE 26
#define DSA_TAG_PROTO_LAN937X_VALUE 27
#define DSA_TAG_PROTO_VSC73XX_8021Q_VALUE 28
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
......@@ -83,6 +84,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_RTL8_4T = DSA_TAG_PROTO_RTL8_4T_VALUE,
DSA_TAG_PROTO_RZN1_A5PSW = DSA_TAG_PROTO_RZN1_A5PSW_VALUE,
DSA_TAG_PROTO_LAN937X = DSA_TAG_PROTO_LAN937X_VALUE,
DSA_TAG_PROTO_VSC73XX_8021Q = DSA_TAG_PROTO_VSC73XX_8021Q_VALUE,
};
struct dsa_switch;
......
......@@ -166,6 +166,12 @@ config NET_DSA_TAG_TRAILER
Say Y or M if you want to enable support for tagging frames at
with a trailed. e.g. Marvell 88E6060.
config NET_DSA_TAG_VSC73XX_8021Q
tristate "Tag driver for Microchip/Vitesse VSC73xx family of switches, using VLAN"
help
Say Y or M if you want to enable support for tagging frames with a
custom VLAN-based header.
config NET_DSA_TAG_XRS700X
tristate "Tag driver for XRS700x switches"
help
......
......@@ -37,6 +37,7 @@ obj-$(CONFIG_NET_DSA_TAG_RTL8_4) += tag_rtl8_4.o
obj-$(CONFIG_NET_DSA_TAG_RZN1_A5PSW) += tag_rzn1_a5psw.o
obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o
obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
obj-$(CONFIG_NET_DSA_TAG_VSC73XX_8021Q) += tag_vsc73xx_8021q.o
obj-$(CONFIG_NET_DSA_TAG_XRS700X) += tag_xrs700x.o
# for tracing framework to find trace.h
......
......@@ -286,7 +286,8 @@ int dsa_switch_tag_8021q_vlan_del(struct dsa_switch *ds,
* be used for VLAN-unaware bridging.
*/
int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, int port,
struct dsa_bridge bridge)
struct dsa_bridge bridge, bool *tx_fwd_offload,
struct netlink_ext_ack *extack)
{
struct dsa_port *dp = dsa_to_port(ds, port);
u16 standalone_vid, bridge_vid;
......@@ -304,6 +305,8 @@ int dsa_tag_8021q_bridge_join(struct dsa_switch *ds, int port,
dsa_port_tag_8021q_vlan_del(dp, standalone_vid, false);
*tx_fwd_offload = true;
return 0;
}
EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_join);
......@@ -468,8 +471,8 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
}
EXPORT_SYMBOL_GPL(dsa_8021q_xmit);
struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *conduit,
int vbid)
static struct net_device *
dsa_tag_8021q_find_port_by_vbid(struct net_device *conduit, int vbid)
{
struct dsa_port *cpu_dp = conduit->dsa_ptr;
struct dsa_switch_tree *dst = cpu_dp->dst;
......@@ -495,30 +498,91 @@ struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *conduit,
return NULL;
}
EXPORT_SYMBOL_GPL(dsa_tag_8021q_find_port_by_vbid);
struct net_device *dsa_tag_8021q_find_user(struct net_device *conduit,
int source_port, int switch_id,
int vid, int vbid)
{
/* Always prefer precise source port information, if available */
if (source_port != -1 && switch_id != -1)
return dsa_conduit_find_user(conduit, switch_id, source_port);
else if (vbid >= 1)
return dsa_tag_8021q_find_port_by_vbid(conduit, vbid);
return dsa_find_designated_bridge_port_by_vid(conduit, vid);
}
EXPORT_SYMBOL_GPL(dsa_tag_8021q_find_user);
/**
* dsa_8021q_rcv - Decode source information from tag_8021q header
* @skb: RX socket buffer
* @source_port: pointer to storage for precise source port information.
* If this is known already from outside tag_8021q, the pre-initialized
* value is preserved. If not known, pass -1.
* @switch_id: similar to source_port.
* @vbid: pointer to storage for imprecise bridge ID. Must be pre-initialized
* with -1. If a positive value is returned, the source_port and switch_id
* are invalid.
* @vid: pointer to storage for original VID, in case tag_8021q decoding failed.
*
* If the packet has a tag_8021q header, decode it and set @source_port,
* @switch_id and @vbid, and strip the header. Otherwise set @vid and keep the
* header in the hwaccel area of the packet.
*/
void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id,
int *vbid)
int *vbid, int *vid)
{
u16 vid, tci;
int tmp_source_port, tmp_switch_id, tmp_vbid;
__be16 vlan_proto;
u16 tmp_vid, tci;
if (skb_vlan_tag_present(skb)) {
vlan_proto = skb->vlan_proto;
tci = skb_vlan_tag_get(skb);
__vlan_hwaccel_clear_tag(skb);
} else {
struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
vlan_proto = hdr->h_vlan_proto;
skb_push_rcsum(skb, ETH_HLEN);
__skb_vlan_pop(skb, &tci);
skb_pull_rcsum(skb, ETH_HLEN);
}
vid = tci & VLAN_VID_MASK;
tmp_vid = tci & VLAN_VID_MASK;
if (!vid_is_dsa_8021q(tmp_vid)) {
/* Not a tag_8021q frame, so return the VID to the
* caller for further processing, and put the tag back
*/
if (vid)
*vid = tmp_vid;
*source_port = dsa_8021q_rx_source_port(vid);
*switch_id = dsa_8021q_rx_switch_id(vid);
__vlan_hwaccel_put_tag(skb, vlan_proto, tci);
return;
}
tmp_source_port = dsa_8021q_rx_source_port(tmp_vid);
tmp_switch_id = dsa_8021q_rx_switch_id(tmp_vid);
tmp_vbid = dsa_tag_8021q_rx_vbid(tmp_vid);
/* Precise source port information is unknown when receiving from a
* VLAN-unaware bridging domain, and tmp_source_port and tmp_switch_id
* are zeroes in this case.
*
* Preserve the source information from hardware-specific mechanisms,
* if available. This allows us to not overwrite a valid source port
* and switch ID with less precise values.
*/
if (tmp_vbid == 0 && *source_port == -1)
*source_port = tmp_source_port;
if (tmp_vbid == 0 && *switch_id == -1)
*switch_id = tmp_switch_id;
if (vbid)
*vbid = dsa_tag_8021q_rx_vbid(vid);
*vbid = tmp_vbid;
skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
return;
}
EXPORT_SYMBOL_GPL(dsa_8021q_rcv);
......@@ -14,10 +14,11 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
u16 tpid, u16 tci);
void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id,
int *vbid);
int *vbid, int *vid);
struct net_device *dsa_tag_8021q_find_port_by_vbid(struct net_device *conduit,
int vbid);
struct net_device *dsa_tag_8021q_find_user(struct net_device *conduit,
int source_port, int switch_id,
int vid, int vbid);
int dsa_switch_tag_8021q_vlan_add(struct dsa_switch *ds,
struct dsa_notifier_tag_8021q_vlan_info *info);
......
......@@ -81,7 +81,7 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb,
{
int src_port, switch_id;
dsa_8021q_rcv(skb, &src_port, &switch_id, NULL);
dsa_8021q_rcv(skb, &src_port, &switch_id, NULL, NULL);
skb->dev = dsa_conduit_find_user(netdev, switch_id, src_port);
if (!skb->dev)
......
......@@ -472,37 +472,14 @@ static bool sja1110_skb_has_inband_control_extension(const struct sk_buff *skb)
return ntohs(eth_hdr(skb)->h_proto) == ETH_P_SJA1110;
}
/* If the VLAN in the packet is a tag_8021q one, set @source_port and
* @switch_id and strip the header. Otherwise set @vid and keep it in the
* packet.
*/
static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port,
int *switch_id, int *vbid, u16 *vid)
{
struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
u16 vlan_tci;
if (skb_vlan_tag_present(skb))
vlan_tci = skb_vlan_tag_get(skb);
else
vlan_tci = ntohs(hdr->h_vlan_TCI);
if (vid_is_dsa_8021q(vlan_tci & VLAN_VID_MASK))
return dsa_8021q_rcv(skb, source_port, switch_id, vbid);
/* Try our best with imprecise RX */
*vid = vlan_tci & VLAN_VID_MASK;
}
static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
struct net_device *netdev)
{
int source_port = -1, switch_id = -1, vbid = -1;
int source_port = -1, switch_id = -1, vbid = -1, vid = -1;
struct sja1105_meta meta = {0};
struct ethhdr *hdr;
bool is_link_local;
bool is_meta;
u16 vid;
hdr = eth_hdr(skb);
is_link_local = sja1105_is_link_local(skb);
......@@ -524,37 +501,16 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
/* Normal data plane traffic and link-local frames are tagged with
* a tag_8021q VLAN which we have to strip
*/
if (sja1105_skb_has_tag_8021q(skb)) {
int tmp_source_port = -1, tmp_switch_id = -1;
sja1105_vlan_rcv(skb, &tmp_source_port, &tmp_switch_id, &vbid,
&vid);
/* Preserve the source information from the INCL_SRCPT option,
* if available. This allows us to not overwrite a valid source
* port and switch ID with zeroes when receiving link-local
* frames from a VLAN-unaware bridged port (non-zero vbid) or a
* VLAN-aware bridged port (non-zero vid). Furthermore, the
* tag_8021q source port information is only of trust when the
* vbid is 0 (precise port). Otherwise, tmp_source_port and
* tmp_switch_id will be zeroes.
*/
if (vbid == 0 && source_port == -1)
source_port = tmp_source_port;
if (vbid == 0 && switch_id == -1)
switch_id = tmp_switch_id;
} else if (source_port == -1 && switch_id == -1) {
if (sja1105_skb_has_tag_8021q(skb))
dsa_8021q_rcv(skb, &source_port, &switch_id, &vbid, &vid);
else if (source_port == -1 && switch_id == -1)
/* Packets with no source information have no chance of
* getting accepted, drop them straight away.
*/
return NULL;
}
if (source_port != -1 && switch_id != -1)
skb->dev = dsa_conduit_find_user(netdev, switch_id, source_port);
else if (vbid >= 1)
skb->dev = dsa_tag_8021q_find_port_by_vbid(netdev, vbid);
else
skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid);
skb->dev = dsa_tag_8021q_find_user(netdev, source_port, switch_id,
vid, vbid);
if (!skb->dev) {
netdev_warn(netdev, "Couldn't decode source port\n");
return NULL;
......@@ -677,9 +633,8 @@ static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb,
static struct sk_buff *sja1110_rcv(struct sk_buff *skb,
struct net_device *netdev)
{
int source_port = -1, switch_id = -1, vbid = -1;
int source_port = -1, switch_id = -1, vbid = -1, vid = -1;
bool host_only = false;
u16 vid = 0;
if (sja1110_skb_has_inband_control_extension(skb)) {
skb = sja1110_rcv_inband_control_extension(skb, &source_port,
......@@ -691,14 +646,11 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb,
/* Packets with in-band control extensions might still have RX VLANs */
if (likely(sja1105_skb_has_tag_8021q(skb)))
sja1105_vlan_rcv(skb, &source_port, &switch_id, &vbid, &vid);
if (vbid >= 1)
skb->dev = dsa_tag_8021q_find_port_by_vbid(netdev, vbid);
else if (source_port == -1 || switch_id == -1)
skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid);
else
skb->dev = dsa_conduit_find_user(netdev, switch_id, source_port);
dsa_8021q_rcv(skb, &source_port, &switch_id, &vbid, &vid);
skb->dev = dsa_tag_8021q_find_user(netdev, source_port, switch_id,
vid, vbid);
if (!skb->dev) {
netdev_warn(netdev, "Couldn't decode source port\n");
return NULL;
......
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright (C) 2024 Pawel Dembicki <paweldembicki@gmail.com>
*/
#include <linux/dsa/8021q.h>
#include "tag.h"
#include "tag_8021q.h"
#define VSC73XX_8021Q_NAME "vsc73xx-8021q"
static struct sk_buff *
vsc73xx_xmit(struct sk_buff *skb, struct net_device *netdev)
{
struct dsa_port *dp = dsa_user_to_port(netdev);
u16 queue_mapping = skb_get_queue_mapping(skb);
u16 tx_vid = dsa_tag_8021q_standalone_vid(dp);
u8 pcp;
if (skb->offload_fwd_mark) {
unsigned int bridge_num = dsa_port_bridge_num_get(dp);
struct net_device *br = dsa_port_bridge_dev_get(dp);
if (br_vlan_enabled(br))
return skb;
tx_vid = dsa_tag_8021q_bridge_vid(bridge_num);
}
pcp = netdev_txq_to_tc(netdev, queue_mapping);
return dsa_8021q_xmit(skb, netdev, ETH_P_8021Q,
((pcp << VLAN_PRIO_SHIFT) | tx_vid));
}
static struct sk_buff *
vsc73xx_rcv(struct sk_buff *skb, struct net_device *netdev)
{
int src_port = -1, switch_id = -1, vbid = -1, vid = -1;
dsa_8021q_rcv(skb, &src_port, &switch_id, &vbid, &vid);
skb->dev = dsa_tag_8021q_find_user(netdev, src_port, switch_id,
vid, vbid);
if (!skb->dev) {
dev_warn_ratelimited(&netdev->dev,
"Couldn't decode source port\n");
return NULL;
}
dsa_default_offload_fwd_mark(skb);
return skb;
}
static const struct dsa_device_ops vsc73xx_8021q_netdev_ops = {
.name = VSC73XX_8021Q_NAME,
.proto = DSA_TAG_PROTO_VSC73XX_8021Q,
.xmit = vsc73xx_xmit,
.rcv = vsc73xx_rcv,
.needed_headroom = VLAN_HLEN,
.promisc_on_conduit = true,
};
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DSA tag driver for VSC73XX family of switches, using VLAN");
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_VSC73XX_8021Q, VSC73XX_8021Q_NAME);
module_dsa_tag_driver(vsc73xx_8021q_netdev_ops);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment