]> git.baikalelectronics.ru Git - kernel.git/commitdiff
net: dsa: propagate switchdev vlan_filtering prepare phase to drivers
authorVladimir Oltean <vladimir.oltean@nxp.com>
Fri, 2 Oct 2020 22:06:46 +0000 (01:06 +0300)
committerDavid S. Miller <davem@davemloft.net>
Mon, 5 Oct 2020 12:56:48 +0000 (05:56 -0700)
A driver may refuse to enable VLAN filtering for any reason beyond what
the DSA framework cares about, such as:
- having tc-flower rules that rely on the switch being VLAN-aware
- the particular switch does not support VLAN, even if the driver does
  (the DSA framework just checks for the presence of the .port_vlan_add
  and .port_vlan_del pointers)
- simply not supporting this configuration to be toggled at runtime

Currently, when a driver rejects a configuration it cannot support, it
does this from the commit phase, which triggers various warnings in
switchdev.

So propagate the prepare phase to drivers, to give them the ability to
refuse invalid configurations cleanly and avoid the warnings.

Since we need to modify all function prototypes and check for the
prepare phase from within the drivers, take that opportunity and move
the existing driver restrictions within the prepare phase where that is
possible and easy.

Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Cc: Hauke Mehrtens <hauke@hauke-m.de>
Cc: Woojung Huh <woojung.huh@microchip.com>
Cc: Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
Cc: Sean Wang <sean.wang@mediatek.com>
Cc: Landen Chao <Landen.Chao@mediatek.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Vivien Didelot <vivien.didelot@gmail.com>
Cc: Jonathan McDowell <noodles@earth.li>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Alexandre Belloni <alexandre.belloni@bootlin.com>
Cc: Claudiu Manoil <claudiu.manoil@nxp.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
21 files changed:
drivers/net/dsa/b53/b53_common.c
drivers/net/dsa/b53/b53_priv.h
drivers/net/dsa/dsa_loop.c
drivers/net/dsa/lantiq_gswip.c
drivers/net/dsa/microchip/ksz8795.c
drivers/net/dsa/microchip/ksz9477.c
drivers/net/dsa/mt7530.c
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/ocelot/felix.c
drivers/net/dsa/qca8k.c
drivers/net/dsa/realtek-smi-core.h
drivers/net/dsa/rtl8366.c
drivers/net/dsa/sja1105/sja1105.h
drivers/net/dsa/sja1105/sja1105_devlink.c
drivers/net/dsa/sja1105/sja1105_main.c
drivers/net/ethernet/mscc/ocelot.c
drivers/net/ethernet/mscc/ocelot_net.c
include/net/dsa.h
include/soc/mscc/ocelot.h
net/dsa/port.c
net/dsa/switch.c

index ce18ba0b74eb1e8077689b5ae3b8e0776c5a8cbf..288b5a5c3e0dbcae40788d27f9843cd06bf3624e 100644 (file)
@@ -1374,10 +1374,14 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,
 }
 EXPORT_SYMBOL(b53_phylink_mac_link_up);
 
-int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering)
+int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+                      struct switchdev_trans *trans)
 {
        struct b53_device *dev = ds->priv;
 
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
        b53_enable_vlan(dev, dev->vlan_enabled, vlan_filtering);
 
        return 0;
index 24893b59221657ddc0211c86a843df0be994d89b..7c67409bb186df99359996eb90ab3e2c1f664536 100644 (file)
@@ -347,7 +347,8 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,
                             struct phy_device *phydev,
                             int speed, int duplex,
                             bool tx_pause, bool rx_pause);
