2018-05-15 04:04:57 +08:00
|
|
|
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
|
|
|
/*
|
|
|
|
* Microsemi Ocelot Switch driver
|
|
|
|
*
|
|
|
|
* Copyright (c) 2017 Microsemi Corporation
|
|
|
|
*/
|
|
|
|
#include <linux/if_bridge.h>
|
2020-09-30 06:27:26 +08:00
|
|
|
#include <soc/mscc/ocelot_vcap.h>
|
2018-05-15 04:04:57 +08:00
|
|
|
#include "ocelot.h"
|
2020-06-20 23:43:45 +08:00
|
|
|
#include "ocelot_vcap.h"
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2018-12-20 21:16:31 +08:00
|
|
|
#define TABLE_UPDATE_SLEEP_US 10
|
|
|
|
#define TABLE_UPDATE_TIMEOUT_US 100000
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
struct ocelot_mact_entry {
|
|
|
|
u8 mac[ETH_ALEN];
|
|
|
|
u16 vid;
|
|
|
|
enum macaccess_entry_type type;
|
|
|
|
};
|
|
|
|
|
2018-12-20 21:16:31 +08:00
|
|
|
static inline u32 ocelot_mact_read_macaccess(struct ocelot *ocelot)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2018-12-20 21:16:31 +08:00
|
|
|
return ocelot_read(ocelot, ANA_TABLES_MACACCESS);
|
|
|
|
}
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2018-12-20 21:16:31 +08:00
|
|
|
static inline int ocelot_mact_wait_for_completion(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
u32 val;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2018-12-20 21:16:31 +08:00
|
|
|
return readx_poll_timeout(ocelot_mact_read_macaccess,
|
|
|
|
ocelot, val,
|
|
|
|
(val & ANA_TABLES_MACACCESS_MAC_TABLE_CMD_M) ==
|
|
|
|
MACACCESS_CMD_IDLE,
|
|
|
|
TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ocelot_mact_select(struct ocelot *ocelot,
|
|
|
|
const unsigned char mac[ETH_ALEN],
|
|
|
|
unsigned int vid)
|
|
|
|
{
|
|
|
|
u32 macl = 0, mach = 0;
|
|
|
|
|
|
|
|
/* Set the MAC address to handle and the vlan associated in a format
|
|
|
|
* understood by the hardware.
|
|
|
|
*/
|
|
|
|
mach |= vid << 16;
|
|
|
|
mach |= mac[0] << 8;
|
|
|
|
mach |= mac[1] << 0;
|
|
|
|
macl |= mac[2] << 24;
|
|
|
|
macl |= mac[3] << 16;
|
|
|
|
macl |= mac[4] << 8;
|
|
|
|
macl |= mac[5] << 0;
|
|
|
|
|
|
|
|
ocelot_write(ocelot, macl, ANA_TABLES_MACLDATA);
|
|
|
|
ocelot_write(ocelot, mach, ANA_TABLES_MACHDATA);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-06-20 23:43:44 +08:00
|
|
|
int ocelot_mact_learn(struct ocelot *ocelot, int port,
|
|
|
|
const unsigned char mac[ETH_ALEN],
|
|
|
|
unsigned int vid, enum macaccess_entry_type type)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
ocelot_mact_select(ocelot, mac, vid);
|
|
|
|
|
|
|
|
/* Issue a write command */
|
|
|
|
ocelot_write(ocelot, ANA_TABLES_MACACCESS_VALID |
|
|
|
|
ANA_TABLES_MACACCESS_DEST_IDX(port) |
|
|
|
|
ANA_TABLES_MACACCESS_ENTRYTYPE(type) |
|
|
|
|
ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_LEARN),
|
|
|
|
ANA_TABLES_MACACCESS);
|
|
|
|
|
|
|
|
return ocelot_mact_wait_for_completion(ocelot);
|
|
|
|
}
|
2020-06-20 23:43:44 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_mact_learn);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2020-06-20 23:43:44 +08:00
|
|
|
int ocelot_mact_forget(struct ocelot *ocelot,
|
|
|
|
const unsigned char mac[ETH_ALEN], unsigned int vid)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
ocelot_mact_select(ocelot, mac, vid);
|
|
|
|
|
|
|
|
/* Issue a forget command */
|
|
|
|
ocelot_write(ocelot,
|
|
|
|
ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_FORGET),
|
|
|
|
ANA_TABLES_MACACCESS);
|
|
|
|
|
|
|
|
return ocelot_mact_wait_for_completion(ocelot);
|
|
|
|
}
|
2020-06-20 23:43:44 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_mact_forget);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
static void ocelot_mact_init(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
/* Configure the learning mode entries attributes:
|
|
|
|
* - Do not copy the frame to the CPU extraction queues.
|
|
|
|
* - Use the vlan and mac_cpoy for dmac lookup.
|
|
|
|
*/
|
|
|
|
ocelot_rmw(ocelot, 0,
|
|
|
|
ANA_AGENCTRL_LEARN_CPU_COPY | ANA_AGENCTRL_IGNORE_DMAC_FLAGS
|
|
|
|
| ANA_AGENCTRL_LEARN_FWD_KILL
|
|
|
|
| ANA_AGENCTRL_LEARN_IGNORE_VLAN,
|
|
|
|
ANA_AGENCTRL);
|
|
|
|
|
|
|
|
/* Clear the MAC table */
|
|
|
|
ocelot_write(ocelot, MACACCESS_CMD_INIT, ANA_TABLES_MACACCESS);
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:52 +08:00
|
|
|
static void ocelot_vcap_enable(struct ocelot *ocelot, int port)
|
2019-05-31 15:16:56 +08:00
|
|
|
{
|
|
|
|
ocelot_write_gix(ocelot, ANA_PORT_VCAP_S2_CFG_S2_ENA |
|
|
|
|
ANA_PORT_VCAP_S2_CFG_S2_IP6_CFG(0xa),
|
2019-11-09 21:02:52 +08:00
|
|
|
ANA_PORT_VCAP_S2_CFG, port);
|
net: mscc: ocelot: offload ingress skbedit and vlan actions to VCAP IS1
VCAP IS1 is a VCAP module which can filter on the most common L2/L3/L4
Ethernet keys, and modify the results of the basic QoS classification
and VLAN classification based on those flow keys.
There are 3 VCAP IS1 lookups, mapped over chains 10000, 11000 and 12000.
Currently the driver is hardcoded to use IS1_ACTION_TYPE_NORMAL half
keys.
Note that the VLAN_MANGLE has been omitted for now. In hardware, the
VCAP_IS1_ACT_VID_REPLACE_ENA field replaces the classified VLAN
(metadata associated with the frame) and not the VLAN from the header
itself. There are currently some issues which need to be addressed when
operating in standalone, or in bridge with vlan_filtering=0 modes,
because in those cases the switch ports have VLAN awareness disabled,
and changing the classified VLAN to anything other than the pvid causes
the packets to be dropped. Another issue is that on egress, we expect
port tagging to push the classified VLAN, but port tagging is disabled
in the modes mentioned above, so although the classified VLAN is
replaced, it is not visible in the packet transmitted by the switch.
Signed-off-by: Xiaoliang Yang <xiaoliang.yang_1@nxp.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-10-02 20:02:23 +08:00
|
|
|
|
|
|
|
ocelot_write_gix(ocelot, ANA_PORT_VCAP_CFG_S1_ENA,
|
|
|
|
ANA_PORT_VCAP_CFG, port);
|
2020-10-02 20:02:24 +08:00
|
|
|
|
|
|
|
ocelot_rmw_gix(ocelot, REW_PORT_CFG_ES0_EN,
|
|
|
|
REW_PORT_CFG_ES0_EN,
|
|
|
|
REW_PORT_CFG, port);
|
2019-05-31 15:16:56 +08:00
|
|
|
}
|
|
|
|
|
2018-12-20 21:16:31 +08:00
|
|
|
static inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2018-12-20 21:16:31 +08:00
|
|
|
return ocelot_read(ocelot, ANA_TABLES_VLANACCESS);
|
|
|
|
}
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2018-12-20 21:16:31 +08:00
|
|
|
static inline int ocelot_vlant_wait_for_completion(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
return readx_poll_timeout(ocelot_vlant_read_vlanaccess,
|
|
|
|
ocelot,
|
|
|
|
val,
|
|
|
|
(val & ANA_TABLES_VLANACCESS_VLAN_TBL_CMD_M) ==
|
|
|
|
ANA_TABLES_VLANACCESS_CMD_IDLE,
|
|
|
|
TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
|
2018-06-26 20:28:49 +08:00
|
|
|
static int ocelot_vlant_set_mask(struct ocelot *ocelot, u16 vid, u32 mask)
|
|
|
|
{
|
|
|
|
/* Select the VID to configure */
|
|
|
|
ocelot_write(ocelot, ANA_TABLES_VLANTIDX_V_INDEX(vid),
|
|
|
|
ANA_TABLES_VLANTIDX);
|
|
|
|
/* Set the vlan port members mask and issue a write command */
|
|
|
|
ocelot_write(ocelot, ANA_TABLES_VLANACCESS_VLAN_PORT_MASK(mask) |
|
|
|
|
ANA_TABLES_VLANACCESS_CMD_WRITE,
|
|
|
|
ANA_TABLES_VLANACCESS);
|
|
|
|
|
|
|
|
return ocelot_vlant_wait_for_completion(ocelot);
|
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port,
|
2020-10-31 18:29:12 +08:00
|
|
|
struct ocelot_vlan native_vlan)
|
2018-06-26 20:28:49 +08:00
|
|
|
{
|
2019-11-09 21:02:47 +08:00
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
u32 val = 0;
|
2018-06-26 20:28:49 +08:00
|
|
|
|
2020-10-31 18:29:12 +08:00
|
|
|
if (ocelot_port->native_vlan.vid != native_vlan.vid) {
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
/* Always permit deleting the native VLAN (vid = 0) */
|
2020-10-31 18:29:12 +08:00
|
|
|
if (ocelot_port->native_vlan.vid && native_vlan.vid) {
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
dev_err(ocelot->dev,
|
|
|
|
"Port already has a native VLAN: %d\n",
|
2020-10-31 18:29:12 +08:00
|
|
|
ocelot_port->native_vlan.vid);
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
2020-10-31 18:29:12 +08:00
|
|
|
ocelot_port->native_vlan = native_vlan;
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
}
|
|
|
|
|
2020-10-31 18:29:12 +08:00
|
|
|
ocelot_rmw_gix(ocelot, REW_PORT_VLAN_CFG_PORT_VID(native_vlan.vid),
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
REW_PORT_VLAN_CFG_PORT_VID_M,
|
|
|
|
REW_PORT_VLAN_CFG, port);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
2020-10-31 18:29:12 +08:00
|
|
|
if (ocelot_port->vlan_aware && !ocelot_port->native_vlan.vid)
|
2018-06-26 20:28:49 +08:00
|
|
|
/* If port is vlan-aware and tagged, drop untagged and priority
|
|
|
|
* tagged frames.
|
|
|
|
*/
|
2019-11-09 21:02:47 +08:00
|
|
|
val = ANA_PORT_DROP_CFG_DROP_UNTAGGED_ENA |
|
|
|
|
ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
|
|
|
|
ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA;
|
|
|
|
ocelot_rmw_gix(ocelot, val,
|
|
|
|
ANA_PORT_DROP_CFG_DROP_UNTAGGED_ENA |
|
2018-06-26 20:28:49 +08:00
|
|
|
ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
|
2019-11-09 21:02:47 +08:00
|
|
|
ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA,
|
|
|
|
ANA_PORT_DROP_CFG, port);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
if (ocelot_port->vlan_aware) {
|
2020-10-31 18:29:12 +08:00
|
|
|
if (ocelot_port->native_vlan.vid)
|
2018-06-26 20:28:49 +08:00
|
|
|
/* Tag all frames except when VID == DEFAULT_VLAN */
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
val = REW_TAG_CFG_TAG_CFG(1);
|
2018-06-26 20:28:49 +08:00
|
|
|
else
|
|
|
|
/* Tag all frames */
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
val = REW_TAG_CFG_TAG_CFG(3);
|
2019-11-09 21:02:47 +08:00
|
|
|
} else {
|
|
|
|
/* Port tagging disabled. */
|
|
|
|
val = REW_TAG_CFG_TAG_CFG(0);
|
2018-06-26 20:28:49 +08:00
|
|
|
}
|
|
|
|
ocelot_rmw_gix(ocelot, val,
|
|
|
|
REW_TAG_CFG_TAG_CFG_M,
|
2019-11-09 21:02:47 +08:00
|
|
|
REW_TAG_CFG, port);
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
|
|
|
|
return 0;
|
2019-11-09 21:02:47 +08:00
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: use the pvid of zero when bridged with vlan_filtering=0
Currently, mscc_ocelot ports configure pvid=0 in standalone mode, and
inherit the pvid from the bridge when one is present.
When the bridge has vlan_filtering=0, the software semantics are that
packets should be received regardless of whether there's a pvid
configured on the ingress port or not. However, ocelot does not observe
those semantics today.
Moreover, changing the PVID is also a problem with vlan_filtering=0.
We are privately remapping the VID of FDB, MDB entries to the port's
PVID when those are VLAN-unaware (i.e. when the VID of these entries
comes to us as 0). But we have no logic of adjusting that remapping when
the user changes the pvid and vlan_filtering is 0. So stale entries
would be left behind, and untagged traffic will stop matching on them.
And even if we were to solve that, there's an even bigger problem. If
swp0 has pvid 1, and swp1 has pvid 2, and both are under a vlan_filtering=0
bridge, they should be able to forward traffic between one another.
However, with ocelot they wouldn't do that.
The simplest way of fixing this is to never configure the pvid based on
what the bridge is asking for, when vlan_filtering is 0. Only if there
was a VLAN that the bridge couldn't mangle, that we could use as pvid....
So, turns out, there's 0 just for that. And for a reason: IEEE
802.1Q-2018, page 247, Table 9-2-Reserved VID values says:
The null VID. Indicates that the tag header contains only
priority information; no VID is present in the frame.
This VID value shall not be configured as a PVID or a member
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
of a VID Set, or configured in any FDB entry, or used in any
Management operation.
So, aren't we doing exactly what 802.1Q says not to? Well, in a way, but
what we're doing here is just driver-level bookkeeping, all for the
better. The fact that we're using a pvid of 0 is not observable behavior
from the outside world: the network stack does not see the classified
VLAN that the switch uses, in vlan_filtering=0 mode. And we're also more
consistent with the standalone mode now.
And now that we use the pvid of 0 in this mode, there's another advantage:
we don't need to perform any VID remapping for FDB and MDB entries either,
we can just use the VID of 0 that the bridge is passing to us.
The only gotcha is that every time we change the vlan_filtering setting,
we need to reapply the pvid (either to 0, or to the value from the bridge).
A small side-effect visible in the patch is that ocelot_port_set_pvid
needs to be moved above ocelot_port_vlan_filtering, so that it can be
called from there without forward-declarations.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-31 18:29:10 +08:00
|
|
|
/* Default vlan to clasify for untagged frames (may be zero) */
|
2020-10-31 18:29:12 +08:00
|
|
|
static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
|
|
|
|
struct ocelot_vlan pvid_vlan)
|
net: mscc: ocelot: use the pvid of zero when bridged with vlan_filtering=0
Currently, mscc_ocelot ports configure pvid=0 in standalone mode, and
inherit the pvid from the bridge when one is present.
When the bridge has vlan_filtering=0, the software semantics are that
packets should be received regardless of whether there's a pvid
configured on the ingress port or not. However, ocelot does not observe
those semantics today.
Moreover, changing the PVID is also a problem with vlan_filtering=0.
We are privately remapping the VID of FDB, MDB entries to the port's
PVID when those are VLAN-unaware (i.e. when the VID of these entries
comes to us as 0). But we have no logic of adjusting that remapping when
the user changes the pvid and vlan_filtering is 0. So stale entries
would be left behind, and untagged traffic will stop matching on them.
And even if we were to solve that, there's an even bigger problem. If
swp0 has pvid 1, and swp1 has pvid 2, and both are under a vlan_filtering=0
bridge, they should be able to forward traffic between one another.
However, with ocelot they wouldn't do that.
The simplest way of fixing this is to never configure the pvid based on
what the bridge is asking for, when vlan_filtering is 0. Only if there
was a VLAN that the bridge couldn't mangle, that we could use as pvid....
So, turns out, there's 0 just for that. And for a reason: IEEE
802.1Q-2018, page 247, Table 9-2-Reserved VID values says:
The null VID. Indicates that the tag header contains only
priority information; no VID is present in the frame.
This VID value shall not be configured as a PVID or a member
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
of a VID Set, or configured in any FDB entry, or used in any
Management operation.
So, aren't we doing exactly what 802.1Q says not to? Well, in a way, but
what we're doing here is just driver-level bookkeeping, all for the
better. The fact that we're using a pvid of 0 is not observable behavior
from the outside world: the network stack does not see the classified
VLAN that the switch uses, in vlan_filtering=0 mode. And we're also more
consistent with the standalone mode now.
And now that we use the pvid of 0 in this mode, there's another advantage:
we don't need to perform any VID remapping for FDB and MDB entries either,
we can just use the VID of 0 that the bridge is passing to us.
The only gotcha is that every time we change the vlan_filtering setting,
we need to reapply the pvid (either to 0, or to the value from the bridge).
A small side-effect visible in the patch is that ocelot_port_set_pvid
needs to be moved above ocelot_port_vlan_filtering, so that it can be
called from there without forward-declarations.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-31 18:29:10 +08:00
|
|
|
{
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
|
|
|
2020-10-31 18:29:12 +08:00
|
|
|
ocelot_port->pvid_vlan = pvid_vlan;
|
net: mscc: ocelot: use the pvid of zero when bridged with vlan_filtering=0
Currently, mscc_ocelot ports configure pvid=0 in standalone mode, and
inherit the pvid from the bridge when one is present.
When the bridge has vlan_filtering=0, the software semantics are that
packets should be received regardless of whether there's a pvid
configured on the ingress port or not. However, ocelot does not observe
those semantics today.
Moreover, changing the PVID is also a problem with vlan_filtering=0.
We are privately remapping the VID of FDB, MDB entries to the port's
PVID when those are VLAN-unaware (i.e. when the VID of these entries
comes to us as 0). But we have no logic of adjusting that remapping when
the user changes the pvid and vlan_filtering is 0. So stale entries
would be left behind, and untagged traffic will stop matching on them.
And even if we were to solve that, there's an even bigger problem. If
swp0 has pvid 1, and swp1 has pvid 2, and both are under a vlan_filtering=0
bridge, they should be able to forward traffic between one another.
However, with ocelot they wouldn't do that.
The simplest way of fixing this is to never configure the pvid based on
what the bridge is asking for, when vlan_filtering is 0. Only if there
was a VLAN that the bridge couldn't mangle, that we could use as pvid....
So, turns out, there's 0 just for that. And for a reason: IEEE
802.1Q-2018, page 247, Table 9-2-Reserved VID values says:
The null VID. Indicates that the tag header contains only
priority information; no VID is present in the frame.
This VID value shall not be configured as a PVID or a member
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
of a VID Set, or configured in any FDB entry, or used in any
Management operation.
So, aren't we doing exactly what 802.1Q says not to? Well, in a way, but
what we're doing here is just driver-level bookkeeping, all for the
better. The fact that we're using a pvid of 0 is not observable behavior
from the outside world: the network stack does not see the classified
VLAN that the switch uses, in vlan_filtering=0 mode. And we're also more
consistent with the standalone mode now.
And now that we use the pvid of 0 in this mode, there's another advantage:
we don't need to perform any VID remapping for FDB and MDB entries either,
we can just use the VID of 0 that the bridge is passing to us.
The only gotcha is that every time we change the vlan_filtering setting,
we need to reapply the pvid (either to 0, or to the value from the bridge).
A small side-effect visible in the patch is that ocelot_port_set_pvid
needs to be moved above ocelot_port_vlan_filtering, so that it can be
called from there without forward-declarations.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-31 18:29:10 +08:00
|
|
|
|
|
|
|
if (!ocelot_port->vlan_aware)
|
2020-10-31 18:29:12 +08:00
|
|
|
pvid_vlan.vid = 0;
|
net: mscc: ocelot: use the pvid of zero when bridged with vlan_filtering=0
Currently, mscc_ocelot ports configure pvid=0 in standalone mode, and
inherit the pvid from the bridge when one is present.
When the bridge has vlan_filtering=0, the software semantics are that
packets should be received regardless of whether there's a pvid
configured on the ingress port or not. However, ocelot does not observe
those semantics today.
Moreover, changing the PVID is also a problem with vlan_filtering=0.
We are privately remapping the VID of FDB, MDB entries to the port's
PVID when those are VLAN-unaware (i.e. when the VID of these entries
comes to us as 0). But we have no logic of adjusting that remapping when
the user changes the pvid and vlan_filtering is 0. So stale entries
would be left behind, and untagged traffic will stop matching on them.
And even if we were to solve that, there's an even bigger problem. If
swp0 has pvid 1, and swp1 has pvid 2, and both are under a vlan_filtering=0
bridge, they should be able to forward traffic between one another.
However, with ocelot they wouldn't do that.
The simplest way of fixing this is to never configure the pvid based on
what the bridge is asking for, when vlan_filtering is 0. Only if there
was a VLAN that the bridge couldn't mangle, that we could use as pvid....
So, turns out, there's 0 just for that. And for a reason: IEEE
802.1Q-2018, page 247, Table 9-2-Reserved VID values says:
The null VID. Indicates that the tag header contains only
priority information; no VID is present in the frame.
This VID value shall not be configured as a PVID or a member
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
of a VID Set, or configured in any FDB entry, or used in any
Management operation.
So, aren't we doing exactly what 802.1Q says not to? Well, in a way, but
what we're doing here is just driver-level bookkeeping, all for the
better. The fact that we're using a pvid of 0 is not observable behavior
from the outside world: the network stack does not see the classified
VLAN that the switch uses, in vlan_filtering=0 mode. And we're also more
consistent with the standalone mode now.
And now that we use the pvid of 0 in this mode, there's another advantage:
we don't need to perform any VID remapping for FDB and MDB entries either,
we can just use the VID of 0 that the bridge is passing to us.
The only gotcha is that every time we change the vlan_filtering setting,
we need to reapply the pvid (either to 0, or to the value from the bridge).
A small side-effect visible in the patch is that ocelot_port_set_pvid
needs to be moved above ocelot_port_vlan_filtering, so that it can be
called from there without forward-declarations.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-31 18:29:10 +08:00
|
|
|
|
|
|
|
ocelot_rmw_gix(ocelot,
|
2020-10-31 18:29:12 +08:00
|
|
|
ANA_PORT_VLAN_CFG_VLAN_VID(pvid_vlan.vid),
|
net: mscc: ocelot: use the pvid of zero when bridged with vlan_filtering=0
Currently, mscc_ocelot ports configure pvid=0 in standalone mode, and
inherit the pvid from the bridge when one is present.
When the bridge has vlan_filtering=0, the software semantics are that
packets should be received regardless of whether there's a pvid
configured on the ingress port or not. However, ocelot does not observe
those semantics today.
Moreover, changing the PVID is also a problem with vlan_filtering=0.
We are privately remapping the VID of FDB, MDB entries to the port's
PVID when those are VLAN-unaware (i.e. when the VID of these entries
comes to us as 0). But we have no logic of adjusting that remapping when
the user changes the pvid and vlan_filtering is 0. So stale entries
would be left behind, and untagged traffic will stop matching on them.
And even if we were to solve that, there's an even bigger problem. If
swp0 has pvid 1, and swp1 has pvid 2, and both are under a vlan_filtering=0
bridge, they should be able to forward traffic between one another.
However, with ocelot they wouldn't do that.
The simplest way of fixing this is to never configure the pvid based on
what the bridge is asking for, when vlan_filtering is 0. Only if there
was a VLAN that the bridge couldn't mangle, that we could use as pvid....
So, turns out, there's 0 just for that. And for a reason: IEEE
802.1Q-2018, page 247, Table 9-2-Reserved VID values says:
The null VID. Indicates that the tag header contains only
priority information; no VID is present in the frame.
This VID value shall not be configured as a PVID or a member
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
of a VID Set, or configured in any FDB entry, or used in any
Management operation.
So, aren't we doing exactly what 802.1Q says not to? Well, in a way, but
what we're doing here is just driver-level bookkeeping, all for the
better. The fact that we're using a pvid of 0 is not observable behavior
from the outside world: the network stack does not see the classified
VLAN that the switch uses, in vlan_filtering=0 mode. And we're also more
consistent with the standalone mode now.
And now that we use the pvid of 0 in this mode, there's another advantage:
we don't need to perform any VID remapping for FDB and MDB entries either,
we can just use the VID of 0 that the bridge is passing to us.
The only gotcha is that every time we change the vlan_filtering setting,
we need to reapply the pvid (either to 0, or to the value from the bridge).
A small side-effect visible in the patch is that ocelot_port_set_pvid
needs to be moved above ocelot_port_vlan_filtering, so that it can be
called from there without forward-declarations.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-31 18:29:10 +08:00
|
|
|
ANA_PORT_VLAN_CFG_VLAN_VID_M,
|
|
|
|
ANA_PORT_VLAN_CFG, port);
|
|
|
|
}
|
|
|
|
|
2020-10-03 06:06:46 +08:00
|
|
|
int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
|
|
|
|
bool vlan_aware, struct switchdev_trans *trans)
|
2019-11-09 21:02:47 +08:00
|
|
|
{
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
u32 val;
|
2019-11-09 21:02:47 +08:00
|
|
|
|
net: mscc: ocelot: offload VLAN mangle action to VCAP IS1
The VCAP_IS1_ACT_VID_REPLACE_ENA action, from the VCAP IS1 ingress TCAM,
changes the classified VLAN.
We are only exposing this ability for switch ports that are under VLAN
aware bridges. This is because in standalone ports mode and under a
bridge with vlan_filtering=0, the ocelot driver configures the switch to
operate as VLAN-unaware, so the classified VLAN is not derived from the
802.1Q header from the packet, but instead is always equal to the
port-based VLAN ID of the ingress port. We _can_ still change the
classified VLAN for packets when operating in this mode, but the end
result will most likely be a drop, since both the ingress and the egress
port need to be members of the modified VLAN. And even if we install the
new classified VLAN into the VLAN table of the switch, the result would
still not be as expected: we wouldn't see, on the output port, the
modified VLAN tag, but the original one, even though the classified VLAN
was indeed modified. This is because of how the hardware works: on
egress, what is pushed to the frame is a "port tag", which gives us the
following options:
- Tag all frames with port tag (derived from the classified VLAN)
- Tag all frames with port tag, except if the classified VLAN is 0 or
equal to the native VLAN of the egress port
- No port tag
Needless to say, in VLAN-unaware mode we are disabling the port tag.
Otherwise, the existing VLAN tag would be ignored, and a second VLAN
tag (the port tag), holding the classified VLAN, would be pushed
(instead of replacing the existing 802.1Q tag). This is definitely not
what the user wanted when installing a "vlan modify" action.
So it is simply not worth bothering with VLAN modify rules under other
configurations except when the ports are fully VLAN-aware.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-08 19:56:58 +08:00
|
|
|
if (switchdev_trans_ph_prepare(trans)) {
|
|
|
|
struct ocelot_vcap_block *block = &ocelot->block[VCAP_IS1];
|
|
|
|
struct ocelot_vcap_filter *filter;
|
|
|
|
|
|
|
|
list_for_each_entry(filter, &block->rules, list) {
|
|
|
|
if (filter->ingress_port_mask & BIT(port) &&
|
|
|
|
filter->action.vid_replace_ena) {
|
|
|
|
dev_err(ocelot->dev,
|
|
|
|
"Cannot change VLAN state with vlan modify rules active\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-03 06:06:46 +08:00
|
|
|
return 0;
|
net: mscc: ocelot: offload VLAN mangle action to VCAP IS1
The VCAP_IS1_ACT_VID_REPLACE_ENA action, from the VCAP IS1 ingress TCAM,
changes the classified VLAN.
We are only exposing this ability for switch ports that are under VLAN
aware bridges. This is because in standalone ports mode and under a
bridge with vlan_filtering=0, the ocelot driver configures the switch to
operate as VLAN-unaware, so the classified VLAN is not derived from the
802.1Q header from the packet, but instead is always equal to the
port-based VLAN ID of the ingress port. We _can_ still change the
classified VLAN for packets when operating in this mode, but the end
result will most likely be a drop, since both the ingress and the egress
port need to be members of the modified VLAN. And even if we install the
new classified VLAN into the VLAN table of the switch, the result would
still not be as expected: we wouldn't see, on the output port, the
modified VLAN tag, but the original one, even though the classified VLAN
was indeed modified. This is because of how the hardware works: on
egress, what is pushed to the frame is a "port tag", which gives us the
following options:
- Tag all frames with port tag (derived from the classified VLAN)
- Tag all frames with port tag, except if the classified VLAN is 0 or
equal to the native VLAN of the egress port
- No port tag
Needless to say, in VLAN-unaware mode we are disabling the port tag.
Otherwise, the existing VLAN tag would be ignored, and a second VLAN
tag (the port tag), holding the classified VLAN, would be pushed
(instead of replacing the existing 802.1Q tag). This is definitely not
what the user wanted when installing a "vlan modify" action.
So it is simply not worth bothering with VLAN modify rules under other
configurations except when the ports are fully VLAN-aware.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-08 19:56:58 +08:00
|
|
|
}
|
2020-10-03 06:06:46 +08:00
|
|
|
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
ocelot_port->vlan_aware = vlan_aware;
|
2019-11-09 21:02:47 +08:00
|
|
|
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
if (vlan_aware)
|
|
|
|
val = ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1);
|
|
|
|
else
|
|
|
|
val = 0;
|
|
|
|
ocelot_rmw_gix(ocelot, val,
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M,
|
|
|
|
ANA_PORT_VLAN_CFG, port);
|
2019-11-09 21:02:47 +08:00
|
|
|
|
2020-10-31 18:29:12 +08:00
|
|
|
ocelot_port_set_pvid(ocelot, port, ocelot_port->pvid_vlan);
|
|
|
|
ocelot_port_set_native_vlan(ocelot, port, ocelot_port->native_vlan);
|
2020-10-03 06:06:46 +08:00
|
|
|
|
|
|
|
return 0;
|
2019-11-09 21:02:47 +08:00
|
|
|
}
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_vlan_filtering);
|
2019-11-09 21:02:47 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
|
|
|
|
bool untagged)
|
2018-06-26 20:28:49 +08:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Make the port a member of the VLAN */
|
2019-11-09 21:02:47 +08:00
|
|
|
ocelot->vlan_mask[vid] |= BIT(port);
|
2018-06-26 20:28:49 +08:00
|
|
|
ret = ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Default ingress vlan classification */
|
2020-10-31 18:29:12 +08:00
|
|
|
if (pvid) {
|
|
|
|
struct ocelot_vlan pvid_vlan;
|
|
|
|
|
|
|
|
pvid_vlan.vid = vid;
|
|
|
|
ocelot_port_set_pvid(ocelot, port, pvid_vlan);
|
|
|
|
}
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
/* Untagged egress vlan clasification */
|
2019-11-09 21:02:47 +08:00
|
|
|
if (untagged) {
|
2020-10-31 18:29:12 +08:00
|
|
|
struct ocelot_vlan native_vlan;
|
|
|
|
|
|
|
|
native_vlan.vid = vid;
|
|
|
|
ret = ocelot_port_set_native_vlan(ocelot, port, native_vlan);
|
2019-11-09 21:02:47 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2019-10-27 02:04:27 +08:00
|
|
|
}
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_vlan_add);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
|
2019-11-09 21:02:48 +08:00
|
|
|
{
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
|
|
int ret;
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
/* Stop the port from being a member of the vlan */
|
2019-11-09 21:02:47 +08:00
|
|
|
ocelot->vlan_mask[vid] &= ~BIT(port);
|
2018-06-26 20:28:49 +08:00
|
|
|
ret = ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Egress */
|
2020-10-31 18:29:12 +08:00
|
|
|
if (ocelot_port->native_vlan.vid == vid) {
|
|
|
|
struct ocelot_vlan native_vlan;
|
|
|
|
|
|
|
|
native_vlan.vid = 0;
|
|
|
|
ocelot_port_set_native_vlan(ocelot, port, native_vlan);
|
|
|
|
}
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_vlan_del);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
static void ocelot_vlan_init(struct ocelot *ocelot)
|
|
|
|
{
|
2018-06-26 20:28:49 +08:00
|
|
|
u16 port, vid;
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
/* Clear VLAN table, by default all ports are members of all VLANs */
|
|
|
|
ocelot_write(ocelot, ANA_TABLES_VLANACCESS_CMD_INIT,
|
|
|
|
ANA_TABLES_VLANACCESS);
|
|
|
|
ocelot_vlant_wait_for_completion(ocelot);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
/* Configure the port VLAN memberships */
|
|
|
|
for (vid = 1; vid < VLAN_N_VID; vid++) {
|
|
|
|
ocelot->vlan_mask[vid] = 0;
|
|
|
|
ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Because VLAN filtering is enabled, we need VID 0 to get untagged
|
|
|
|
* traffic. It is added automatically if 8021q module is loaded, but
|
|
|
|
* we can't rely on it since module may be not loaded.
|
|
|
|
*/
|
|
|
|
ocelot->vlan_mask[0] = GENMASK(ocelot->num_phys_ports - 1, 0);
|
|
|
|
ocelot_vlant_set_mask(ocelot, 0, ocelot->vlan_mask[0]);
|
|
|
|
|
|
|
|
/* Set vlan ingress filter mask to all ports but the CPU port by
|
|
|
|
* default.
|
|
|
|
*/
|
2019-11-09 21:02:55 +08:00
|
|
|
ocelot_write(ocelot, GENMASK(ocelot->num_phys_ports - 1, 0),
|
|
|
|
ANA_VLANMASK);
|
2018-06-26 20:28:49 +08:00
|
|
|
|
|
|
|
for (port = 0; port < ocelot->num_phys_ports; port++) {
|
|
|
|
ocelot_write_gix(ocelot, 0, REW_PORT_VLAN_CFG, port);
|
|
|
|
ocelot_write_gix(ocelot, 0, REW_TAG_CFG, port);
|
|
|
|
}
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_adjust_link(struct ocelot *ocelot, int port,
|
|
|
|
struct phy_device *phydev)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2019-11-09 21:02:59 +08:00
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
2019-11-14 23:03:22 +08:00
|
|
|
int speed, mode = 0;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:59 +08:00
|
|
|
switch (phydev->speed) {
|
2018-05-15 04:04:57 +08:00
|
|
|
case SPEED_10:
|
|
|
|
speed = OCELOT_SPEED_10;
|
|
|
|
break;
|
|
|
|
case SPEED_100:
|
|
|
|
speed = OCELOT_SPEED_100;
|
|
|
|
break;
|
|
|
|
case SPEED_1000:
|
|
|
|
speed = OCELOT_SPEED_1000;
|
|
|
|
mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
|
|
|
|
break;
|
|
|
|
case SPEED_2500:
|
|
|
|
speed = OCELOT_SPEED_2500;
|
|
|
|
mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
|
|
|
|
break;
|
|
|
|
default:
|
2019-11-09 21:02:59 +08:00
|
|
|
dev_err(ocelot->dev, "Unsupported PHY speed on port %d: %d\n",
|
|
|
|
port, phydev->speed);
|
2018-05-15 04:04:57 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:59 +08:00
|
|
|
phy_print_status(phydev);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:59 +08:00
|
|
|
if (!phydev->link)
|
2018-05-15 04:04:57 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* Only full duplex supported for now */
|
2019-11-09 21:02:53 +08:00
|
|
|
ocelot_port_writel(ocelot_port, DEV_MAC_MODE_CFG_FDX_ENA |
|
2018-05-15 04:04:57 +08:00
|
|
|
mode, DEV_MAC_MODE_CFG);
|
|
|
|
|
2020-02-29 22:31:11 +08:00
|
|
|
/* Disable HDX fast control */
|
|
|
|
ocelot_port_writel(ocelot_port, DEV_PORT_MISC_HDX_FAST_DIS,
|
|
|
|
DEV_PORT_MISC);
|
|
|
|
|
|
|
|
/* SGMII only for now */
|
|
|
|
ocelot_port_writel(ocelot_port, PCS1G_MODE_CFG_SGMII_MODE_ENA,
|
|
|
|
PCS1G_MODE_CFG);
|
|
|
|
ocelot_port_writel(ocelot_port, PCS1G_SD_CFG_SD_SEL, PCS1G_SD_CFG);
|
|
|
|
|
|
|
|
/* Enable PCS */
|
|
|
|
ocelot_port_writel(ocelot_port, PCS1G_CFG_PCS_ENA, PCS1G_CFG);
|
|
|
|
|
|
|
|
/* No aneg on SGMII */
|
|
|
|
ocelot_port_writel(ocelot_port, 0, PCS1G_ANEG_CFG);
|
|
|
|
|
|
|
|
/* No loopback */
|
|
|
|
ocelot_port_writel(ocelot_port, 0, PCS1G_LB_CFG);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Enable MAC module */
|
2019-11-09 21:02:53 +08:00
|
|
|
ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA |
|
2018-05-15 04:04:57 +08:00
|
|
|
DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG);
|
|
|
|
|
|
|
|
/* Take MAC, Port, Phy (intern) and PCS (SGMII/Serdes) clock out of
|
|
|
|
* reset */
|
2019-11-09 21:02:53 +08:00
|
|
|
ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(speed),
|
2018-05-15 04:04:57 +08:00
|
|
|
DEV_CLOCK_CFG);
|
|
|
|
|
|
|
|
/* No PFC */
|
|
|
|
ocelot_write_gix(ocelot, ANA_PFC_PFC_CFG_FC_LINK_SPEED(speed),
|
2019-11-09 21:02:53 +08:00
|
|
|
ANA_PFC_PFC_CFG, port);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Core: Enable port for frame transfer */
|
net: mscc: ocelot: convert QSYS_SWITCH_PORT_MODE and SYS_PORT_MODE to regfields
Currently Felix and Ocelot share the same bit layout in these per-port
registers, but Seville does not. So we need reg_fields for that.
Actually since these are per-port registers, we need to also specify the
number of ports, and register size per port, and use the regmap API for
multiple ports.
There's a more subtle point to be made about the other 2 register
fields:
- QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG
- QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE
which we are not writing any longer, for 2 reasons:
- Using the previous API (ocelot_write_rix), we were only writing 1 for
Felix and Ocelot, which was their hardware-default value, and which
there wasn't any intention in changing.
- In the case of SCH_NEXT_CFG, in fact Seville does not have this
register field at all, and therefore, if we want to have common code
we would be required to not write to it.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-07-14 00:57:03 +08:00
|
|
|
ocelot_fields_write(ocelot, port,
|
|
|
|
QSYS_SWITCH_PORT_MODE_PORT_ENA, 1);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Flow control */
|
|
|
|
ocelot_write_rix(ocelot, SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) |
|
|
|
|
SYS_MAC_FC_CFG_RX_FC_ENA | SYS_MAC_FC_CFG_TX_FC_ENA |
|
|
|
|
SYS_MAC_FC_CFG_ZERO_PAUSE_ENA |
|
|
|
|
SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) |
|
|
|
|
SYS_MAC_FC_CFG_FC_LINK_SPEED(speed),
|
2019-11-09 21:02:53 +08:00
|
|
|
SYS_MAC_FC_CFG, port);
|
|
|
|
ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_adjust_link);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_port_enable(struct ocelot *ocelot, int port,
|
|
|
|
struct phy_device *phy)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
/* Enable receiving frames on the port, and activate auto-learning of
|
|
|
|
* MAC addresses.
|
|
|
|
*/
|
|
|
|
ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_LEARNAUTO |
|
|
|
|
ANA_PORT_PORT_CFG_RECV_ENA |
|
2019-11-09 21:02:53 +08:00
|
|
|
ANA_PORT_PORT_CFG_PORTID_VAL(port),
|
|
|
|
ANA_PORT_PORT_CFG, port);
|
2019-11-09 21:02:57 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_enable);
|
2019-11-09 21:02:57 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_port_disable(struct ocelot *ocelot, int port)
|
2019-11-09 21:02:57 +08:00
|
|
|
{
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
|
|
|
|
|
|
ocelot_port_writel(ocelot_port, 0, DEV_MAC_ENA_CFG);
|
net: mscc: ocelot: convert QSYS_SWITCH_PORT_MODE and SYS_PORT_MODE to regfields
Currently Felix and Ocelot share the same bit layout in these per-port
registers, but Seville does not. So we need reg_fields for that.
Actually since these are per-port registers, we need to also specify the
number of ports, and register size per port, and use the regmap API for
multiple ports.
There's a more subtle point to be made about the other 2 register
fields:
- QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG
- QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE
which we are not writing any longer, for 2 reasons:
- Using the previous API (ocelot_write_rix), we were only writing 1 for
Felix and Ocelot, which was their hardware-default value, and which
there wasn't any intention in changing.
- In the case of SCH_NEXT_CFG, in fact Seville does not have this
register field at all, and therefore, if we want to have common code
we would be required to not write to it.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-07-14 00:57:03 +08:00
|
|
|
ocelot_fields_write(ocelot, port, QSYS_SWITCH_PORT_MODE_PORT_ENA, 0);
|
2019-11-09 21:02:57 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_disable);
|
2019-11-09 21:02:57 +08:00
|
|
|
|
2020-09-23 19:24:20 +08:00
|
|
|
void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port,
|
|
|
|
struct sk_buff *clone)
|
2019-11-20 16:23:16 +08:00
|
|
|
{
|
2020-09-23 19:24:20 +08:00
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
2019-11-20 16:23:16 +08:00
|
|
|
|
2020-09-23 19:24:20 +08:00
|
|
|
spin_lock(&ocelot_port->ts_id_lock);
|
2020-09-18 09:07:24 +08:00
|
|
|
|
2020-09-23 19:24:20 +08:00
|
|
|
skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
|
|
|
|
/* Store timestamp ID in cb[0] of sk_buff */
|
|
|
|
clone->cb[0] = ocelot_port->ts_id;
|
|
|
|
ocelot_port->ts_id = (ocelot_port->ts_id + 1) % 4;
|
|
|
|
skb_queue_tail(&ocelot_port->tx_skbs, clone);
|
2020-09-18 09:07:24 +08:00
|
|
|
|
2020-09-23 19:24:20 +08:00
|
|
|
spin_unlock(&ocelot_port->ts_id_lock);
|
2019-11-20 16:23:16 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_port_add_txtstamp_skb);
|
|
|
|
|
2019-11-20 16:23:15 +08:00
|
|
|
static void ocelot_get_hwtimestamp(struct ocelot *ocelot,
|
|
|
|
struct timespec64 *ts)
|
2019-08-12 22:45:37 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
|
|
|
|
|
|
|
|
/* Read current PTP time to get seconds */
|
|
|
|
val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
|
|
|
|
val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
|
|
|
|
val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE);
|
|
|
|
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
|
|
|
|
ts->tv_sec = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
|
|
|
|
|
|
|
|
/* Read packet HW timestamp from FIFO */
|
|
|
|
val = ocelot_read(ocelot, SYS_PTP_TXSTAMP);
|
|
|
|
ts->tv_nsec = SYS_PTP_TXSTAMP_PTP_TXSTAMP(val);
|
|
|
|
|
|
|
|
/* Sec has incremented since the ts was registered */
|
|
|
|
if ((ts->tv_sec & 0x1) != !!(val & SYS_PTP_TXSTAMP_PTP_TXSTAMP_SEC))
|
|
|
|
ts->tv_sec--;
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
|
|
|
|
}
|
2019-11-20 16:23:15 +08:00
|
|
|
|
|
|
|
void ocelot_get_txtstamp(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
int budget = OCELOT_PTP_QUEUE_SZ;
|
|
|
|
|
|
|
|
while (budget--) {
|
2019-11-27 15:27:57 +08:00
|
|
|
struct sk_buff *skb, *skb_tmp, *skb_match = NULL;
|
2019-11-20 16:23:15 +08:00
|
|
|
struct skb_shared_hwtstamps shhwtstamps;
|
|
|
|
struct ocelot_port *port;
|
|
|
|
struct timespec64 ts;
|
2019-11-27 15:27:57 +08:00
|
|
|
unsigned long flags;
|
2019-11-20 16:23:15 +08:00
|
|
|
u32 val, id, txport;
|
|
|
|
|
|
|
|
val = ocelot_read(ocelot, SYS_PTP_STATUS);
|
|
|
|
|
|
|
|
/* Check if a timestamp can be retrieved */
|
|
|
|
if (!(val & SYS_PTP_STATUS_PTP_MESS_VLD))
|
|
|
|
break;
|
|
|
|
|
|
|
|
WARN_ON(val & SYS_PTP_STATUS_PTP_OVFL);
|
|
|
|
|
|
|
|
/* Retrieve the ts ID and Tx port */
|
|
|
|
id = SYS_PTP_STATUS_PTP_MESS_ID_X(val);
|
|
|
|
txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val);
|
|
|
|
|
|
|
|
/* Retrieve its associated skb */
|
|
|
|
port = ocelot->ports[txport];
|
|
|
|
|
2019-11-27 15:27:57 +08:00
|
|
|
spin_lock_irqsave(&port->tx_skbs.lock, flags);
|
2019-11-20 16:23:15 +08:00
|
|
|
|
2019-11-27 15:27:57 +08:00
|
|
|
skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) {
|
|
|
|
if (skb->cb[0] != id)
|
|
|
|
continue;
|
|
|
|
__skb_unlink(skb, &port->tx_skbs);
|
|
|
|
skb_match = skb;
|
2019-11-27 15:27:56 +08:00
|
|
|
break;
|
2019-11-20 16:23:15 +08:00
|
|
|
}
|
|
|
|
|
2019-11-27 15:27:57 +08:00
|
|
|
spin_unlock_irqrestore(&port->tx_skbs.lock, flags);
|
|
|
|
|
2020-07-27 18:26:14 +08:00
|
|
|
/* Get the h/w timestamp */
|
|
|
|
ocelot_get_hwtimestamp(ocelot, &ts);
|
2019-11-20 16:23:15 +08:00
|
|
|
|
2019-11-27 15:27:57 +08:00
|
|
|
if (unlikely(!skb_match))
|
2019-11-20 16:23:15 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Set the timestamp into the skb */
|
|
|
|
memset(&shhwtstamps, 0, sizeof(shhwtstamps));
|
|
|
|
shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec);
|
2020-09-23 19:24:20 +08:00
|
|
|
skb_complete_tx_timestamp(skb_match, &shhwtstamps);
|
2020-07-27 18:26:14 +08:00
|
|
|
|
|
|
|
/* Next ts */
|
|
|
|
ocelot_write(ocelot, SYS_PTP_NXT_PTP_NXT, SYS_PTP_NXT);
|
2019-11-20 16:23:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_get_txtstamp);
|
2019-08-12 22:45:37 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_fdb_add(struct ocelot *ocelot, int port,
|
net: mscc: ocelot: fix untagged packet drops when enslaving to vlan aware bridge
To rehash a previous explanation given in commit 1c44ce560b4d ("net:
mscc: ocelot: fix vlan_filtering when enslaving to bridge before link is
up"), the switch driver operates the in a mode where a single VLAN can
be transmitted as untagged on a particular egress port. That is the
"native VLAN on trunk port" use case.
The configuration for this native VLAN is driven in 2 ways:
- Set the egress port rewriter to strip the VLAN tag for the native
VID (as it is egress-untagged, after all).
- Configure the ingress port to drop untagged and priority-tagged
traffic, if there is no native VLAN. The intention of this setting is
that a trunk port with no native VLAN should not accept untagged
traffic.
Since both of the above configurations for the native VLAN should only
be done if VLAN awareness is requested, they are actually done from the
ocelot_port_vlan_filtering function, after the basic procedure of
toggling the VLAN awareness flag of the port.
But there's a problem with that simplistic approach: we are trying to
juggle with 2 independent variables from a single function:
- Native VLAN of the port - its value is held in port->vid.
- VLAN awareness state of the port - currently there are some issues
here, more on that later*.
The actual problem can be seen when enslaving the switch ports to a VLAN
filtering bridge:
0. The driver configures a pvid of zero for each port, when in
standalone mode. While the bridge configures a default_pvid of 1 for
each port that gets added as a slave to it.
1. The bridge calls ocelot_port_vlan_filtering with vlan_aware=true.
The VLAN-filtering-dependent portion of the native VLAN
configuration is done, considering that the native VLAN is 0.
2. The bridge calls ocelot_vlan_add with vid=1, pvid=true,
untagged=true. The native VLAN changes to 1 (change which gets
propagated to hardware).
3. ??? - nobody calls ocelot_port_vlan_filtering again, to reapply the
VLAN-filtering-dependent portion of the native VLAN configuration,
for the new native VLAN of 1. One can notice that after toggling "ip
link set dev br0 type bridge vlan_filtering 0 && ip link set dev br0
type bridge vlan_filtering 1", the new native VLAN finally makes it
through and untagged traffic finally starts flowing again. But
obviously that shouldn't be needed.
So it is clear that 2 independent variables need to both re-trigger the
native VLAN configuration. So we introduce the second variable as
ocelot_port->vlan_aware.
*Actually both the DSA Felix driver and the Ocelot driver already had
each its own variable:
- Ocelot: ocelot_port_private->vlan_aware
- Felix: dsa_port->vlan_filtering
but the common Ocelot library needs to work with a single, common,
variable, so there is some refactoring done to move the vlan_aware
property from the private structure into the common ocelot_port
structure.
Fixes: 97bb69e1e36e ("net: mscc: ocelot: break apart ocelot_vlan_port_apply")
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-04-15 03:36:15 +08:00
|
|
|
const unsigned char *addr, u16 vid)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2020-06-21 19:46:00 +08:00
|
|
|
int pgid = port;
|
|
|
|
|
|
|
|
if (port == ocelot->npi)
|
|
|
|
pgid = PGID_CPU;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2020-06-21 19:46:00 +08:00
|
|
|
return ocelot_mact_learn(ocelot, pgid, addr, vid, ENTRYTYPE_LOCKED);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_fdb_add);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_fdb_del(struct ocelot *ocelot, int port,
|
|
|
|
const unsigned char *addr, u16 vid)
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
{
|
2018-05-15 04:04:57 +08:00
|
|
|
return ocelot_mact_forget(ocelot, addr, vid);
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_fdb_del);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2020-06-20 23:43:44 +08:00
|
|
|
int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
|
|
|
|
bool is_static, void *data)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
struct ocelot_dump_ctx *dump = data;
|
2018-05-15 04:04:57 +08:00
|
|
|
u32 portid = NETLINK_CB(dump->cb->skb).portid;
|
|
|
|
u32 seq = dump->cb->nlh->nlmsg_seq;
|
|
|
|
struct nlmsghdr *nlh;
|
|
|
|
struct ndmsg *ndm;
|
|
|
|
|
|
|
|
if (dump->idx < dump->cb->args[2])
|
|
|
|
goto skip;
|
|
|
|
|
|
|
|
nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
|
|
|
|
sizeof(*ndm), NLM_F_MULTI);
|
|
|
|
if (!nlh)
|
|
|
|
return -EMSGSIZE;
|
|
|
|
|
|
|
|
ndm = nlmsg_data(nlh);
|
|
|
|
ndm->ndm_family = AF_BRIDGE;
|
|
|
|
ndm->ndm_pad1 = 0;
|
|
|
|
ndm->ndm_pad2 = 0;
|
|
|
|
ndm->ndm_flags = NTF_SELF;
|
|
|
|
ndm->ndm_type = 0;
|
|
|
|
ndm->ndm_ifindex = dump->dev->ifindex;
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr))
|
2018-05-15 04:04:57 +08:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid))
|
2018-05-15 04:04:57 +08:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
nlmsg_end(dump->skb, nlh);
|
|
|
|
|
|
|
|
skip:
|
|
|
|
dump->idx++;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
nlmsg_cancel(dump->skb, nlh);
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
2020-06-20 23:43:44 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_fdb_do_dump);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
|
|
|
|
struct ocelot_mact_entry *entry)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
u32 val, dst, macl, mach;
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
char mac[ETH_ALEN];
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Set row and column to read from */
|
|
|
|
ocelot_field_write(ocelot, ANA_TABLES_MACTINDX_M_INDEX, row);
|
|
|
|
ocelot_field_write(ocelot, ANA_TABLES_MACTINDX_BUCKET, col);
|
|
|
|
|
|
|
|
/* Issue a read command */
|
|
|
|
ocelot_write(ocelot,
|
|
|
|
ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_READ),
|
|
|
|
ANA_TABLES_MACACCESS);
|
|
|
|
|
|
|
|
if (ocelot_mact_wait_for_completion(ocelot))
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
|
|
|
|
/* Read the entry flags */
|
|
|
|
val = ocelot_read(ocelot, ANA_TABLES_MACACCESS);
|
|
|
|
if (!(val & ANA_TABLES_MACACCESS_VALID))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* If the entry read has another port configured as its destination,
|
|
|
|
* do not report it.
|
|
|
|
*/
|
|
|
|
dst = (val & ANA_TABLES_MACACCESS_DEST_IDX_M) >> 3;
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
if (dst != port)
|
2018-05-15 04:04:57 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Get the entry's MAC address and VLAN id */
|
|
|
|
macl = ocelot_read(ocelot, ANA_TABLES_MACLDATA);
|
|
|
|
mach = ocelot_read(ocelot, ANA_TABLES_MACHDATA);
|
|
|
|
|
|
|
|
mac[0] = (mach >> 8) & 0xff;
|
|
|
|
mac[1] = (mach >> 0) & 0xff;
|
|
|
|
mac[2] = (macl >> 24) & 0xff;
|
|
|
|
mac[3] = (macl >> 16) & 0xff;
|
|
|
|
mac[4] = (macl >> 8) & 0xff;
|
|
|
|
mac[5] = (macl >> 0) & 0xff;
|
|
|
|
|
|
|
|
entry->vid = (mach >> 16) & 0xfff;
|
|
|
|
ether_addr_copy(entry->mac, mac);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_fdb_dump(struct ocelot *ocelot, int port,
|
|
|
|
dsa_fdb_dump_cb_t *cb, void *data)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
int i, j;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2020-05-04 06:20:26 +08:00
|
|
|
/* Loop through all the mac tables entries. */
|
|
|
|
for (i = 0; i < ocelot->num_mact_rows; i++) {
|
2018-05-15 04:04:57 +08:00
|
|
|
for (j = 0; j < 4; j++) {
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
struct ocelot_mact_entry entry;
|
|
|
|
bool is_static;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ocelot_mact_read(ocelot, port, i, j, &entry);
|
2018-05-15 04:04:57 +08:00
|
|
|
/* If the entry is invalid (wrong port, invalid...),
|
|
|
|
* skip it.
|
|
|
|
*/
|
|
|
|
if (ret == -EINVAL)
|
|
|
|
continue;
|
|
|
|
else if (ret)
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
is_static = (entry.type == ENTRYTYPE_LOCKED);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
ret = cb(entry.mac, entry.vid, is_static, data);
|
2018-05-15 04:04:57 +08:00
|
|
|
if (ret)
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
return ret;
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_fdb_dump);
|
net: mscc: ocelot: break out fdb operations into abstract implementations
To be able to implement a DSA front-end over ocelot_fdb_add,
ocelot_fdb_del, ocelot_fdb_dump, these need to have a simple function
prototype that is independent of struct net_device, netlink skb, etc.
So rename the ndo ops of the ocelot driver into
ocelot_port_fdb_{add,del,dump}, and have them all call the abstract
implementations. At the same time, refactor ocelot_port_fdb_do_dump into
a function whose prototype is compatible with dsa_fdb_dump_cb_t, so that
the do_dump implementations can live together and be called by the
ocelot_fdb_dump through a function pointer.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-11-09 21:02:49 +08:00
|
|
|
|
2019-11-20 16:23:14 +08:00
|
|
|
int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr)
|
2019-08-12 22:45:37 +08:00
|
|
|
{
|
|
|
|
return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config,
|
|
|
|
sizeof(ocelot->hwtstamp_config)) ? -EFAULT : 0;
|
|
|
|
}
|
2019-11-20 16:23:14 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_hwstamp_get);
|
2019-08-12 22:45:37 +08:00
|
|
|
|
2019-11-20 16:23:14 +08:00
|
|
|
int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr)
|
2019-08-12 22:45:37 +08:00
|
|
|
{
|
2019-11-09 21:02:50 +08:00
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
2019-08-12 22:45:37 +08:00
|
|
|
struct hwtstamp_config cfg;
|
|
|
|
|
|
|
|
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
/* reserved for future extensions */
|
|
|
|
if (cfg.flags)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Tx type sanity check */
|
|
|
|
switch (cfg.tx_type) {
|
|
|
|
case HWTSTAMP_TX_ON:
|
2019-11-09 21:02:50 +08:00
|
|
|
ocelot_port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP;
|
2019-08-12 22:45:37 +08:00
|
|
|
break;
|
|
|
|
case HWTSTAMP_TX_ONESTEP_SYNC:
|
|
|
|
/* IFH_REW_OP_ONE_STEP_PTP updates the correctional field, we
|
|
|
|
* need to update the origin time.
|
|
|
|
*/
|
2019-11-09 21:02:50 +08:00
|
|
|
ocelot_port->ptp_cmd = IFH_REW_OP_ORIGIN_PTP;
|
2019-08-12 22:45:37 +08:00
|
|
|
break;
|
|
|
|
case HWTSTAMP_TX_OFF:
|
2019-11-09 21:02:50 +08:00
|
|
|
ocelot_port->ptp_cmd = 0;
|
2019-08-12 22:45:37 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_lock(&ocelot->ptp_lock);
|
|
|
|
|
|
|
|
switch (cfg.rx_filter) {
|
|
|
|
case HWTSTAMP_FILTER_NONE:
|
|
|
|
break;
|
|
|
|
case HWTSTAMP_FILTER_ALL:
|
|
|
|
case HWTSTAMP_FILTER_SOME:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
|
|
|
|
case HWTSTAMP_FILTER_NTP_ALL:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_SYNC:
|
|
|
|
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
|
|
|
cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
mutex_unlock(&ocelot->ptp_lock);
|
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Commit back the result & save it */
|
|
|
|
memcpy(&ocelot->hwtstamp_config, &cfg, sizeof(cfg));
|
|
|
|
mutex_unlock(&ocelot->ptp_lock);
|
|
|
|
|
|
|
|
return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
|
|
|
|
}
|
2019-11-20 16:23:14 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_hwstamp_set);
|
2019-08-12 22:45:37 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (sset != ETH_SS_STATS)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < ocelot->num_stats; i++)
|
|
|
|
memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name,
|
|
|
|
ETH_GSTRING_LEN);
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_get_strings);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-04-16 22:51:59 +08:00
|
|
|
static void ocelot_update_stats(struct ocelot *ocelot)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
mutex_lock(&ocelot->stats_lock);
|
|
|
|
|
|
|
|
for (i = 0; i < ocelot->num_phys_ports; i++) {
|
|
|
|
/* Configure the port to read the stats from */
|
|
|
|
ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(i), SYS_STAT_CFG);
|
|
|
|
|
|
|
|
for (j = 0; j < ocelot->num_stats; j++) {
|
|
|
|
u32 val;
|
|
|
|
unsigned int idx = i * ocelot->num_stats + j;
|
|
|
|
|
|
|
|
val = ocelot_read_rix(ocelot, SYS_COUNT_RX_OCTETS,
|
|
|
|
ocelot->stats_layout[j].offset);
|
|
|
|
|
|
|
|
if (val < (ocelot->stats[idx] & U32_MAX))
|
|
|
|
ocelot->stats[idx] += (u64)1 << 32;
|
|
|
|
|
|
|
|
ocelot->stats[idx] = (ocelot->stats[idx] &
|
|
|
|
~(u64)U32_MAX) + val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-16 22:51:59 +08:00
|
|
|
mutex_unlock(&ocelot->stats_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ocelot_check_stats_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct delayed_work *del_work = to_delayed_work(work);
|
|
|
|
struct ocelot *ocelot = container_of(del_work, struct ocelot,
|
|
|
|
stats_work);
|
|
|
|
|
|
|
|
ocelot_update_stats(ocelot);
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
|
|
|
|
OCELOT_STATS_CHECK_DELAY);
|
|
|
|
}
|
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* check and update now */
|
2019-04-16 22:51:59 +08:00
|
|
|
ocelot_update_stats(ocelot);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Copy all counters */
|
|
|
|
for (i = 0; i < ocelot->num_stats; i++)
|
2019-11-09 21:02:53 +08:00
|
|
|
*data++ = ocelot->stats[port * ocelot->num_stats + i];
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_get_ethtool_stats);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset)
|
2019-11-09 21:02:54 +08:00
|
|
|
{
|
2018-05-15 04:04:57 +08:00
|
|
|
if (sset != ETH_SS_STATS)
|
|
|
|
return -EOPNOTSUPP;
|
2019-11-09 21:02:54 +08:00
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
return ocelot->num_stats;
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_get_sset_count);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_get_ts_info(struct ocelot *ocelot, int port,
|
|
|
|
struct ethtool_ts_info *info)
|
2019-11-09 21:02:54 +08:00
|
|
|
{
|
2019-08-12 22:45:37 +08:00
|
|
|
info->phc_index = ocelot->ptp_clock ?
|
|
|
|
ptp_clock_index(ocelot->ptp_clock) : -1;
|
2020-04-20 10:46:46 +08:00
|
|
|
if (info->phc_index == -1) {
|
|
|
|
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
|
|
|
|
SOF_TIMESTAMPING_RX_SOFTWARE |
|
|
|
|
SOF_TIMESTAMPING_SOFTWARE;
|
|
|
|
return 0;
|
|
|
|
}
|
2019-08-12 22:45:37 +08:00
|
|
|
info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE |
|
|
|
|
SOF_TIMESTAMPING_RX_SOFTWARE |
|
|
|
|
SOF_TIMESTAMPING_SOFTWARE |
|
|
|
|
SOF_TIMESTAMPING_TX_HARDWARE |
|
|
|
|
SOF_TIMESTAMPING_RX_HARDWARE |
|
|
|
|
SOF_TIMESTAMPING_RAW_HARDWARE;
|
|
|
|
info->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON) |
|
|
|
|
BIT(HWTSTAMP_TX_ONESTEP_SYNC);
|
|
|
|
info->rx_filters = BIT(HWTSTAMP_FILTER_NONE) | BIT(HWTSTAMP_FILTER_ALL);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_get_ts_info);
|
2019-08-12 22:45:37 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
u32 port_cfg;
|
2019-11-09 21:02:51 +08:00
|
|
|
int p, i;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:51 +08:00
|
|
|
if (!(BIT(port) & ocelot->bridge_mask))
|
|
|
|
return;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-09 21:02:51 +08:00
|
|
|
port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, port);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case BR_STATE_FORWARDING:
|
2019-11-09 21:02:51 +08:00
|
|
|
ocelot->bridge_fwd_mask |= BIT(port);
|
2020-08-24 06:36:59 +08:00
|
|
|
fallthrough;
|
2018-05-15 04:04:57 +08:00
|
|
|
case BR_STATE_LEARNING:
|
|
|
|
port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
port_cfg &= ~ANA_PORT_PORT_CFG_LEARN_ENA;
|
2019-11-09 21:02:51 +08:00
|
|
|
ocelot->bridge_fwd_mask &= ~BIT(port);
|
2018-05-15 04:04:57 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:51 +08:00
|
|
|
ocelot_write_gix(ocelot, port_cfg, ANA_PORT_PORT_CFG, port);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
/* Apply FWD mask. The loop is needed to add/remove the current port as
|
|
|
|
* a source for the other ports.
|
|
|
|
*/
|
2019-11-09 21:02:51 +08:00
|
|
|
for (p = 0; p < ocelot->num_phys_ports; p++) {
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
if (ocelot->bridge_fwd_mask & BIT(p)) {
|
2019-11-09 21:02:51 +08:00
|
|
|
unsigned long mask = ocelot->bridge_fwd_mask & ~BIT(p);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
for (i = 0; i < ocelot->num_phys_ports; i++) {
|
|
|
|
unsigned long bond_mask = ocelot->lags[i];
|
|
|
|
|
|
|
|
if (!bond_mask)
|
|
|
|
continue;
|
|
|
|
|
2019-11-09 21:02:51 +08:00
|
|
|
if (bond_mask & BIT(p)) {
|
2018-05-15 04:04:57 +08:00
|
|
|
mask &= ~bond_mask;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:03:01 +08:00
|
|
|
ocelot_write_rix(ocelot, mask,
|
2019-11-09 21:02:51 +08:00
|
|
|
ANA_PGID_PGID, PGID_SRC + p);
|
2018-05-15 04:04:57 +08:00
|
|
|
} else {
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
ocelot_write_rix(ocelot, 0,
|
2019-11-09 21:02:51 +08:00
|
|
|
ANA_PGID_PGID, PGID_SRC + p);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
|
|
|
}
|
2019-11-09 21:02:51 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_bridge_stp_state_set);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs)
|
2019-11-09 21:02:51 +08:00
|
|
|
{
|
2020-05-04 06:20:27 +08:00
|
|
|
unsigned int age_period = ANA_AUTOAGE_AGE_PERIOD(msecs / 2000);
|
|
|
|
|
|
|
|
/* Setting AGE_PERIOD to zero effectively disables automatic aging,
|
|
|
|
* which is clearly not what our intention is. So avoid that.
|
|
|
|
*/
|
|
|
|
if (!age_period)
|
|
|
|
age_period = 1;
|
|
|
|
|
|
|
|
ocelot_rmw(ocelot, age_period, ANA_AUTOAGE_AGE_PERIOD_M, ANA_AUTOAGE);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_set_ageing_time);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
static struct ocelot_multicast *ocelot_multicast_get(struct ocelot *ocelot,
|
|
|
|
const unsigned char *addr,
|
|
|
|
u16 vid)
|
|
|
|
{
|
|
|
|
struct ocelot_multicast *mc;
|
|
|
|
|
|
|
|
list_for_each_entry(mc, &ocelot->multicast, list) {
|
|
|
|
if (ether_addr_equal(mc->addr, addr) && mc->vid == vid)
|
|
|
|
return mc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: support IPv4, IPv6 and plain Ethernet mdb entries
The current procedure for installing a multicast address is hardcoded
for IPv4. But, in the ocelot hardware, there are 3 different procedures
for IPv4, IPv6 and for regular L2 multicast.
For IPv6 (33-33-xx-xx-xx-xx), it's the same as for IPv4
(01-00-5e-xx-xx-xx), except that the destination port mask is stuffed
into first 2 bytes of the MAC address except into first 3 bytes.
For plain Ethernet multicast, there's no port-in-address stuffing going
on, instead the DEST_IDX (pointer to PGID) is used there, just as for
unicast. So we have to use one of the nonreserved multicast PGIDs that
the hardware has allocated for this purpose.
This patch classifies the type of multicast address based on its first
bytes, then redirects to one of the 3 different hardware procedures.
Note that this gives us a really better way of redirecting PTP frames
sent at 01-1b-19-00-00-00 to the CPU. Previously, Yangbo Lu tried to add
a trapping rule for PTP EtherType but got a lot of pushback:
https://patchwork.ozlabs.org/project/netdev/patch/20190813025214.18601-5-yangbo.lu@nxp.com/
But right now, that isn't needed at all. The application stack (ptp4l)
does this for the PTP multicast addresses it's interested in (which are
configurable, and include 01-1b-19-00-00-00):
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = MAC_LEN;
memcpy(mreq.mr_address, addr1, MAC_LEN);
err1 = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
Into the kernel, this translates into a dev_mc_add on the switch network
interfaces, and our drivers know that it means they should translate it
into a host MDB address (make the CPU port be the destination).
Previously, this was broken because all mdb addresses were treated as
IPv4 (which 01-1b-19-00-00-00 obviously is not).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-06-21 19:46:03 +08:00
|
|
|
static enum macaccess_entry_type ocelot_classify_mdb(const unsigned char *addr)
|
|
|
|
{
|
|
|
|
if (addr[0] == 0x01 && addr[1] == 0x00 && addr[2] == 0x5e)
|
|
|
|
return ENTRYTYPE_MACv4;
|
|
|
|
if (addr[0] == 0x33 && addr[1] == 0x33)
|
|
|
|
return ENTRYTYPE_MACv6;
|
2020-10-29 10:27:34 +08:00
|
|
|
return ENTRYTYPE_LOCKED;
|
net: mscc: ocelot: support IPv4, IPv6 and plain Ethernet mdb entries
The current procedure for installing a multicast address is hardcoded
for IPv4. But, in the ocelot hardware, there are 3 different procedures
for IPv4, IPv6 and for regular L2 multicast.
For IPv6 (33-33-xx-xx-xx-xx), it's the same as for IPv4
(01-00-5e-xx-xx-xx), except that the destination port mask is stuffed
into first 2 bytes of the MAC address except into first 3 bytes.
For plain Ethernet multicast, there's no port-in-address stuffing going
on, instead the DEST_IDX (pointer to PGID) is used there, just as for
unicast. So we have to use one of the nonreserved multicast PGIDs that
the hardware has allocated for this purpose.
This patch classifies the type of multicast address based on its first
bytes, then redirects to one of the 3 different hardware procedures.
Note that this gives us a really better way of redirecting PTP frames
sent at 01-1b-19-00-00-00 to the CPU. Previously, Yangbo Lu tried to add
a trapping rule for PTP EtherType but got a lot of pushback:
https://patchwork.ozlabs.org/project/netdev/patch/20190813025214.18601-5-yangbo.lu@nxp.com/
But right now, that isn't needed at all. The application stack (ptp4l)
does this for the PTP multicast addresses it's interested in (which are
configurable, and include 01-1b-19-00-00-00):
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = MAC_LEN;
memcpy(mreq.mr_address, addr1, MAC_LEN);
err1 = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
Into the kernel, this translates into a dev_mc_add on the switch network
interfaces, and our drivers know that it means they should translate it
into a host MDB address (make the CPU port be the destination).
Previously, this was broken because all mdb addresses were treated as
IPv4 (which 01-1b-19-00-00-00 obviously is not).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-06-21 19:46:03 +08:00
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
static struct ocelot_pgid *ocelot_pgid_alloc(struct ocelot *ocelot, int index,
|
|
|
|
unsigned long ports)
|
net: mscc: ocelot: support IPv4, IPv6 and plain Ethernet mdb entries
The current procedure for installing a multicast address is hardcoded
for IPv4. But, in the ocelot hardware, there are 3 different procedures
for IPv4, IPv6 and for regular L2 multicast.
For IPv6 (33-33-xx-xx-xx-xx), it's the same as for IPv4
(01-00-5e-xx-xx-xx), except that the destination port mask is stuffed
into first 2 bytes of the MAC address except into first 3 bytes.
For plain Ethernet multicast, there's no port-in-address stuffing going
on, instead the DEST_IDX (pointer to PGID) is used there, just as for
unicast. So we have to use one of the nonreserved multicast PGIDs that
the hardware has allocated for this purpose.
This patch classifies the type of multicast address based on its first
bytes, then redirects to one of the 3 different hardware procedures.
Note that this gives us a really better way of redirecting PTP frames
sent at 01-1b-19-00-00-00 to the CPU. Previously, Yangbo Lu tried to add
a trapping rule for PTP EtherType but got a lot of pushback:
https://patchwork.ozlabs.org/project/netdev/patch/20190813025214.18601-5-yangbo.lu@nxp.com/
But right now, that isn't needed at all. The application stack (ptp4l)
does this for the PTP multicast addresses it's interested in (which are
configurable, and include 01-1b-19-00-00-00):
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = MAC_LEN;
memcpy(mreq.mr_address, addr1, MAC_LEN);
err1 = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
Into the kernel, this translates into a dev_mc_add on the switch network
interfaces, and our drivers know that it means they should translate it
into a host MDB address (make the CPU port be the destination).
Previously, this was broken because all mdb addresses were treated as
IPv4 (which 01-1b-19-00-00-00 obviously is not).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-06-21 19:46:03 +08:00
|
|
|
{
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
struct ocelot_pgid *pgid;
|
|
|
|
|
|
|
|
pgid = kzalloc(sizeof(*pgid), GFP_KERNEL);
|
|
|
|
if (!pgid)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
pgid->ports = ports;
|
|
|
|
pgid->index = index;
|
|
|
|
refcount_set(&pgid->refcount, 1);
|
|
|
|
list_add_tail(&pgid->list, &ocelot->pgids);
|
|
|
|
|
|
|
|
return pgid;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ocelot_pgid_free(struct ocelot *ocelot, struct ocelot_pgid *pgid)
|
|
|
|
{
|
|
|
|
if (!refcount_dec_and_test(&pgid->refcount))
|
|
|
|
return;
|
|
|
|
|
|
|
|
list_del(&pgid->list);
|
|
|
|
kfree(pgid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ocelot_pgid *ocelot_mdb_get_pgid(struct ocelot *ocelot,
|
|
|
|
const struct ocelot_multicast *mc)
|
|
|
|
{
|
|
|
|
struct ocelot_pgid *pgid;
|
|
|
|
int index;
|
net: mscc: ocelot: support IPv4, IPv6 and plain Ethernet mdb entries
The current procedure for installing a multicast address is hardcoded
for IPv4. But, in the ocelot hardware, there are 3 different procedures
for IPv4, IPv6 and for regular L2 multicast.
For IPv6 (33-33-xx-xx-xx-xx), it's the same as for IPv4
(01-00-5e-xx-xx-xx), except that the destination port mask is stuffed
into first 2 bytes of the MAC address except into first 3 bytes.
For plain Ethernet multicast, there's no port-in-address stuffing going
on, instead the DEST_IDX (pointer to PGID) is used there, just as for
unicast. So we have to use one of the nonreserved multicast PGIDs that
the hardware has allocated for this purpose.
This patch classifies the type of multicast address based on its first
bytes, then redirects to one of the 3 different hardware procedures.
Note that this gives us a really better way of redirecting PTP frames
sent at 01-1b-19-00-00-00 to the CPU. Previously, Yangbo Lu tried to add
a trapping rule for PTP EtherType but got a lot of pushback:
https://patchwork.ozlabs.org/project/netdev/patch/20190813025214.18601-5-yangbo.lu@nxp.com/
But right now, that isn't needed at all. The application stack (ptp4l)
does this for the PTP multicast addresses it's interested in (which are
configurable, and include 01-1b-19-00-00-00):
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = MAC_LEN;
memcpy(mreq.mr_address, addr1, MAC_LEN);
err1 = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
Into the kernel, this translates into a dev_mc_add on the switch network
interfaces, and our drivers know that it means they should translate it
into a host MDB address (make the CPU port be the destination).
Previously, this was broken because all mdb addresses were treated as
IPv4 (which 01-1b-19-00-00-00 obviously is not).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-06-21 19:46:03 +08:00
|
|
|
|
|
|
|
/* According to VSC7514 datasheet 3.9.1.5 IPv4 Multicast Entries and
|
|
|
|
* 3.9.1.6 IPv6 Multicast Entries, "Instead of a lookup in the
|
|
|
|
* destination mask table (PGID), the destination set is programmed as
|
|
|
|
* part of the entry MAC address.", and the DEST_IDX is set to 0.
|
|
|
|
*/
|
2020-10-29 10:27:37 +08:00
|
|
|
if (mc->entry_type == ENTRYTYPE_MACv4 ||
|
|
|
|
mc->entry_type == ENTRYTYPE_MACv6)
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
return ocelot_pgid_alloc(ocelot, 0, mc->ports);
|
|
|
|
|
|
|
|
list_for_each_entry(pgid, &ocelot->pgids, list) {
|
|
|
|
/* When searching for a nonreserved multicast PGID, ignore the
|
|
|
|
* dummy PGID of zero that we have for MACv4/MACv6 entries
|
|
|
|
*/
|
|
|
|
if (pgid->index && pgid->ports == mc->ports) {
|
|
|
|
refcount_inc(&pgid->refcount);
|
|
|
|
return pgid;
|
|
|
|
}
|
|
|
|
}
|
net: mscc: ocelot: support IPv4, IPv6 and plain Ethernet mdb entries
The current procedure for installing a multicast address is hardcoded
for IPv4. But, in the ocelot hardware, there are 3 different procedures
for IPv4, IPv6 and for regular L2 multicast.
For IPv6 (33-33-xx-xx-xx-xx), it's the same as for IPv4
(01-00-5e-xx-xx-xx), except that the destination port mask is stuffed
into first 2 bytes of the MAC address except into first 3 bytes.
For plain Ethernet multicast, there's no port-in-address stuffing going
on, instead the DEST_IDX (pointer to PGID) is used there, just as for
unicast. So we have to use one of the nonreserved multicast PGIDs that
the hardware has allocated for this purpose.
This patch classifies the type of multicast address based on its first
bytes, then redirects to one of the 3 different hardware procedures.
Note that this gives us a really better way of redirecting PTP frames
sent at 01-1b-19-00-00-00 to the CPU. Previously, Yangbo Lu tried to add
a trapping rule for PTP EtherType but got a lot of pushback:
https://patchwork.ozlabs.org/project/netdev/patch/20190813025214.18601-5-yangbo.lu@nxp.com/
But right now, that isn't needed at all. The application stack (ptp4l)
does this for the PTP multicast addresses it's interested in (which are
configurable, and include 01-1b-19-00-00-00):
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = MAC_LEN;
memcpy(mreq.mr_address, addr1, MAC_LEN);
err1 = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
Into the kernel, this translates into a dev_mc_add on the switch network
interfaces, and our drivers know that it means they should translate it
into a host MDB address (make the CPU port be the destination).
Previously, this was broken because all mdb addresses were treated as
IPv4 (which 01-1b-19-00-00-00 obviously is not).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-06-21 19:46:03 +08:00
|
|
|
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
/* Search for a free index in the nonreserved multicast PGID area */
|
|
|
|
for_each_nonreserved_multicast_dest_pgid(ocelot, index) {
|
net: mscc: ocelot: support IPv4, IPv6 and plain Ethernet mdb entries
The current procedure for installing a multicast address is hardcoded
for IPv4. But, in the ocelot hardware, there are 3 different procedures
for IPv4, IPv6 and for regular L2 multicast.
For IPv6 (33-33-xx-xx-xx-xx), it's the same as for IPv4
(01-00-5e-xx-xx-xx), except that the destination port mask is stuffed
into first 2 bytes of the MAC address except into first 3 bytes.
For plain Ethernet multicast, there's no port-in-address stuffing going
on, instead the DEST_IDX (pointer to PGID) is used there, just as for
unicast. So we have to use one of the nonreserved multicast PGIDs that
the hardware has allocated for this purpose.
This patch classifies the type of multicast address based on its first
bytes, then redirects to one of the 3 different hardware procedures.
Note that this gives us a really better way of redirecting PTP frames
sent at 01-1b-19-00-00-00 to the CPU. Previously, Yangbo Lu tried to add
a trapping rule for PTP EtherType but got a lot of pushback:
https://patchwork.ozlabs.org/project/netdev/patch/20190813025214.18601-5-yangbo.lu@nxp.com/
But right now, that isn't needed at all. The application stack (ptp4l)
does this for the PTP multicast addresses it's interested in (which are
configurable, and include 01-1b-19-00-00-00):
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = MAC_LEN;
memcpy(mreq.mr_address, addr1, MAC_LEN);
err1 = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
Into the kernel, this translates into a dev_mc_add on the switch network
interfaces, and our drivers know that it means they should translate it
into a host MDB address (make the CPU port be the destination).
Previously, this was broken because all mdb addresses were treated as
IPv4 (which 01-1b-19-00-00-00 obviously is not).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-06-21 19:46:03 +08:00
|
|
|
bool used = false;
|
|
|
|
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
list_for_each_entry(pgid, &ocelot->pgids, list) {
|
|
|
|
if (pgid->index == index) {
|
net: mscc: ocelot: support IPv4, IPv6 and plain Ethernet mdb entries
The current procedure for installing a multicast address is hardcoded
for IPv4. But, in the ocelot hardware, there are 3 different procedures
for IPv4, IPv6 and for regular L2 multicast.
For IPv6 (33-33-xx-xx-xx-xx), it's the same as for IPv4
(01-00-5e-xx-xx-xx), except that the destination port mask is stuffed
into first 2 bytes of the MAC address except into first 3 bytes.
For plain Ethernet multicast, there's no port-in-address stuffing going
on, instead the DEST_IDX (pointer to PGID) is used there, just as for
unicast. So we have to use one of the nonreserved multicast PGIDs that
the hardware has allocated for this purpose.
This patch classifies the type of multicast address based on its first
bytes, then redirects to one of the 3 different hardware procedures.
Note that this gives us a really better way of redirecting PTP frames
sent at 01-1b-19-00-00-00 to the CPU. Previously, Yangbo Lu tried to add
a trapping rule for PTP EtherType but got a lot of pushback:
https://patchwork.ozlabs.org/project/netdev/patch/20190813025214.18601-5-yangbo.lu@nxp.com/
But right now, that isn't needed at all. The application stack (ptp4l)
does this for the PTP multicast addresses it's interested in (which are
configurable, and include 01-1b-19-00-00-00):
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = MAC_LEN;
memcpy(mreq.mr_address, addr1, MAC_LEN);
err1 = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
Into the kernel, this translates into a dev_mc_add on the switch network
interfaces, and our drivers know that it means they should translate it
into a host MDB address (make the CPU port be the destination).
Previously, this was broken because all mdb addresses were treated as
IPv4 (which 01-1b-19-00-00-00 obviously is not).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-06-21 19:46:03 +08:00
|
|
|
used = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!used)
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
return ocelot_pgid_alloc(ocelot, index, mc->ports);
|
net: mscc: ocelot: support IPv4, IPv6 and plain Ethernet mdb entries
The current procedure for installing a multicast address is hardcoded
for IPv4. But, in the ocelot hardware, there are 3 different procedures
for IPv4, IPv6 and for regular L2 multicast.
For IPv6 (33-33-xx-xx-xx-xx), it's the same as for IPv4
(01-00-5e-xx-xx-xx), except that the destination port mask is stuffed
into first 2 bytes of the MAC address except into first 3 bytes.
For plain Ethernet multicast, there's no port-in-address stuffing going
on, instead the DEST_IDX (pointer to PGID) is used there, just as for
unicast. So we have to use one of the nonreserved multicast PGIDs that
the hardware has allocated for this purpose.
This patch classifies the type of multicast address based on its first
bytes, then redirects to one of the 3 different hardware procedures.
Note that this gives us a really better way of redirecting PTP frames
sent at 01-1b-19-00-00-00 to the CPU. Previously, Yangbo Lu tried to add
a trapping rule for PTP EtherType but got a lot of pushback:
https://patchwork.ozlabs.org/project/netdev/patch/20190813025214.18601-5-yangbo.lu@nxp.com/
But right now, that isn't needed at all. The application stack (ptp4l)
does this for the PTP multicast addresses it's interested in (which are
configurable, and include 01-1b-19-00-00-00):
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = MAC_LEN;
memcpy(mreq.mr_address, addr1, MAC_LEN);
err1 = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
Into the kernel, this translates into a dev_mc_add on the switch network
interfaces, and our drivers know that it means they should translate it
into a host MDB address (make the CPU port be the destination).
Previously, this was broken because all mdb addresses were treated as
IPv4 (which 01-1b-19-00-00-00 obviously is not).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-06-21 19:46:03 +08:00
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
return ERR_PTR(-ENOSPC);
|
net: mscc: ocelot: support IPv4, IPv6 and plain Ethernet mdb entries
The current procedure for installing a multicast address is hardcoded
for IPv4. But, in the ocelot hardware, there are 3 different procedures
for IPv4, IPv6 and for regular L2 multicast.
For IPv6 (33-33-xx-xx-xx-xx), it's the same as for IPv4
(01-00-5e-xx-xx-xx), except that the destination port mask is stuffed
into first 2 bytes of the MAC address except into first 3 bytes.
For plain Ethernet multicast, there's no port-in-address stuffing going
on, instead the DEST_IDX (pointer to PGID) is used there, just as for
unicast. So we have to use one of the nonreserved multicast PGIDs that
the hardware has allocated for this purpose.
This patch classifies the type of multicast address based on its first
bytes, then redirects to one of the 3 different hardware procedures.
Note that this gives us a really better way of redirecting PTP frames
sent at 01-1b-19-00-00-00 to the CPU. Previously, Yangbo Lu tried to add
a trapping rule for PTP EtherType but got a lot of pushback:
https://patchwork.ozlabs.org/project/netdev/patch/20190813025214.18601-5-yangbo.lu@nxp.com/
But right now, that isn't needed at all. The application stack (ptp4l)
does this for the PTP multicast addresses it's interested in (which are
configurable, and include 01-1b-19-00-00-00):
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = MAC_LEN;
memcpy(mreq.mr_address, addr1, MAC_LEN);
err1 = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
Into the kernel, this translates into a dev_mc_add on the switch network
interfaces, and our drivers know that it means they should translate it
into a host MDB address (make the CPU port be the destination).
Previously, this was broken because all mdb addresses were treated as
IPv4 (which 01-1b-19-00-00-00 obviously is not).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-06-21 19:46:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ocelot_encode_ports_to_mdb(unsigned char *addr,
|
2020-10-29 10:27:37 +08:00
|
|
|
struct ocelot_multicast *mc)
|
net: mscc: ocelot: support IPv4, IPv6 and plain Ethernet mdb entries
The current procedure for installing a multicast address is hardcoded
for IPv4. But, in the ocelot hardware, there are 3 different procedures
for IPv4, IPv6 and for regular L2 multicast.
For IPv6 (33-33-xx-xx-xx-xx), it's the same as for IPv4
(01-00-5e-xx-xx-xx), except that the destination port mask is stuffed
into first 2 bytes of the MAC address except into first 3 bytes.
For plain Ethernet multicast, there's no port-in-address stuffing going
on, instead the DEST_IDX (pointer to PGID) is used there, just as for
unicast. So we have to use one of the nonreserved multicast PGIDs that
the hardware has allocated for this purpose.
This patch classifies the type of multicast address based on its first
bytes, then redirects to one of the 3 different hardware procedures.
Note that this gives us a really better way of redirecting PTP frames
sent at 01-1b-19-00-00-00 to the CPU. Previously, Yangbo Lu tried to add
a trapping rule for PTP EtherType but got a lot of pushback:
https://patchwork.ozlabs.org/project/netdev/patch/20190813025214.18601-5-yangbo.lu@nxp.com/
But right now, that isn't needed at all. The application stack (ptp4l)
does this for the PTP multicast addresses it's interested in (which are
configurable, and include 01-1b-19-00-00-00):
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = MAC_LEN;
memcpy(mreq.mr_address, addr1, MAC_LEN);
err1 = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
Into the kernel, this translates into a dev_mc_add on the switch network
interfaces, and our drivers know that it means they should translate it
into a host MDB address (make the CPU port be the destination).
Previously, this was broken because all mdb addresses were treated as
IPv4 (which 01-1b-19-00-00-00 obviously is not).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-06-21 19:46:03 +08:00
|
|
|
{
|
2020-10-29 10:27:35 +08:00
|
|
|
ether_addr_copy(addr, mc->addr);
|
net: mscc: ocelot: support IPv4, IPv6 and plain Ethernet mdb entries
The current procedure for installing a multicast address is hardcoded
for IPv4. But, in the ocelot hardware, there are 3 different procedures
for IPv4, IPv6 and for regular L2 multicast.
For IPv6 (33-33-xx-xx-xx-xx), it's the same as for IPv4
(01-00-5e-xx-xx-xx), except that the destination port mask is stuffed
into first 2 bytes of the MAC address except into first 3 bytes.
For plain Ethernet multicast, there's no port-in-address stuffing going
on, instead the DEST_IDX (pointer to PGID) is used there, just as for
unicast. So we have to use one of the nonreserved multicast PGIDs that
the hardware has allocated for this purpose.
This patch classifies the type of multicast address based on its first
bytes, then redirects to one of the 3 different hardware procedures.
Note that this gives us a really better way of redirecting PTP frames
sent at 01-1b-19-00-00-00 to the CPU. Previously, Yangbo Lu tried to add
a trapping rule for PTP EtherType but got a lot of pushback:
https://patchwork.ozlabs.org/project/netdev/patch/20190813025214.18601-5-yangbo.lu@nxp.com/
But right now, that isn't needed at all. The application stack (ptp4l)
does this for the PTP multicast addresses it's interested in (which are
configurable, and include 01-1b-19-00-00-00):
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = MAC_LEN;
memcpy(mreq.mr_address, addr1, MAC_LEN);
err1 = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
Into the kernel, this translates into a dev_mc_add on the switch network
interfaces, and our drivers know that it means they should translate it
into a host MDB address (make the CPU port be the destination).
Previously, this was broken because all mdb addresses were treated as
IPv4 (which 01-1b-19-00-00-00 obviously is not).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-06-21 19:46:03 +08:00
|
|
|
|
2020-10-29 10:27:37 +08:00
|
|
|
if (mc->entry_type == ENTRYTYPE_MACv4) {
|
net: mscc: ocelot: support IPv4, IPv6 and plain Ethernet mdb entries
The current procedure for installing a multicast address is hardcoded
for IPv4. But, in the ocelot hardware, there are 3 different procedures
for IPv4, IPv6 and for regular L2 multicast.
For IPv6 (33-33-xx-xx-xx-xx), it's the same as for IPv4
(01-00-5e-xx-xx-xx), except that the destination port mask is stuffed
into first 2 bytes of the MAC address except into first 3 bytes.
For plain Ethernet multicast, there's no port-in-address stuffing going
on, instead the DEST_IDX (pointer to PGID) is used there, just as for
unicast. So we have to use one of the nonreserved multicast PGIDs that
the hardware has allocated for this purpose.
This patch classifies the type of multicast address based on its first
bytes, then redirects to one of the 3 different hardware procedures.
Note that this gives us a really better way of redirecting PTP frames
sent at 01-1b-19-00-00-00 to the CPU. Previously, Yangbo Lu tried to add
a trapping rule for PTP EtherType but got a lot of pushback:
https://patchwork.ozlabs.org/project/netdev/patch/20190813025214.18601-5-yangbo.lu@nxp.com/
But right now, that isn't needed at all. The application stack (ptp4l)
does this for the PTP multicast addresses it's interested in (which are
configurable, and include 01-1b-19-00-00-00):
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = MAC_LEN;
memcpy(mreq.mr_address, addr1, MAC_LEN);
err1 = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
Into the kernel, this translates into a dev_mc_add on the switch network
interfaces, and our drivers know that it means they should translate it
into a host MDB address (make the CPU port be the destination).
Previously, this was broken because all mdb addresses were treated as
IPv4 (which 01-1b-19-00-00-00 obviously is not).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-06-21 19:46:03 +08:00
|
|
|
addr[0] = 0;
|
|
|
|
addr[1] = mc->ports >> 8;
|
|
|
|
addr[2] = mc->ports & 0xff;
|
2020-10-29 10:27:37 +08:00
|
|
|
} else if (mc->entry_type == ENTRYTYPE_MACv6) {
|
net: mscc: ocelot: support IPv4, IPv6 and plain Ethernet mdb entries
The current procedure for installing a multicast address is hardcoded
for IPv4. But, in the ocelot hardware, there are 3 different procedures
for IPv4, IPv6 and for regular L2 multicast.
For IPv6 (33-33-xx-xx-xx-xx), it's the same as for IPv4
(01-00-5e-xx-xx-xx), except that the destination port mask is stuffed
into first 2 bytes of the MAC address except into first 3 bytes.
For plain Ethernet multicast, there's no port-in-address stuffing going
on, instead the DEST_IDX (pointer to PGID) is used there, just as for
unicast. So we have to use one of the nonreserved multicast PGIDs that
the hardware has allocated for this purpose.
This patch classifies the type of multicast address based on its first
bytes, then redirects to one of the 3 different hardware procedures.
Note that this gives us a really better way of redirecting PTP frames
sent at 01-1b-19-00-00-00 to the CPU. Previously, Yangbo Lu tried to add
a trapping rule for PTP EtherType but got a lot of pushback:
https://patchwork.ozlabs.org/project/netdev/patch/20190813025214.18601-5-yangbo.lu@nxp.com/
But right now, that isn't needed at all. The application stack (ptp4l)
does this for the PTP multicast addresses it's interested in (which are
configurable, and include 01-1b-19-00-00-00):
memset(&mreq, 0, sizeof(mreq));
mreq.mr_ifindex = index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = MAC_LEN;
memcpy(mreq.mr_address, addr1, MAC_LEN);
err1 = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
Into the kernel, this translates into a dev_mc_add on the switch network
interfaces, and our drivers know that it means they should translate it
into a host MDB address (make the CPU port be the destination).
Previously, this was broken because all mdb addresses were treated as
IPv4 (which 01-1b-19-00-00-00 obviously is not).
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-06-21 19:46:03 +08:00
|
|
|
addr[0] = mc->ports >> 8;
|
|
|
|
addr[1] = mc->ports & 0xff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-21 19:46:01 +08:00
|
|
|
int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
|
|
|
|
const struct switchdev_obj_port_mdb *mdb)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
unsigned char addr[ETH_ALEN];
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_multicast *mc;
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
struct ocelot_pgid *pgid;
|
2018-05-15 04:04:57 +08:00
|
|
|
u16 vid = mdb->vid;
|
|
|
|
|
2020-06-21 19:46:00 +08:00
|
|
|
if (port == ocelot->npi)
|
|
|
|
port = ocelot->num_phys_ports;
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
|
|
|
|
if (!mc) {
|
2020-10-29 10:27:36 +08:00
|
|
|
/* New entry */
|
2020-10-29 10:27:37 +08:00
|
|
|
mc = devm_kzalloc(ocelot->dev, sizeof(*mc), GFP_KERNEL);
|
|
|
|
if (!mc)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
mc->entry_type = ocelot_classify_mdb(mdb->addr);
|
|
|
|
ether_addr_copy(mc->addr, mdb->addr);
|
|
|
|
mc->vid = vid;
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
list_add_tail(&mc->list, &ocelot->multicast);
|
2020-10-29 10:27:36 +08:00
|
|
|
} else {
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
/* Existing entry. Clean up the current port mask from
|
|
|
|
* hardware now, because we'll be modifying it.
|
|
|
|
*/
|
|
|
|
ocelot_pgid_free(ocelot, mc->pgid);
|
2020-10-29 10:27:37 +08:00
|
|
|
ocelot_encode_ports_to_mdb(addr, mc);
|
2018-05-15 04:04:57 +08:00
|
|
|
ocelot_mact_forget(ocelot, addr, vid);
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
mc->ports |= BIT(port);
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
|
|
|
|
pgid = ocelot_mdb_get_pgid(ocelot, mc);
|
|
|
|
if (IS_ERR(pgid)) {
|
|
|
|
dev_err(ocelot->dev,
|
|
|
|
"Cannot allocate PGID for mdb %pM vid %d\n",
|
|
|
|
mc->addr, mc->vid);
|
|
|
|
devm_kfree(ocelot->dev, mc);
|
|
|
|
return PTR_ERR(pgid);
|
|
|
|
}
|
|
|
|
mc->pgid = pgid;
|
|
|
|
|
2020-10-29 10:27:37 +08:00
|
|
|
ocelot_encode_ports_to_mdb(addr, mc);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
if (mc->entry_type != ENTRYTYPE_MACv4 &&
|
|
|
|
mc->entry_type != ENTRYTYPE_MACv6)
|
|
|
|
ocelot_write_rix(ocelot, pgid->ports, ANA_PGID_PGID,
|
|
|
|
pgid->index);
|
|
|
|
|
|
|
|
return ocelot_mact_learn(ocelot, pgid->index, addr, vid,
|
2020-10-29 10:27:37 +08:00
|
|
|
mc->entry_type);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
2020-06-21 19:46:01 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_mdb_add);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2020-06-21 19:46:01 +08:00
|
|
|
int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
|
|
|
|
const struct switchdev_obj_port_mdb *mdb)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
unsigned char addr[ETH_ALEN];
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_multicast *mc;
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
struct ocelot_pgid *pgid;
|
2018-05-15 04:04:57 +08:00
|
|
|
u16 vid = mdb->vid;
|
|
|
|
|
2020-06-21 19:46:00 +08:00
|
|
|
if (port == ocelot->npi)
|
|
|
|
port = ocelot->num_phys_ports;
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
|
|
|
|
if (!mc)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2020-10-29 10:27:37 +08:00
|
|
|
ocelot_encode_ports_to_mdb(addr, mc);
|
2018-05-15 04:04:57 +08:00
|
|
|
ocelot_mact_forget(ocelot, addr, vid);
|
|
|
|
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
ocelot_pgid_free(ocelot, mc->pgid);
|
2019-11-09 21:02:53 +08:00
|
|
|
mc->ports &= ~BIT(port);
|
2018-05-15 04:04:57 +08:00
|
|
|
if (!mc->ports) {
|
|
|
|
list_del(&mc->list);
|
|
|
|
devm_kfree(ocelot->dev, mc);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
/* We have a PGID with fewer ports now */
|
|
|
|
pgid = ocelot_mdb_get_pgid(ocelot, mc);
|
|
|
|
if (IS_ERR(pgid))
|
|
|
|
return PTR_ERR(pgid);
|
|
|
|
mc->pgid = pgid;
|
|
|
|
|
2020-10-29 10:27:37 +08:00
|
|
|
ocelot_encode_ports_to_mdb(addr, mc);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
if (mc->entry_type != ENTRYTYPE_MACv4 &&
|
|
|
|
mc->entry_type != ENTRYTYPE_MACv6)
|
|
|
|
ocelot_write_rix(ocelot, pgid->ports, ANA_PGID_PGID,
|
|
|
|
pgid->index);
|
|
|
|
|
|
|
|
return ocelot_mact_learn(ocelot, pgid->index, addr, vid,
|
2020-10-29 10:27:37 +08:00
|
|
|
mc->entry_type);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
2020-06-21 19:46:01 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_mdb_del);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
|
|
|
|
struct net_device *bridge)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
|
|
|
if (!ocelot->bridge_mask) {
|
|
|
|
ocelot->hw_bridge_dev = bridge;
|
|
|
|
} else {
|
|
|
|
if (ocelot->hw_bridge_dev != bridge)
|
|
|
|
/* This is adding the port to a second bridge, this is
|
|
|
|
* unsupported */
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:52 +08:00
|
|
|
ocelot->bridge_mask |= BIT(port);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_bridge_join);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
|
|
|
|
struct net_device *bridge)
|
2018-05-15 04:04:57 +08:00
|
|
|
{
|
2020-10-31 18:29:12 +08:00
|
|
|
struct ocelot_vlan pvid = {0}, native_vlan = {0};
|
2020-10-03 06:06:46 +08:00
|
|
|
struct switchdev_trans trans;
|
|
|
|
int ret;
|
|
|
|
|
2019-11-09 21:02:47 +08:00
|
|
|
ocelot->bridge_mask &= ~BIT(port);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
if (!ocelot->bridge_mask)
|
|
|
|
ocelot->hw_bridge_dev = NULL;
|
2018-06-26 20:28:49 +08:00
|
|
|
|
2020-10-03 06:06:46 +08:00
|
|
|
trans.ph_prepare = true;
|
|
|
|
ret = ocelot_port_vlan_filtering(ocelot, port, false, &trans);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
trans.ph_prepare = false;
|
|
|
|
ret = ocelot_port_vlan_filtering(ocelot, port, false, &trans);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2020-10-31 18:29:12 +08:00
|
|
|
ocelot_port_set_pvid(ocelot, port, pvid);
|
|
|
|
return ocelot_port_set_native_vlan(ocelot, port, native_vlan);
|
2018-05-15 04:04:57 +08:00
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_bridge_leave);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2018-06-26 20:28:48 +08:00
|
|
|
static void ocelot_set_aggr_pgids(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
int i, port, lag;
|
|
|
|
|
|
|
|
/* Reset destination and aggregation PGIDS */
|
2020-06-21 19:46:02 +08:00
|
|
|
for_each_unicast_dest_pgid(ocelot, port)
|
2018-06-26 20:28:48 +08:00
|
|
|
ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, port);
|
|
|
|
|
2020-06-21 19:46:02 +08:00
|
|
|
for_each_aggr_pgid(ocelot, i)
|
2018-06-26 20:28:48 +08:00
|
|
|
ocelot_write_rix(ocelot, GENMASK(ocelot->num_phys_ports - 1, 0),
|
|
|
|
ANA_PGID_PGID, i);
|
|
|
|
|
|
|
|
/* Now, set PGIDs for each LAG */
|
|
|
|
for (lag = 0; lag < ocelot->num_phys_ports; lag++) {
|
|
|
|
unsigned long bond_mask;
|
|
|
|
int aggr_count = 0;
|
|
|
|
u8 aggr_idx[16];
|
|
|
|
|
|
|
|
bond_mask = ocelot->lags[lag];
|
|
|
|
if (!bond_mask)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for_each_set_bit(port, &bond_mask, ocelot->num_phys_ports) {
|
|
|
|
// Destination mask
|
|
|
|
ocelot_write_rix(ocelot, bond_mask,
|
|
|
|
ANA_PGID_PGID, port);
|
|
|
|
aggr_idx[aggr_count] = port;
|
|
|
|
aggr_count++;
|
|
|
|
}
|
|
|
|
|
2020-06-21 19:46:02 +08:00
|
|
|
for_each_aggr_pgid(ocelot, i) {
|
2018-06-26 20:28:48 +08:00
|
|
|
u32 ac;
|
|
|
|
|
|
|
|
ac = ocelot_read_rix(ocelot, ANA_PGID_PGID, i);
|
|
|
|
ac &= ~bond_mask;
|
|
|
|
ac |= BIT(aggr_idx[i % aggr_count]);
|
|
|
|
ocelot_write_rix(ocelot, ac, ANA_PGID_PGID, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ocelot_setup_lag(struct ocelot *ocelot, int lag)
|
|
|
|
{
|
|
|
|
unsigned long bond_mask = ocelot->lags[lag];
|
|
|
|
unsigned int p;
|
|
|
|
|
|
|
|
for_each_set_bit(p, &bond_mask, ocelot->num_phys_ports) {
|
|
|
|
u32 port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, p);
|
|
|
|
|
|
|
|
port_cfg &= ~ANA_PORT_PORT_CFG_PORTID_VAL_M;
|
|
|
|
|
|
|
|
/* Use lag port as logical port for port i */
|
|
|
|
ocelot_write_gix(ocelot, port_cfg |
|
|
|
|
ANA_PORT_PORT_CFG_PORTID_VAL(lag),
|
|
|
|
ANA_PORT_PORT_CFG, p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-20 23:43:44 +08:00
|
|
|
int ocelot_port_lag_join(struct ocelot *ocelot, int port,
|
|
|
|
struct net_device *bond)
|
2018-06-26 20:28:48 +08:00
|
|
|
{
|
|
|
|
struct net_device *ndev;
|
|
|
|
u32 bond_mask = 0;
|
2019-11-09 21:02:52 +08:00
|
|
|
int lag, lp;
|
2018-06-26 20:28:48 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
for_each_netdev_in_bond_rcu(bond, ndev) {
|
2019-11-09 21:02:53 +08:00
|
|
|
struct ocelot_port_private *priv = netdev_priv(ndev);
|
2018-06-26 20:28:48 +08:00
|
|
|
|
2019-11-09 21:02:53 +08:00
|
|
|
bond_mask |= BIT(priv->chip_port);
|
2018-06-26 20:28:48 +08:00
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
lp = __ffs(bond_mask);
|
|
|
|
|
|
|
|
/* If the new port is the lowest one, use it as the logical port from
|
|
|
|
* now on
|
|
|
|
*/
|
2019-11-09 21:02:52 +08:00
|
|
|
if (port == lp) {
|
|
|
|
lag = port;
|
|
|
|
ocelot->lags[port] = bond_mask;
|
|
|
|
bond_mask &= ~BIT(port);
|
2018-06-26 20:28:48 +08:00
|
|
|
if (bond_mask) {
|
|
|
|
lp = __ffs(bond_mask);
|
|
|
|
ocelot->lags[lp] = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lag = lp;
|
2019-11-09 21:02:52 +08:00
|
|
|
ocelot->lags[lp] |= BIT(port);
|
2018-06-26 20:28:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ocelot_setup_lag(ocelot, lag);
|
|
|
|
ocelot_set_aggr_pgids(ocelot);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2020-06-20 23:43:44 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_lag_join);
|
2018-06-26 20:28:48 +08:00
|
|
|
|
2020-06-20 23:43:44 +08:00
|
|
|
void ocelot_port_lag_leave(struct ocelot *ocelot, int port,
|
|
|
|
struct net_device *bond)
|
2018-06-26 20:28:48 +08:00
|
|
|
{
|
|
|
|
u32 port_cfg;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Remove port from any lag */
|
|
|
|
for (i = 0; i < ocelot->num_phys_ports; i++)
|
2019-11-09 21:02:52 +08:00
|
|
|
ocelot->lags[i] &= ~BIT(port);
|
2018-06-26 20:28:48 +08:00
|
|
|
|
|
|
|
/* if it was the logical port of the lag, move the lag config to the
|
|
|
|
* next port
|
|
|
|
*/
|
2019-11-09 21:02:52 +08:00
|
|
|
if (ocelot->lags[port]) {
|
|
|
|
int n = __ffs(ocelot->lags[port]);
|
2018-06-26 20:28:48 +08:00
|
|
|
|
2019-11-09 21:02:52 +08:00
|
|
|
ocelot->lags[n] = ocelot->lags[port];
|
|
|
|
ocelot->lags[port] = 0;
|
2018-06-26 20:28:48 +08:00
|
|
|
|
|
|
|
ocelot_setup_lag(ocelot, n);
|
|
|
|
}
|
|
|
|
|
2019-11-09 21:02:52 +08:00
|
|
|
port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, port);
|
2018-06-26 20:28:48 +08:00
|
|
|
port_cfg &= ~ANA_PORT_PORT_CFG_PORTID_VAL_M;
|
2019-11-09 21:02:52 +08:00
|
|
|
ocelot_write_gix(ocelot, port_cfg | ANA_PORT_PORT_CFG_PORTID_VAL(port),
|
|
|
|
ANA_PORT_PORT_CFG, port);
|
2018-06-26 20:28:48 +08:00
|
|
|
|
|
|
|
ocelot_set_aggr_pgids(ocelot);
|
|
|
|
}
|
2020-06-20 23:43:44 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_lag_leave);
|
2018-11-23 07:30:11 +08:00
|
|
|
|
2020-03-10 09:28:18 +08:00
|
|
|
/* Configure the maximum SDU (L2 payload) on RX to the value specified in @sdu.
|
|
|
|
* The length of VLAN tags is accounted for automatically via DEV_MAC_TAGS_CFG.
|
2020-03-28 03:55:47 +08:00
|
|
|
* In the special case that it's the NPI port that we're configuring, the
|
|
|
|
* length of the tag and optional prefix needs to be accounted for privately,
|
|
|
|
* in order to be able to sustain communication at the requested @sdu.
|
2020-03-10 09:28:18 +08:00
|
|
|
*/
|
2020-03-28 03:55:47 +08:00
|
|
|
void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu)
|
2019-11-09 21:02:56 +08:00
|
|
|
{
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
2020-03-10 09:28:18 +08:00
|
|
|
int maxlen = sdu + ETH_HLEN + ETH_FCS_LEN;
|
2020-07-14 00:57:05 +08:00
|
|
|
int pause_start, pause_stop;
|
2020-10-05 17:09:11 +08:00
|
|
|
int atop, atop_tot;
|
2019-11-09 21:02:56 +08:00
|
|
|
|
2020-03-28 03:55:47 +08:00
|
|
|
if (port == ocelot->npi) {
|
|
|
|
maxlen += OCELOT_TAG_LEN;
|
|
|
|
|
|
|
|
if (ocelot->inj_prefix == OCELOT_TAG_PREFIX_SHORT)
|
|
|
|
maxlen += OCELOT_SHORT_PREFIX_LEN;
|
|
|
|
else if (ocelot->inj_prefix == OCELOT_TAG_PREFIX_LONG)
|
|
|
|
maxlen += OCELOT_LONG_PREFIX_LEN;
|
|
|
|
}
|
|
|
|
|
2020-03-10 09:28:18 +08:00
|
|
|
ocelot_port_writel(ocelot_port, maxlen, DEV_MAC_MAXLEN_CFG);
|
2019-11-14 23:03:23 +08:00
|
|
|
|
2020-07-14 00:57:05 +08:00
|
|
|
/* Set Pause watermark hysteresis */
|
|
|
|
pause_start = 6 * maxlen / OCELOT_BUFFER_CELL_SZ;
|
|
|
|
pause_stop = 4 * maxlen / OCELOT_BUFFER_CELL_SZ;
|
2020-07-14 00:57:07 +08:00
|
|
|
ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_START,
|
|
|
|
pause_start);
|
|
|
|
ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_STOP,
|
|
|
|
pause_stop);
|
2019-11-14 23:03:23 +08:00
|
|
|
|
2020-10-05 17:09:11 +08:00
|
|
|
/* Tail dropping watermarks */
|
|
|
|
atop_tot = (ocelot->shared_queue_sz - 9 * maxlen) /
|
2020-03-10 09:28:18 +08:00
|
|
|
OCELOT_BUFFER_CELL_SZ;
|
2020-10-05 17:09:11 +08:00
|
|
|
atop = (9 * maxlen) / OCELOT_BUFFER_CELL_SZ;
|
|
|
|
ocelot_write_rix(ocelot, ocelot->ops->wm_enc(atop), SYS_ATOP, port);
|
|
|
|
ocelot_write(ocelot, ocelot->ops->wm_enc(atop_tot), SYS_ATOP_TOT_CFG);
|
2019-11-14 23:03:23 +08:00
|
|
|
}
|
2020-03-28 03:55:47 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_port_set_maxlen);
|
|
|
|
|
|
|
|
int ocelot_get_max_mtu(struct ocelot *ocelot, int port)
|
|
|
|
{
|
|
|
|
int max_mtu = 65535 - ETH_HLEN - ETH_FCS_LEN;
|
|
|
|
|
|
|
|
if (port == ocelot->npi) {
|
|
|
|
max_mtu -= OCELOT_TAG_LEN;
|
|
|
|
|
|
|
|
if (ocelot->inj_prefix == OCELOT_TAG_PREFIX_SHORT)
|
|
|
|
max_mtu -= OCELOT_SHORT_PREFIX_LEN;
|
|
|
|
else if (ocelot->inj_prefix == OCELOT_TAG_PREFIX_LONG)
|
|
|
|
max_mtu -= OCELOT_LONG_PREFIX_LEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
return max_mtu;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_get_max_mtu);
|
2019-11-14 23:03:23 +08:00
|
|
|
|
2019-11-14 23:03:27 +08:00
|
|
|
void ocelot_init_port(struct ocelot *ocelot, int port)
|
2019-11-14 23:03:23 +08:00
|
|
|
{
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
|
|
|
2019-11-27 15:27:57 +08:00
|
|
|
skb_queue_head_init(&ocelot_port->tx_skbs);
|
2020-09-18 09:07:24 +08:00
|
|
|
spin_lock_init(&ocelot_port->ts_id_lock);
|
2019-11-09 21:02:56 +08:00
|
|
|
|
|
|
|
/* Basic L2 initialization */
|
|
|
|
|
2019-11-14 23:03:22 +08:00
|
|
|
/* Set MAC IFG Gaps
|
|
|
|
* FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 0
|
|
|
|
* !FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 5
|
|
|
|
*/
|
|
|
|
ocelot_port_writel(ocelot_port, DEV_MAC_IFG_CFG_TX_IFG(5),
|
|
|
|
DEV_MAC_IFG_CFG);
|
|
|
|
|
|
|
|
/* Load seed (0) and set MAC HDX late collision */
|
|
|
|
ocelot_port_writel(ocelot_port, DEV_MAC_HDX_CFG_LATE_COL_POS(67) |
|
|
|
|
DEV_MAC_HDX_CFG_SEED_LOAD,
|
|
|
|
DEV_MAC_HDX_CFG);
|
|
|
|
mdelay(1);
|
|
|
|
ocelot_port_writel(ocelot_port, DEV_MAC_HDX_CFG_LATE_COL_POS(67),
|
|
|
|
DEV_MAC_HDX_CFG);
|
|
|
|
|
|
|
|
/* Set Max Length and maximum tags allowed */
|
2020-03-10 09:28:18 +08:00
|
|
|
ocelot_port_set_maxlen(ocelot, port, ETH_DATA_LEN);
|
2019-11-14 23:03:22 +08:00
|
|
|
ocelot_port_writel(ocelot_port, DEV_MAC_TAGS_CFG_TAG_ID(ETH_P_8021AD) |
|
|
|
|
DEV_MAC_TAGS_CFG_VLAN_AWR_ENA |
|
2020-03-10 09:28:18 +08:00
|
|
|
DEV_MAC_TAGS_CFG_VLAN_DBL_AWR_ENA |
|
2019-11-14 23:03:22 +08:00
|
|
|
DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA,
|
|
|
|
DEV_MAC_TAGS_CFG);
|
|
|
|
|
|
|
|
/* Set SMAC of Pause frame (00:00:00:00:00:00) */
|
|
|
|
ocelot_port_writel(ocelot_port, 0, DEV_MAC_FC_MAC_HIGH_CFG);
|
|
|
|
ocelot_port_writel(ocelot_port, 0, DEV_MAC_FC_MAC_LOW_CFG);
|
|
|
|
|
2020-07-14 00:57:05 +08:00
|
|
|
/* Enable transmission of pause frames */
|
2020-07-14 00:57:07 +08:00
|
|
|
ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 1);
|
2020-07-14 00:57:05 +08:00
|
|
|
|
2019-11-09 21:02:56 +08:00
|
|
|
/* Drop frames with multicast source address */
|
|
|
|
ocelot_rmw_gix(ocelot, ANA_PORT_DROP_CFG_DROP_MC_SMAC_ENA,
|
|
|
|
ANA_PORT_DROP_CFG_DROP_MC_SMAC_ENA,
|
|
|
|
ANA_PORT_DROP_CFG, port);
|
|
|
|
|
|
|
|
/* Set default VLAN and tag type to 8021Q. */
|
|
|
|
ocelot_rmw_gix(ocelot, REW_PORT_VLAN_CFG_PORT_TPID(ETH_P_8021Q),
|
|
|
|
REW_PORT_VLAN_CFG_PORT_TPID_M,
|
|
|
|
REW_PORT_VLAN_CFG, port);
|
|
|
|
|
|
|
|
/* Enable vcap lookups */
|
|
|
|
ocelot_vcap_enable(ocelot, port);
|
|
|
|
}
|
2019-11-14 23:03:27 +08:00
|
|
|
EXPORT_SYMBOL(ocelot_init_port);
|
2019-11-09 21:02:56 +08:00
|
|
|
|
2020-09-27 03:32:01 +08:00
|
|
|
/* Configure and enable the CPU port module, which is a set of queues
|
|
|
|
* accessible through register MMIO, frame DMA or Ethernet (in case
|
|
|
|
* NPI mode is used).
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
*/
|
2020-09-27 03:32:01 +08:00
|
|
|
static void ocelot_cpu_port_init(struct ocelot *ocelot)
|
2019-11-09 21:03:00 +08:00
|
|
|
{
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
int cpu = ocelot->num_phys_ports;
|
|
|
|
|
|
|
|
/* The unicast destination PGID for the CPU port module is unused */
|
2019-11-09 21:03:00 +08:00
|
|
|
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, cpu);
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
/* Instead set up a multicast destination PGID for traffic copied to
|
|
|
|
* the CPU. Whitelisted MAC addresses like the port netdevice MAC
|
|
|
|
* addresses will be copied to the CPU via this PGID.
|
|
|
|
*/
|
2019-11-09 21:03:00 +08:00
|
|
|
ocelot_write_rix(ocelot, BIT(cpu), ANA_PGID_PGID, PGID_CPU);
|
|
|
|
ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_RECV_ENA |
|
|
|
|
ANA_PORT_PORT_CFG_PORTID_VAL(cpu),
|
|
|
|
ANA_PORT_PORT_CFG, cpu);
|
|
|
|
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
/* Enable CPU port module */
|
net: mscc: ocelot: convert QSYS_SWITCH_PORT_MODE and SYS_PORT_MODE to regfields
Currently Felix and Ocelot share the same bit layout in these per-port
registers, but Seville does not. So we need reg_fields for that.
Actually since these are per-port registers, we need to also specify the
number of ports, and register size per port, and use the regmap API for
multiple ports.
There's a more subtle point to be made about the other 2 register
fields:
- QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG
- QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE
which we are not writing any longer, for 2 reasons:
- Using the previous API (ocelot_write_rix), we were only writing 1 for
Felix and Ocelot, which was their hardware-default value, and which
there wasn't any intention in changing.
- In the case of SCH_NEXT_CFG, in fact Seville does not have this
register field at all, and therefore, if we want to have common code
we would be required to not write to it.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-07-14 00:57:03 +08:00
|
|
|
ocelot_fields_write(ocelot, cpu, QSYS_SWITCH_PORT_MODE_PORT_ENA, 1);
|
net: mscc: ocelot: eliminate confusion between CPU and NPI port
Ocelot has the concept of a CPU port. The CPU port is represented in the
forwarding and the queueing system, but it is not a physical device. The
CPU port can either be accessed via register-based injection/extraction
(which is the case of Ocelot), via Frame-DMA (similar to the first one),
or "connected" to a physical Ethernet port (called NPI in the datasheet)
which is the case of the Felix DSA switch.
In Ocelot the CPU port is at index 11.
In Felix the CPU port is at index 6.
The CPU bit is treated special in the forwarding, as it is never cleared
from the forwarding port mask (once added to it). Other than that, it is
treated the same as a normal front port.
Both Felix and Ocelot should use the CPU port in the same way. This
means that Felix should not use the NPI port directly when forwarding to
the CPU, but instead use the CPU port.
This patch is fixing this such that Felix will use port 6 as its CPU
port, and just use the NPI port to carry the traffic.
Therefore, eliminate the "ocelot->cpu" variable which was holding the
index of the NPI port for Felix, and the index of the CPU port module
for Ocelot, so the variable was actually configuring different things
for different drivers and causing at least part of the confusion.
Also remove the "ocelot->num_cpu_ports" variable, which is the result of
another confusion. The 2 CPU ports mentioned in the datasheet are
because there are two frame extraction channels (register based or DMA
based). This is of no relevance to the driver at the moment, and
invisible to the analyzer module.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Suggested-by: Allan W. Nielsen <allan.nielsen@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-02-29 22:50:02 +08:00
|
|
|
/* CPU port Injection/Extraction configuration */
|
net: mscc: ocelot: convert QSYS_SWITCH_PORT_MODE and SYS_PORT_MODE to regfields
Currently Felix and Ocelot share the same bit layout in these per-port
registers, but Seville does not. So we need reg_fields for that.
Actually since these are per-port registers, we need to also specify the
number of ports, and register size per port, and use the regmap API for
multiple ports.
There's a more subtle point to be made about the other 2 register
fields:
- QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG
- QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE
which we are not writing any longer, for 2 reasons:
- Using the previous API (ocelot_write_rix), we were only writing 1 for
Felix and Ocelot, which was their hardware-default value, and which
there wasn't any intention in changing.
- In the case of SCH_NEXT_CFG, in fact Seville does not have this
register field at all, and therefore, if we want to have common code
we would be required to not write to it.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-07-14 00:57:03 +08:00
|
|
|
ocelot_fields_write(ocelot, cpu, SYS_PORT_MODE_INCL_XTR_HDR,
|
2020-09-27 03:32:01 +08:00
|
|
|
ocelot->xtr_prefix);
|
net: mscc: ocelot: convert QSYS_SWITCH_PORT_MODE and SYS_PORT_MODE to regfields
Currently Felix and Ocelot share the same bit layout in these per-port
registers, but Seville does not. So we need reg_fields for that.
Actually since these are per-port registers, we need to also specify the
number of ports, and register size per port, and use the regmap API for
multiple ports.
There's a more subtle point to be made about the other 2 register
fields:
- QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG
- QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE
which we are not writing any longer, for 2 reasons:
- Using the previous API (ocelot_write_rix), we were only writing 1 for
Felix and Ocelot, which was their hardware-default value, and which
there wasn't any intention in changing.
- In the case of SCH_NEXT_CFG, in fact Seville does not have this
register field at all, and therefore, if we want to have common code
we would be required to not write to it.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2020-07-14 00:57:03 +08:00
|
|
|
ocelot_fields_write(ocelot, cpu, SYS_PORT_MODE_INCL_INJ_HDR,
|
2020-09-27 03:32:01 +08:00
|
|
|
ocelot->inj_prefix);
|
2019-11-09 21:03:00 +08:00
|
|
|
|
|
|
|
/* Configure the CPU port to be VLAN aware */
|
|
|
|
ocelot_write_gix(ocelot, ANA_PORT_VLAN_CFG_VLAN_VID(0) |
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
|
|
|
|
ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1),
|
|
|
|
ANA_PORT_VLAN_CFG, cpu);
|
|
|
|
}
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
int ocelot_init(struct ocelot *ocelot)
|
|
|
|
{
|
|
|
|
char queue_name[32];
|
2019-11-09 21:03:00 +08:00
|
|
|
int i, ret;
|
|
|
|
u32 port;
|
2018-05-15 04:04:57 +08:00
|
|
|
|
2019-11-14 23:03:26 +08:00
|
|
|
if (ocelot->ops->reset) {
|
|
|
|
ret = ocelot->ops->reset(ocelot);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(ocelot->dev, "Switch reset failed\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-26 20:28:48 +08:00
|
|
|
ocelot->lags = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
|
|
|
|
sizeof(u32), GFP_KERNEL);
|
|
|
|
if (!ocelot->lags)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
ocelot->stats = devm_kcalloc(ocelot->dev,
|
|
|
|
ocelot->num_phys_ports * ocelot->num_stats,
|
|
|
|
sizeof(u64), GFP_KERNEL);
|
|
|
|
if (!ocelot->stats)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
mutex_init(&ocelot->stats_lock);
|
2019-08-12 22:45:37 +08:00
|
|
|
mutex_init(&ocelot->ptp_lock);
|
|
|
|
spin_lock_init(&ocelot->ptp_clock_lock);
|
2018-05-15 04:04:57 +08:00
|
|
|
snprintf(queue_name, sizeof(queue_name), "%s-stats",
|
|
|
|
dev_name(ocelot->dev));
|
|
|
|
ocelot->stats_queue = create_singlethread_workqueue(queue_name);
|
|
|
|
if (!ocelot->stats_queue)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2019-11-09 21:02:58 +08:00
|
|
|
INIT_LIST_HEAD(&ocelot->multicast);
|
net: mscc: ocelot: support L2 multicast entries
There is one main difference in mscc_ocelot between IP multicast and L2
multicast. With IP multicast, destination ports are encoded into the
upper bytes of the multicast MAC address. Example: to deliver the
address 01:00:5E:11:22:33 to ports 3, 8, and 9, one would need to
program the address of 00:03:08:11:22:33 into hardware. Whereas for L2
multicast, the MAC table entry points to a Port Group ID (PGID), and
that PGID contains the port mask that the packet will be forwarded to.
As to why it is this way, no clue. My guess is that not all port
combinations can be supported simultaneously with the limited number of
PGIDs, and this was somehow an issue for IP multicast but not for L2
multicast. Anyway.
Prior to this change, the raw L2 multicast code was bogus, due to the
fact that there wasn't really any way to test it using the bridge code.
There were 2 issues:
- A multicast PGID was allocated for each MDB entry, but it wasn't in
fact programmed to hardware. It was dummy.
- In fact we don't want to reserve a multicast PGID for every single MDB
entry. That would be odd because we can only have ~60 PGIDs, but
thousands of MDB entries. So instead, we want to reserve a multicast
PGID for every single port combination for multicast traffic. And
since we can have 2 (or more) MDB entries delivered to the same port
group (and therefore PGID), we need to reference-count the PGIDs.
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2020-10-29 10:27:38 +08:00
|
|
|
INIT_LIST_HEAD(&ocelot->pgids);
|
2018-05-15 04:04:57 +08:00
|
|
|
ocelot_mact_init(ocelot);
|
|
|
|
ocelot_vlan_init(ocelot);
|
2020-06-20 23:43:46 +08:00
|
|
|
ocelot_vcap_init(ocelot);
|
2020-09-27 03:32:01 +08:00
|
|
|
ocelot_cpu_port_init(ocelot);
|
2018-05-15 04:04:57 +08:00
|
|
|
|
|
|
|
for (port = 0; port < ocelot->num_phys_ports; port++) {
|
|
|
|
/* Clear all counters (5 groups) */
|
|
|
|
ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port) |
|
|
|
|
SYS_STAT_CFG_STAT_CLEAR_SHOT(0x7f),
|
|
|
|
SYS_STAT_CFG);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Only use S-Tag */
|
|
|
|
ocelot_write(ocelot, ETH_P_8021AD, SYS_VLAN_ETYPE_CFG);
|
|
|
|
|
|
|
|
/* Aggregation mode */
|
|
|
|
ocelot_write(ocelot, ANA_AGGR_CFG_AC_SMAC_ENA |
|
|
|
|
ANA_AGGR_CFG_AC_DMAC_ENA |
|
|
|
|
ANA_AGGR_CFG_AC_IP4_SIPDIP_ENA |
|
|
|
|
ANA_AGGR_CFG_AC_IP4_TCPUDP_ENA, ANA_AGGR_CFG);
|
|
|
|
|
|
|
|
/* Set MAC age time to default value. The entry is aged after
|
|
|
|
* 2*AGE_PERIOD
|
|
|
|
*/
|
|
|
|
ocelot_write(ocelot,
|
|
|
|
ANA_AUTOAGE_AGE_PERIOD(BR_DEFAULT_AGEING_TIME / 2 / HZ),
|
|
|
|
ANA_AUTOAGE);
|
|
|
|
|
|
|
|
/* Disable learning for frames discarded by VLAN ingress filtering */
|
|
|
|
regmap_field_write(ocelot->regfields[ANA_ADVLEARN_VLAN_CHK], 1);
|
|
|
|
|
|
|
|
/* Setup frame ageing - fixed value "2 sec" - in 6.5 us units */
|
|
|
|
ocelot_write(ocelot, SYS_FRM_AGING_AGE_TX_ENA |
|
|
|
|
SYS_FRM_AGING_MAX_AGE(307692), SYS_FRM_AGING);
|
|
|
|
|
|
|
|
/* Setup flooding PGIDs */
|
|
|
|
ocelot_write_rix(ocelot, ANA_FLOODING_FLD_MULTICAST(PGID_MC) |
|
|
|
|
ANA_FLOODING_FLD_BROADCAST(PGID_MC) |
|
|
|
|
ANA_FLOODING_FLD_UNICAST(PGID_UC),
|
|
|
|
ANA_FLOODING, 0);
|
|
|
|
ocelot_write(ocelot, ANA_FLOODING_IPMC_FLD_MC6_DATA(PGID_MCIPV6) |
|
|
|
|
ANA_FLOODING_IPMC_FLD_MC6_CTRL(PGID_MC) |
|
|
|
|
ANA_FLOODING_IPMC_FLD_MC4_DATA(PGID_MCIPV4) |
|
|
|
|
ANA_FLOODING_IPMC_FLD_MC4_CTRL(PGID_MC),
|
|
|
|
ANA_FLOODING_IPMC);
|
|
|
|
|
|
|
|
for (port = 0; port < ocelot->num_phys_ports; port++) {
|
|
|
|
/* Transmit the frame to the local port. */
|
|
|
|
ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, port);
|
|
|
|
/* Do not forward BPDU frames to the front ports. */
|
|
|
|
ocelot_write_gix(ocelot,
|
|
|
|
ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0xffff),
|
|
|
|
ANA_PORT_CPU_FWD_BPDU_CFG,
|
|
|
|
port);
|
|
|
|
/* Ensure bridging is disabled */
|
|
|
|
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_SRC + port);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allow broadcast MAC frames. */
|
2020-06-21 19:46:02 +08:00
|
|
|
for_each_nonreserved_multicast_dest_pgid(ocelot, i) {
|
2018-05-15 04:04:57 +08:00
|
|
|
u32 val = ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports - 1, 0));
|
|
|
|
|
|
|
|
ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i);
|
|
|
|
}
|
|
|
|
ocelot_write_rix(ocelot,
|
|
|
|
ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports, 0)),
|
|
|
|
ANA_PGID_PGID, PGID_MC);
|
|
|
|
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV4);
|
|
|
|
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV6);
|
|
|
|
|
|
|
|
/* Allow manual injection via DEVCPU_QS registers, and byte swap these
|
|
|
|
* registers endianness.
|
|
|
|
*/
|
|
|
|
ocelot_write_rix(ocelot, QS_INJ_GRP_CFG_BYTE_SWAP |
|
|
|
|
QS_INJ_GRP_CFG_MODE(1), QS_INJ_GRP_CFG, 0);
|
|
|
|
ocelot_write_rix(ocelot, QS_XTR_GRP_CFG_BYTE_SWAP |
|
|
|
|
QS_XTR_GRP_CFG_MODE(1), QS_XTR_GRP_CFG, 0);
|
|
|
|
ocelot_write(ocelot, ANA_CPUQ_CFG_CPUQ_MIRROR(2) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_LRN(2) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_MAC_COPY(2) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_SRC_COPY(2) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_LOCKED_PORTMOVE(2) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_ALLBRIDGE(6) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_IPMC_CTRL(6) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_IGMP(6) |
|
|
|
|
ANA_CPUQ_CFG_CPUQ_MLD(6), ANA_CPUQ_CFG);
|
|
|
|
for (i = 0; i < 16; i++)
|
|
|
|
ocelot_write_rix(ocelot, ANA_CPUQ_8021_CFG_CPUQ_GARP_VAL(6) |
|
|
|
|
ANA_CPUQ_8021_CFG_CPUQ_BPDU_VAL(6),
|
|
|
|
ANA_CPUQ_8021_CFG, i);
|
|
|
|
|
2019-04-16 22:51:59 +08:00
|
|
|
INIT_DELAYED_WORK(&ocelot->stats_work, ocelot_check_stats_work);
|
2018-05-15 04:04:57 +08:00
|
|
|
queue_delayed_work(ocelot->stats_queue, &ocelot->stats_work,
|
|
|
|
OCELOT_STATS_CHECK_DELAY);
|
2019-08-12 22:45:37 +08:00
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_init);
|
|
|
|
|
|
|
|
void ocelot_deinit(struct ocelot *ocelot)
|
|
|
|
{
|
2019-07-25 21:33:18 +08:00
|
|
|
cancel_delayed_work(&ocelot->stats_work);
|
2018-05-15 04:04:57 +08:00
|
|
|
destroy_workqueue(ocelot->stats_queue);
|
|
|
|
mutex_destroy(&ocelot->stats_lock);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_deinit);
|
|
|
|
|
2020-09-18 09:07:30 +08:00
|
|
|
void ocelot_deinit_port(struct ocelot *ocelot, int port)
|
|
|
|
{
|
|
|
|
struct ocelot_port *ocelot_port = ocelot->ports[port];
|
|
|
|
|
|
|
|
skb_queue_purge(&ocelot_port->tx_skbs);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ocelot_deinit_port);
|
|
|
|
|
2018-05-15 04:04:57 +08:00
|
|
|
MODULE_LICENSE("Dual MIT/GPL");
|