--- /dev/null
+From 9d5ef190e5615a7b63af89f88c4106a5bc127974 Mon Sep 17 00:00:00 2001
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Fri, 5 Feb 2021 15:37:10 +0200
+Subject: [PATCH] net: dsa: automatically bring up DSA master when opening user
+ port
+
+DSA wants the master interface to be open before the user port is due to
+historical reasons. The promiscuity of interfaces that are down used to
+have issues, as referenced Lennert Buytenhek in commit df02c6ff2e39
+("dsa: fix master interface allmulti/promisc handling").
+
+The bugfix mentioned there, commit b6c40d68ff64 ("net: only invoke
+dev->change_rx_flags when device is UP"), was basically a "don't do
+that" approach to working around the promiscuity while down issue.
+
+Further work done by Vlad Yasevich in commit d2615bf45069 ("net: core:
+Always propagate flag changes to interfaces") has resolved the
+underlying issue, and it is strictly up to the DSA and 8021q drivers
+now, it is no longer mandated by the networking core that the master
+interface must be up when changing its promiscuity.
+
+From DSA's point of view, deciding to error out in dsa_slave_open
+because the master isn't up is
+(a) a bad user experience and
+(b) knocking at an open door.
+Even if there still was an issue with promiscuity while down, DSA could
+still just open the master and avoid it.
+
+Doing it this way has the additional benefit that user space can now
+remove DSA-specific workarounds, like systemd-networkd with BindCarrier:
+https://github.com/systemd/systemd/issues/7478
+
+And we can finally remove one of the 2 bullets in the "Common pitfalls
+using DSA setups" chapter.
+
+Tested with two cascaded DSA switches:
+
+$ ip link set sw0p2 up
+fsl_enetc 0000:00:00.2 eno2: configuring for fixed/internal link mode
+fsl_enetc 0000:00:00.2 eno2: Link is Up - 1Gbps/Full - flow control rx/tx
+mscc_felix 0000:00:00.5 swp0: configuring for fixed/sgmii link mode
+mscc_felix 0000:00:00.5 swp0: Link is Up - 1Gbps/Full - flow control off
+8021q: adding VLAN 0 to HW filter on device swp0
+sja1105 spi2.0 sw0p2: configuring for phy/rgmii-id link mode
+IPv6: ADDRCONF(NETDEV_CHANGE): eno2: link becomes ready
+IPv6: ADDRCONF(NETDEV_CHANGE): swp0: link becomes ready
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ Documentation/networking/dsa/dsa.rst | 4 ----
+ net/dsa/slave.c | 7 +++++--
+ 2 files changed, 5 insertions(+), 6 deletions(-)
+
+--- a/Documentation/networking/dsa/dsa.rst
++++ b/Documentation/networking/dsa/dsa.rst
+@@ -273,10 +273,6 @@ will not make us go through the switch t
+ the Ethernet switch on the other end, expecting a tag will typically drop this
+ frame.
+
+-Slave network devices check that the master network device is UP before allowing
+-you to administratively bring UP these slave network devices. A common
+-configuration mistake is forgetting to bring UP the master network device first.
+-
+ Interactions with other subsystems
+ ==================================
+
+--- a/net/dsa/slave.c
++++ b/net/dsa/slave.c
+@@ -70,8 +70,11 @@ static int dsa_slave_open(struct net_dev
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ int err;
+
+- if (!(master->flags & IFF_UP))
+- return -ENETDOWN;
++ err = dev_open(master, NULL);
++ if (err < 0) {
++ netdev_err(dev, "failed to open master %s\n", master->name);
++ goto out;
++ }
+
+ if (!ether_addr_equal(dev->dev_addr, master->dev_addr)) {
+ err = dev_uc_add(master, dev->dev_addr);
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
-@@ -1592,7 +1592,9 @@ static void dsa_slave_switchdev_event_wo
+@@ -1595,7 +1595,9 @@ static void dsa_slave_switchdev_event_wo
err = dsa_port_fdb_add(dp, fdb_info->addr, fdb_info->vid);
if (err) {
break;
}
fdb_info->offloaded = true;
-@@ -1607,9 +1609,11 @@ static void dsa_slave_switchdev_event_wo
+@@ -1610,9 +1612,11 @@ static void dsa_slave_switchdev_event_wo
err = dsa_port_fdb_del(dp, fdb_info->addr, fdb_info->vid);
if (err) {
struct sk_buff * (*xmit)(struct sk_buff *skb,
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
-@@ -1567,76 +1567,66 @@ static int dsa_slave_netdevice_event(str
+@@ -1570,76 +1570,66 @@ static int dsa_slave_netdevice_event(str
return NOTIFY_DONE;
}
}
/* Called under rcu_read_lock() */
-@@ -1644,7 +1634,9 @@ static int dsa_slave_switchdev_event(str
+@@ -1647,7 +1637,9 @@ static int dsa_slave_switchdev_event(str
unsigned long event, void *ptr)
{
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
int err;
if (event == SWITCHDEV_PORT_ATTR_SET) {
-@@ -1657,20 +1649,32 @@ static int dsa_slave_switchdev_event(str
+@@ -1660,20 +1652,32 @@ static int dsa_slave_switchdev_event(str
if (!dsa_slave_dev_check(dev))
return NOTIFY_DONE;
dev_hold(dev);
break;
default:
-@@ -1680,10 +1684,6 @@ static int dsa_slave_switchdev_event(str
+@@ -1683,10 +1687,6 @@ static int dsa_slave_switchdev_event(str
dsa_schedule_work(&switchdev_work->work);
return NOTIFY_OK;
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
-@@ -1639,31 +1639,29 @@ static int dsa_slave_switchdev_event(str
+@@ -1642,31 +1642,29 @@ static int dsa_slave_switchdev_event(str
struct dsa_port *dp;
int err;
fdb_info = ptr;
if (!fdb_info->added_by_user) {
-@@ -1676,13 +1674,12 @@ static int dsa_slave_switchdev_event(str
+@@ -1679,13 +1677,12 @@ static int dsa_slave_switchdev_event(str
switchdev_work->vid = fdb_info->vid;
dev_hold(dev);
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
-@@ -1652,6 +1652,9 @@ static int dsa_slave_switchdev_event(str
+@@ -1655,6 +1655,9 @@ static int dsa_slave_switchdev_event(str
dp = dsa_slave_to_port(dev);
*/
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
-@@ -1629,6 +1629,25 @@ static void dsa_slave_switchdev_event_wo
+@@ -1632,6 +1632,25 @@ static void dsa_slave_switchdev_event_wo
dev_put(dp->slave);
}
/* Called under rcu_read_lock() */
static int dsa_slave_switchdev_event(struct notifier_block *unused,
unsigned long event, void *ptr)
-@@ -1647,10 +1666,37 @@ static int dsa_slave_switchdev_event(str
+@@ -1650,10 +1669,37 @@ static int dsa_slave_switchdev_event(str
return notifier_from_errno(err);
case SWITCHDEV_FDB_ADD_TO_DEVICE:
case SWITCHDEV_FDB_DEL_TO_DEVICE:
if (!dp->ds->ops->port_fdb_add || !dp->ds->ops->port_fdb_del)
return NOTIFY_DONE;
-@@ -1665,18 +1711,13 @@ static int dsa_slave_switchdev_event(str
+@@ -1668,18 +1714,13 @@ static int dsa_slave_switchdev_event(str
switchdev_work->port = dp->index;
switchdev_work->event = event;
#include "dsa_priv.h"
-@@ -1223,6 +1227,27 @@ static struct devlink_port *dsa_slave_ge
+@@ -1226,6 +1230,27 @@ static struct devlink_port *dsa_slave_ge
return dp->ds->devlink ? &dp->devlink_port : NULL;
}
static const struct net_device_ops dsa_slave_netdev_ops = {
.ndo_open = dsa_slave_open,
.ndo_stop = dsa_slave_close,
-@@ -1247,6 +1272,9 @@ static const struct net_device_ops dsa_s
+@@ -1250,6 +1275,9 @@ static const struct net_device_ops dsa_s
.ndo_vlan_rx_add_vid = dsa_slave_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = dsa_slave_vlan_rx_kill_vid,
.ndo_get_devlink_port = dsa_slave_get_devlink_port,
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
-@@ -1697,10 +1697,12 @@ static int dsa_slave_switchdev_event(str
+@@ -1700,10 +1700,12 @@ static int dsa_slave_switchdev_event(str
fdb_info = ptr;
if (dsa_slave_dev_check(dev)) {
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
-@@ -1711,7 +1711,11 @@ static int dsa_slave_switchdev_event(str
+@@ -1714,7 +1714,11 @@ static int dsa_slave_switchdev_event(str
struct net_device *br_dev;
struct dsa_slave_priv *p;
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
-@@ -1704,9 +1704,12 @@ static int dsa_slave_switchdev_event(str
+@@ -1707,9 +1707,12 @@ static int dsa_slave_switchdev_event(str
else if (!fdb_info->added_by_user)
return NOTIFY_OK;
} else {
*/
struct net_device *br_dev;
struct dsa_slave_priv *p;
-@@ -1728,7 +1731,8 @@ static int dsa_slave_switchdev_event(str
+@@ -1731,7 +1734,8 @@ static int dsa_slave_switchdev_event(str
dp = p->dp->cpu_dp;