-int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering);
+int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+                      struct switchdev_trans *trans);
 int b53_vlan_prepare(struct dsa_switch *ds, int port,
                     const struct switchdev_obj_port_vlan *vlan);
 void b53_vlan_add(struct dsa_switch *ds, int port,
index b588614d1e5e28b034c97a46d36a0b4bcdddc9e4..e38906ae8f235d24b47f676eb9a0e7b060fbc22f 100644 (file)
@@ -190,7 +190,8 @@ static void dsa_loop_port_stp_state_set(struct dsa_switch *ds, int port,
 }
 
 static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port,
-                                       bool vlan_filtering)
+                                       bool vlan_filtering,
+                                       struct switchdev_trans *trans)
 {
        dev_dbg(ds->dev, "%s: port: %d, vlan_filtering: %d\n",
                __func__, port, vlan_filtering);
index 521ebc072903bbd775973ec7cc74c087dba45db6..74db81dafee35d371e48e724171516386bfaf6a8 100644 (file)
@@ -736,14 +736,23 @@ static int gswip_pce_load_microcode(struct gswip_priv *priv)
 }
 
 static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port,
-                                    bool vlan_filtering)
+                                    bool vlan_filtering,
+                                    struct switchdev_trans *trans)
 {
        struct gswip_priv *priv = ds->priv;
-       struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev;
 
        /* Do not allow changing the VLAN filtering options while in bridge */
-       if (!!(priv->port_vlan_filter & BIT(port)) != vlan_filtering && bridge)
-               return -EIO;
+       if (switchdev_trans_ph_prepare(trans)) {
+               struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev;
+
+               if (!bridge)
+                       return 0;
+
+               if (!!(priv->port_vlan_filter & BIT(port)) != vlan_filtering)
+                       return -EIO;
+
+               return 0;
+       }
 
        if (vlan_filtering) {
                /* Use port based VLAN tag */
@@ -781,8 +790,15 @@ static int gswip_setup(struct dsa_switch *ds)
 
        /* disable port fetch/store dma on all ports */
        for (i = 0; i < priv->hw_info->max_ports; i++) {
+               struct switchdev_trans trans;
+
+               /* Skip the prepare phase, this shouldn't return an error
+                * during setup.
+                */
+               trans.ph_prepare = false;
+
                gswip_port_disable(ds, i);
-               gswip_port_vlan_filtering(ds, i, false);
+               gswip_port_vlan_filtering(ds, i, false, &trans);
        }
 
        /* enable Switch */
index f5779e15237781d9aa857ba8af8489b169a45d79..1e101ab56cea113a637a2d6294ef3068b3638eef 100644 (file)
@@ -782,10 +782,14 @@ static void ksz8795_flush_dyn_mac_table(struct ksz_device *dev, int port)
 }
 
 static int ksz8795_port_vlan_filtering(struct dsa_switch *ds, int port,
-                                      bool flag)
+                                      bool flag,
+                                      struct switchdev_trans *trans)
 {
        struct ksz_device *dev = ds->priv;
 
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
        ksz_cfg(dev, S_MIRROR_CTRL, SW_VLAN_ENABLE, flag);
 
        return 0;
index 153664bf0e20569eed5152aaae2f0fe8e9cc17c3..abfd3802bb51706b842466cf8d6ad47aac8c560f 100644 (file)
@@ -493,10 +493,14 @@ static void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port)
 }
 
 static int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port,
