Re: [PATCH net-next] net: dsa: mv88e6xxx: Allow dynamic reconfiguration of tag protocol

Tobias Waldekranz
2021-03-23T14:50:00Z

On Tue, Mar 23, 2021 at 13:35, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Tue, Mar 23, 2021 at 11:23:26AM +0100, Tobias Waldekranz wrote:
>> All devices are capable of using regular DSA tags. Support for
>> Ethertyped DSA tags sort into three categories:
>>
>> 1. No support. Older chips fall into this category.
>>
>> 2. Full support. Datasheet explicitly supports configuring the CPU
>> port to receive FORWARDs with a DSA tag.
>>
>> 3. Undocumented support. Datasheet lists the configuration from
>> category 2 as "reserved for future use", but does empirically
>> behave like a category 2 device.
>>
>> Because there are ethernet controllers that do not handle regular DSA
>> tags in all cases, it is sometimes preferable to rely on the
>> undocumented behavior, as the alternative is a very crippled
>> system. But, in those cases, make sure to log the fact that an
>> undocumented feature has been enabled.
>>
>> Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
>> ---
>>
>> In a system using an NXP T1023 SoC connected to a 6390X switch, we
>> noticed that TO_CPU frames where not reaching the CPU. This only
>> happened on hardware port 8. Looking at the DSA master interface
>> (dpaa-ethernet) we could see that an Rx error counter was bumped at
>> the same rate. The logs indicated a parser error.
>>
>> It just so happens that a TO_CPU coming in on device 0, port 8, will
>> result in the first two bytes of the DSA tag being one of:
>>
>> 00 40
>> 00 44
>> 00 46
>>
>> My guess is that since these values look like 802.3 length fields, the
>> controller's parser will signal an error if the frame length does not
>> match what is in the header.
>
> Interesting assumption.
> Could you please try this patch out, just for my amusement? It is only
> compile-tested.
>
> -----------------------------[ cut here ]-----------------------------
> From ab75b63d1bfeccc3032060e6e6dbd2ea8f1d31ed Mon Sep 17 00:00:00 2001
> From: Vladimir Oltean <vladimir.oltean@nxp.com>
> Date: Tue, 23 Mar 2021 13:03:34 +0200
> Subject: [PATCH] fsl/fman: ignore RX parse errors on ports that are DSA
> masters
>
> Tobias reports that when an FMan port receives a Marvell DSA tagged
> frame (normal/legacy tag, not EtherType tag) which is a TO_CPU frame
> coming in from device 0, port 8, that frame will be dropped.
>
> It appears that the first two bytes of this particular DSA tag (which
> overlap with what the FMan parser interprets as an EtherType/Length
> field) look like one of the possible values below:
>
> 00 40
> 00 44
> 00 46
>
> Reported-by: Tobias Waldekranz <tobias@waldekranz.com>
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> .../net/ethernet/freescale/dpaa/dpaa_eth.c | 65 ++++++++++---------
> .../net/ethernet/freescale/fman/fman_port.c | 8 ++-
> .../net/ethernet/freescale/fman/fman_port.h | 2 +-
> 3 files changed, 42 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> index 720dc99bd1fc..069d38cd63c5 100644
> --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
> @@ -55,6 +55,7 @@
> #include <linux/phy_fixed.h>
> #include <linux/bpf.h>
> #include <linux/bpf_trace.h>
> +#include <net/dsa.h>
> #include <soc/fsl/bman.h>
> #include <soc/fsl/qman.h>
> #include "fman.h"
> @@ -2447,34 +2448,6 @@ static inline int dpaa_eth_napi_schedule(struct dpaa_percpu_priv *percpu_priv,
> return 0;
> }
>
> -static enum qman_cb_dqrr_result rx_error_dqrr(struct qman_portal *portal,
> - struct qman_fq *fq,
> - const struct qm_dqrr_entry *dq,
> - bool sched_napi)
> -{
> - struct dpaa_fq *dpaa_fq = container_of(fq, struct dpaa_fq, fq_base);
> - struct dpaa_percpu_priv *percpu_priv;
> - struct net_device *net_dev;
> - struct dpaa_bp *dpaa_bp;
> - struct dpaa_priv *priv;
> -
> - net_dev = dpaa_fq->net_dev;
> - priv = netdev_priv(net_dev);
> - dpaa_bp = dpaa_bpid2pool(dq->fd.bpid);
> - if (!dpaa_bp)
> - return qman_cb_dqrr_consume;
> -
> - percpu_priv = this_cpu_ptr(priv->percpu_priv);
> -
> - if (dpaa_eth_napi_schedule(percpu_priv, portal, sched_napi))
> - return qman_cb_dqrr_stop;
> -
> - dpaa_eth_refill_bpools(priv);
> - dpaa_rx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid);
> -
> - return qman_cb_dqrr_consume;
> -}
> -
> static int dpaa_xdp_xmit_frame(struct net_device *net_dev,
> struct xdp_frame *xdpf)
> {
> @@ -2699,7 +2672,7 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal,
> return qman_cb_dqrr_consume;
> }
>
> - if (unlikely(fd_status & FM_FD_STAT_RX_ERRORS) != 0) {
> + if (!netdev_uses_dsa(net_dev) && unlikely(fd_status & FM_FD_STAT_RX_ERRORS) != 0) {
> if (net_ratelimit())
> netif_warn(priv, hw, net_dev, "FD status = 0x%08x\n",
> fd_status & FM_FD_STAT_RX_ERRORS);
> @@ -2802,6 +2775,37 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal,
> return qman_cb_dqrr_consume;
> }
>
> +static enum qman_cb_dqrr_result rx_error_dqrr(struct qman_portal *portal,
> + struct qman_fq *fq,
> + const struct qm_dqrr_entry *dq,
> + bool sched_napi)
> +{
> + struct dpaa_fq *dpaa_fq = container_of(fq, struct dpaa_fq, fq_base);
> + struct dpaa_percpu_priv *percpu_priv;
> + struct net_device *net_dev;
> + struct dpaa_bp *dpaa_bp;
> + struct dpaa_priv *priv;
> +
> + net_dev = dpaa_fq->net_dev;
> + if (netdev_uses_dsa(net_dev))
> + return rx_default_dqrr(portal, fq, dq, sched_napi);
> +
> + priv = netdev_priv(net_dev);
> + dpaa_bp = dpaa_bpid2pool(dq->fd.bpid);
> + if (!dpaa_bp)
> + return qman_cb_dqrr_consume;
> +
> + percpu_priv = this_cpu_ptr(priv->percpu_priv);
> +
> + if (dpaa_eth_napi_schedule(percpu_priv, portal, sched_napi))
> + return qman_cb_dqrr_stop;
> +
> + dpaa_eth_refill_bpools(priv);
> + dpaa_rx_error(net_dev, priv, percpu_priv, &dq->fd, fq->fqid);
> +
> + return qman_cb_dqrr_consume;
> +}
> +
> static enum qman_cb_dqrr_result conf_error_dqrr(struct qman_portal *portal,
> struct qman_fq *fq,
> const struct qm_dqrr_entry *dq,
> @@ -2955,6 +2959,7 @@ static int dpaa_phy_init(struct net_device *net_dev)
>
> static int dpaa_open(struct net_device *net_dev)
> {
> + bool ignore_errors = netdev_uses_dsa(net_dev);
> struct mac_device *mac_dev;
> struct dpaa_priv *priv;
> int err, i;
> @@ -2968,7 +2973,7 @@ static int dpaa_open(struct net_device *net_dev)
> goto phy_init_failed;
>
> for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) {
> - err = fman_port_enable(mac_dev->port[i]);
> + err = fman_port_enable(mac_dev->port[i], ignore_errors);
> if (err)
> goto mac_start_failed;
> }
> diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c
> index d9baac0dbc7d..763faec11f5c 100644
> --- a/drivers/net/ethernet/freescale/fman/fman_port.c
> +++ b/drivers/net/ethernet/freescale/fman/fman_port.c
> @@ -106,6 +106,7 @@
> #define BMI_EBD_EN 0x80000000
>
> #define BMI_PORT_CFG_EN 0x80000000
> +#define BMI_PORT_CFG_FDOVR 0x02000000
>
> #define BMI_PORT_STATUS_BSY 0x80000000
>
> @@ -1629,7 +1630,7 @@ int fman_port_disable(struct fman_port *port)
> }
>
> /* Disable BMI */
> - tmp = ioread32be(bmi_cfg_reg) & ~BMI_PORT_CFG_EN;
> + tmp = ioread32be(bmi_cfg_reg) & ~(BMI_PORT_CFG_EN | BMI_PORT_CFG_FDOVR);
> iowrite32be(tmp, bmi_cfg_reg);
>
> /* Wait for graceful stop end */
> @@ -1655,6 +1656,7 @@ EXPORT_SYMBOL(fman_port_disable);
> /**
> * fman_port_enable
> * @port: A pointer to a FM Port module.
> + * @ignore_errors: If set, do not discard frames received with errors.
> *
> * A runtime routine provided to allow disable/enable of port.
> *
> @@ -1662,7 +1664,7 @@ EXPORT_SYMBOL(fman_port_disable);
> *
> * Return: 0 on success; Error code otherwise.
> */
> -int fman_port_enable(struct fman_port *port)
> +int fman_port_enable(struct fman_port *port, bool ignore_errors)
> {
> u32 __iomem *bmi_cfg_reg;
> u32 tmp;
> @@ -1692,6 +1694,8 @@ int fman_port_enable(struct fman_port *port)
>
> /* Enable BMI */
> tmp = ioread32be(bmi_cfg_reg) | BMI_PORT_CFG_EN;
> + if (ignore_errors)
> + tmp |= BMI_PORT_CFG_FDOVR;
> iowrite32be(tmp, bmi_cfg_reg);
>
> return 0;
> diff --git a/drivers/net/ethernet/freescale/fman/fman_port.h b/drivers/net/ethernet/freescale/fman/fman_port.h
> index 82f12661a46d..0928361b0e73 100644
> --- a/drivers/net/ethernet/freescale/fman/fman_port.h
> +++ b/drivers/net/ethernet/freescale/fman/fman_port.h
> @@ -147,7 +147,7 @@ int fman_port_cfg_buf_prefix_content(struct fman_port *port,
>
> int fman_port_disable(struct fman_port *port);
>
> -int fman_port_enable(struct fman_port *port);
> +int fman_port_enable(struct fman_port *port, bool ignore_errors);
>
> u32 fman_port_get_qman_channel_id(struct fman_port *port);
>
> -----------------------------[ cut here ]-----------------------------
>
> The netdev_uses_dsa thing is a bit trashy, I think that a more polished
> version should rather set NETIF_F_RXALL for the DSA master, and have the
> dpaa driver act upon that. But first I'm curious if it works.

It does work. Thank you!

Does setting RXALL mean that the master would accept frames with a bad
FCS as well? If so, would that mean that we would have to verify it in
software?

>>
>> As a workaround, switching to EDSA (thereby always having a proper
>> EtherType in the frame) solves the issue.
>
> So basically every user needs to change the tag protocol manually to be
> able to receive from port 8? Not sure if that's too friendly.

No it is not friendly at all. My goal was to add it as a device-tree
property, but for reasons I will detail in my answer to Andrew, I did
not manage to figure out a good way to do that. Happy to take
suggestions.

>> drivers/net/dsa/mv88e6xxx/chip.c | 41 +++++++++++++++++++++++++++++---
>> drivers/net/dsa/mv88e6xxx/chip.h | 3 +++
>> 2 files changed, 41 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
>> index 95f07fcd4f85..e7ec883d5f6b 100644
>> --- a/drivers/net/dsa/mv88e6xxx/chip.c
>> +++ b/drivers/net/dsa/mv88e6xxx/chip.c
>> @@ -2531,10 +2531,10 @@ static int mv88e6xxx_setup_port_mode(struct mv88e6xxx_chip *chip, int port)
>> return mv88e6xxx_set_port_mode_normal(chip, port);
>>
>> /* Setup CPU port mode depending on its supported tag format */
>> - if (chip->info->tag_protocol == DSA_TAG_PROTO_DSA)
>> + if (chip->tag_protocol == DSA_TAG_PROTO_DSA)
>> return mv88e6xxx_set_port_mode_dsa(chip, port);
>>
>> - if (chip->info->tag_protocol == DSA_TAG_PROTO_EDSA)
>> + if (chip->tag_protocol == DSA_TAG_PROTO_EDSA)
>> return mv88e6xxx_set_port_mode_edsa(chip, port);
>>
>> return -EINVAL;
>> @@ -5564,7 +5564,39 @@ static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds,
>> {
>> struct mv88e6xxx_chip *chip = ds->priv;
>>
>> - return chip->info->tag_protocol;
>> + return chip->tag_protocol;
>> +}
>> +
>> +static int mv88e6xxx_change_tag_protocol(struct dsa_switch *ds, int port,
>> + enum dsa_tag_protocol proto)
>> +{
>> + struct mv88e6xxx_chip *chip = ds->priv;
>> + enum dsa_tag_protocol old_protocol;
>> + int err;
>> +
>> + switch (proto) {
>> + case DSA_TAG_PROTO_EDSA:
>> + if (chip->info->tag_protocol != DSA_TAG_PROTO_EDSA)
>> + dev_warn(chip->dev, "Relying on undocumented EDSA tagging behavior\n");
>> +
>> + break;
>> + case DSA_TAG_PROTO_DSA:
>> + break;
>> + default:
>> + return -EPROTONOSUPPORT;
>> + }
>> +
>> + old_protocol = chip->tag_protocol;
>> + chip->tag_protocol = proto;
>> +
>> + mv88e6xxx_reg_lock(chip);
>> + err = mv88e6xxx_setup_port_mode(chip, port);
>> + mv88e6xxx_reg_unlock(chip);
>> +
>> + if (err)
>> + chip->tag_protocol = old_protocol;
>> +
>> + return err;
>> }
>>
>> static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
>> @@ -6029,6 +6061,7 @@ static int mv88e6xxx_crosschip_lag_leave(struct dsa_switch *ds, int sw_index,
>>
>> static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
>> .get_tag_protocol = mv88e6xxx_get_tag_protocol,
>> + .change_tag_protocol = mv88e6xxx_change_tag_protocol,
>> .setup = mv88e6xxx_setup,
>> .teardown = mv88e6xxx_teardown,
>> .phylink_validate = mv88e6xxx_validate,
>> @@ -6209,6 +6242,8 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
>> if (err)
>> goto out;
>>
>> + chip->tag_protocol = chip->info->tag_protocol;
>> +
>> mv88e6xxx_phy_init(chip);
>>
>> if (chip->info->ops->get_eeprom) {
>> diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
>> index bce6e0dc8535..96b775f3fda2 100644
>> --- a/drivers/net/dsa/mv88e6xxx/chip.h
>> +++ b/drivers/net/dsa/mv88e6xxx/chip.h
>> @@ -261,6 +261,9 @@ struct mv88e6xxx_region_priv {
>> struct mv88e6xxx_chip {
>> const struct mv88e6xxx_info *info;
>>
>> + /* Currently configured tagging protocol */
>> + enum dsa_tag_protocol tag_protocol;
>> +
>> /* The dsa_switch this private structure is related to */
>> struct dsa_switch *ds;
>>
>> --
>> 2.25.1
>>