-                                      bool flag)
+                                      bool flag,
+                                      struct switchdev_trans *trans)
 {
        struct ksz_device *dev = ds->priv;
 
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
        if (flag) {
                ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,
                             PORT_VLAN_LOOKUP_VID_0, true);
index cb3efa7de7a89c3893a677a982572f1f13f498a8..de7692b763d83f4a3323961a8005b432483e05cd 100644 (file)
@@ -1289,8 +1289,12 @@ mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid)
 
 static int
 mt7530_port_vlan_filtering(struct dsa_switch *ds, int port,
-                          bool vlan_filtering)
+                          bool vlan_filtering,
+                          struct switchdev_trans *trans)
 {
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
        if (vlan_filtering) {
                /* The port is being kept as VLAN-unaware port when bridge is
                 * set up with vlan_filtering not being set, Otherwise, the
index 9417412e5fce1bc5eb7c4cbf95dfe44e33b92fd3..bd297ae7cf9e79ec4e6339730259b972804380e4 100644 (file)
@@ -1578,15 +1578,16 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
 }
 
 static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
-                                        bool vlan_filtering)
+                                        bool vlan_filtering,
+                                        struct switchdev_trans *trans)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
        u16 mode = vlan_filtering ? MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE :
                MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED;
        int err;
 
-       if (!chip->info->max_vid)
-               return -EOPNOTSUPP;
+       if (switchdev_trans_ph_prepare(trans))
+               return chip->info->max_vid ? 0 : -EOPNOTSUPP;
 
        mv88e6xxx_reg_lock(chip);
        err = mv88e6xxx_port_set_8021q_mode(chip, port, mode);
index 552b1f7bde17b7153f6865fb3065c70670b02655..f791860d495f331d79cb1cf76bcf9986f1e7469b 100644 (file)
@@ -119,13 +119,12 @@ static int felix_vlan_prepare(struct dsa_switch *ds, int port,
        return 0;
 }
 
-static int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
+static int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
+                               struct switchdev_trans *trans)
 {
        struct ocelot *ocelot = ds->priv;
 
-       ocelot_port_vlan_filtering(ocelot, port, enabled);
-
-       return 0;
+       return ocelot_port_vlan_filtering(ocelot, port, enabled, trans);
 }
 
 static void felix_vlan_add(struct dsa_switch *ds, int port,
index f1e484477e35d656a25d86fab001f149ccf933f1..53064e0e161873eb4bf1b82b9c4acb37daceeba5 100644 (file)
@@ -1294,10 +1294,14 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
 }
 
 static int
-qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering)
+qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+                         struct switchdev_trans *trans)
 {
        struct qca8k_priv *priv = ds->priv;
 
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
        if (vlan_filtering) {
                qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
                          QCA8K_PORT_LOOKUP_VLAN_MODE,
index 6f2dab7e33d652fe89d03227ff85cc07babfb137..5e2e11a26da3dbcf0c082bcbaace580fd4e77795 100644 (file)
@@ -129,7 +129,8 @@ int rtl8366_enable_vlan(struct realtek_smi *smi, bool enable);
 int rtl8366_reset_vlan(struct realtek_smi *smi);
 int rtl8366_init_vlan(struct realtek_smi *smi);
 int rtl8366_vlan_filtering(struct dsa_switch *ds, int port,
-                          bool vlan_filtering);
+                          bool vlan_filtering,
+                          struct switchdev_trans *trans);
 int rtl8366_vlan_prepare(struct dsa_switch *ds, int port,
                         const struct switchdev_obj_port_vlan *vlan);
 void rtl8366_vlan_add(struct dsa_switch *ds, int port,
index c58ca324a4b24ae60d5be61dcbcf864e9e4061b0..307466b90489fd71f1863663f6e3d07522c7dc0f 100644 (file)
@@ -340,15 +340,20 @@ int rtl8366_init_vlan(struct realtek_smi *smi)
 }
 EXPORT_SYMBOL_GPL(rtl8366_init_vlan);
 
-int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering)
+int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
+                          struct switchdev_trans *trans)
 {
        struct realtek_smi *smi = ds->priv;
        struct rtl8366_vlan_4k vlan4k;
        int ret;
 
        /* Use VLAN nr port + 1 since VLAN0 is not valid */
-       if (!smi->ops->is_vlan_valid(smi, port + 1))
-               return -EINVAL;
+       if (switchdev_trans_ph_prepare(trans)) {
+               if (!smi->ops->is_vlan_valid(smi, port + 1))
+                       return -EINVAL;
+
+               return 0;
+       }
 
        dev_info(smi->dev, "%s filtering on port %d\n",
                 vlan_filtering ? "enable" : "disable",
index d582308c24016c07499f868734172a3eff1a9c58..4ebc4a5a7b355f22ac5320fe6c5131e18f319bd4 100644 (file)
@@ -245,7 +245,8 @@ enum sja1105_reset_reason {
 
 int sja1105_static_config_reload(struct sja1105_private *priv,
                                 enum sja1105_reset_reason reason);
-int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled);
+int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
+                          struct switchdev_trans *trans);
 void sja1105_frame_memory_partitioning(struct sja1105_private *priv);
 
 /* From sja1105_devlink.c */
index b4bf1b10e66cabc1d7b4d34a700a39573272492b..4a2ec395bcb00893ea207a3c4e7718014730f2a5 100644 (file)
@@ -135,6 +135,7 @@ static int sja1105_best_effort_vlan_filtering_set(struct sja1105_private *priv,
 
        rtnl_lock();
        for (port = 0; port < ds->num_ports; port++) {
+               struct switchdev_trans trans;
                struct dsa_port *dp;
 
                if (!dsa_is_user_port(ds, port))
@@ -143,7 +144,13 @@ static int sja1105_best_effort_vlan_filtering_set(struct sja1105_private *priv,
                dp = dsa_to_port(ds, port);
                vlan_filtering = dsa_port_is_vlan_filtering(dp);
 
-               rc = sja1105_vlan_filtering(ds, port, vlan_filtering);
+               trans.ph_prepare = true;
+               rc = sja1105_vlan_filtering(ds, port, vlan_filtering, &trans);
+               if (rc)
+                       break;
+
+               trans.ph_prepare = false;
+               rc = sja1105_vlan_filtering(ds, port, vlan_filtering, &trans);
                if (rc)
                        break;
        }
index 547487c535df721126b940695c330225655d6064..4ca0296509936a483760b31820707c6379f6a2f6 100644 (file)
@@ -2634,7 +2634,8 @@ static int sja1105_vlan_prepare(struct dsa_switch *ds, int port,
  * which can only be partially reconfigured at runtime (and not the TPID).
  * So a switch reset is required.
  */
-int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
+int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
+                          struct switchdev_trans *trans)
 {
        struct sja1105_l2_lookup_params_entry *l2_lookup_params;
        struct sja1105_general_params_entry *general_params;
@@ -2646,12 +2647,16 @@ int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
        u16 tpid, tpid2;
        int rc;
 
-       list_for_each_entry(rule, &priv->flow_block.rules, list) {
-               if (rule->type == SJA1105_RULE_VL) {
-                       dev_err(ds->dev,
-                               "Cannot change VLAN filtering state while VL rules are active\n");
-                       return -EBUSY;
+       if (switchdev_trans_ph_prepare(trans)) {
+               list_for_each_entry(rule, &priv->flow_block.rules, list) {
+                       if (rule->type == SJA1105_RULE_VL) {
+                               dev_err(ds->dev,
+                                       "Cannot change VLAN filtering with active VL rules\n");
+                               return -EBUSY;
+                       }
                }
+
+               return 0;
        }
 
        if (enabled) {
index e026617d61337e7fa613a7b32051b383e4d1e728..a965a554ff8b1b8157da076b784081efd8a342c4 100644 (file)
@@ -199,12 +199,15 @@ static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port,
        return 0;
 }
 
-void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
-                               bool vlan_aware)
+int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
+                              bool vlan_aware, struct switchdev_trans *trans)
 {
        struct ocelot_port *ocelot_port = ocelot->ports[port];
        u32 val;
 
+       if (switchdev_trans_ph_prepare(trans))
+               return 0;
+
        ocelot_port->vlan_aware = vlan_aware;
 
        if (vlan_aware)
@@ -218,6 +221,8 @@ void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
                       ANA_PORT_VLAN_CFG, port);
 
        ocelot_port_set_native_vlan(ocelot, port, ocelot_port->vid);
+
+       return 0;
 }
 EXPORT_SYMBOL(ocelot_port_vlan_filtering);
 
@@ -1102,12 +1107,24 @@ EXPORT_SYMBOL(ocelot_port_bridge_join);
 int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
                             struct net_device *bridge)
 {
+       struct switchdev_trans trans;
+       int ret;
+
        ocelot->bridge_mask &= ~BIT(port);
 
        if (!ocelot->bridge_mask)
                ocelot->hw_bridge_dev = NULL;
 
-       ocelot_port_vlan_filtering(ocelot, port, 0);
+       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;
+
        ocelot_port_set_pvid(ocelot, port, 0);
        return ocelot_port_set_native_vlan(ocelot, port, 0);
 }
index 64e619f0f5b22289570e231998537c6174284ab3..d3c03942546d40bdc9ed838fffd5f1597ba36bbe 100644 (file)
@@ -790,7 +790,7 @@ static int ocelot_port_attr_set(struct net_device *dev,
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
                ocelot_port_vlan_filtering(ocelot, port,
-                                          attr->u.vlan_filtering);
+                                          attr->u.vlan_filtering, trans);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
                ocelot_port_attr_mc_set(ocelot, port, !attr->u.mc_disabled);
index c0185660881cfb758db7e71525bfed96ed0f0df9..35429a140dfa773a71c0a0239ac58b7d05f41d88 100644 (file)
@@ -552,7 +552,8 @@ struct dsa_switch_ops {
         * VLAN support
         */
        int     (*port_vlan_filtering)(struct dsa_switch *ds, int port,
-                                      bool vlan_filtering);
+                                      bool vlan_filtering,
+                                      struct switchdev_trans *trans);
        int (*port_vlan_prepare)(struct dsa_switch *ds, int port,
                                 const struct switchdev_obj_port_vlan *vlan);
        void (*port_vlan_add)(struct dsa_switch *ds, int port,
index 46608494616fde80002acf9d4746dcd82012276b..1e9db95774415087a6db79dc07faf6c07265a9d2 100644 (file)
@@ -730,8 +730,8 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,
 void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs);
 void ocelot_adjust_link(struct ocelot *ocelot, int port,
                        struct phy_device *phydev);
-void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
-                               bool vlan_aware);
+int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, bool enabled,
+                              struct switchdev_trans *trans);
 void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state);
 int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
                            struct net_device *bridge);
index 9a4fb80d27312a021b5e077f51fdb0e2446490fc..73569c9af3cc0557341622e891a1d2ea630a7630 100644 (file)
@@ -280,22 +280,23 @@ int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
                rcu_read_unlock();
                if (!apply)
                        return -EINVAL;
-
-               return 0;
        }
 
        if (dsa_port_is_vlan_filtering(dp) == vlan_filtering)
                return 0;
 
-       err = ds->ops->port_vlan_filtering(ds, dp->index,
-                                          vlan_filtering);
+       err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering,
+                                          trans);
        if (err)
                return err;
 
-       if (ds->vlan_filtering_is_global)
-               ds->vlan_filtering = vlan_filtering;
-       else
-               dp->vlan_filtering = vlan_filtering;
+       if (switchdev_trans_ph_commit(trans)) {
+               if (ds->vlan_filtering_is_global)
+                       ds->vlan_filtering = vlan_filtering;
+               else
+                       dp->vlan_filtering = vlan_filtering;
+       }
+
        return 0;
 }
 
index 9afef6f0f9dfc3feb52270cd5698d2d86a37c6bb..3fb362b6874e3640f2867e0ba1ef872e3c36740b 100644 (file)
@@ -139,8 +139,15 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
                }
        }
        if (unset_vlan_filtering) {
-               struct switchdev_trans trans = {0};
+               struct switchdev_trans trans;
 
+               trans.ph_prepare = true;
+               err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
+                                             false, &trans);
+               if (err && err != EOPNOTSUPP)
+                       return err;
+
+               trans.ph_prepare = false;
                err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
                                              false, &trans);
                if (err && err != EOPNOTSUPP)