summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2021-10-25 12:59:42 +0100
committerDavid S. Miller <davem@davemloft.net>2021-10-25 12:59:42 +0100
commit57bb11328f9ab88571889f4a842859fcdd64d6cb (patch)
treeca7071763f0791120ec4ce1e5f0e03ea1c281e20 /net
parent2d7e73f09fc2f5d968ca375f047718cf25ae2b92 (diff)
parenteccd0a80dc7f4be65430236db475546b0ab9ec37 (diff)
downloadlinux-57bb11328f9ab88571889f4a842859fcdd64d6cb.tar.gz
linux-57bb11328f9ab88571889f4a842859fcdd64d6cb.tar.bz2
linux-57bb11328f9ab88571889f4a842859fcdd64d6cb.zip
Merge branch 'dsa-rtnl'
Vladimir Oltean says: ==================== Drop rtnl_lock from DSA .port_fdb_{add,del} As mentioned in the RFC posted 2 months ago: https://patchwork.kernel.org/project/netdevbpf/cover/20210824114049.3814660-1-vladimir.oltean@nxp.com/ DSA is transitioning to a driver API where the rtnl_lock is not held when calling ds->ops->port_fdb_add() and ds->ops->port_fdb_del(). Drivers cannot take that lock privately from those callbacks either. This change is required so that DSA can wait for switchdev FDB work items to finish before leaving the bridge. That change will be made in a future patch series. A small selftest is provided with the patch set in the hope that concurrency issues uncovered by this series, but not spotted by me by code inspection, will be caught. A status of the existing drivers: - mv88e6xxx_port_fdb_add() and mv88e6xxx_port_fdb_del() take mv88e6xxx_reg_lock() so they should be safe. - qca8k_fdb_add() and qca8k_fdb_del() take mutex_lock(&priv->reg_mutex) so they should be safe. - hellcreek_fdb_add() and hellcreek_fdb_add() take mutex_lock(&hellcreek->reg_lock) so they should be safe. - ksz9477_port_fdb_add() and ksz9477_port_fdb_del() take mutex_lock(&dev->alu_mutex) so they should be safe. - b53_fdb_add() and b53_fdb_del() did not have locking, so I've added a scheme based on my own judgement there (not tested). - felix_fdb_add() and felix_fdb_del() did not have locking, I've added and tested a locking scheme there. - mt7530_port_fdb_add() and mt7530_port_fdb_del() take mutex_lock(&priv->reg_mutex), so they should be safe. - gswip_port_fdb() did not have locking, so I've added a non-expert locking scheme based on my own judgement (not tested). - lan9303_alr_add_port() and lan9303_alr_del_port() take mutex_lock(&chip->alr_mutex) so they should be safe. - sja1105_fdb_add() and sja1105_fdb_del() did not have locking, I've added and tested a locking scheme. Changes in v3: Unlock arl_mutex only once in b53_fdb_dump(). Changes in v4: - Use __must_hold in ocelot and b53 - Add missing mutex_init in lantiq_gswip - Clean up the selftest a bit. Changes in v5: - Replace __must_hold with a comment. - Add a new patch (01/10). ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/dsa/dsa2.c1
-rw-r--r--net/dsa/slave.c2
-rw-r--r--net/dsa/switch.c80
3 files changed, 55 insertions, 28 deletions
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index f5270114dcb8..826957b6442b 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -433,6 +433,7 @@ static int dsa_port_setup(struct dsa_port *dp)
if (dp->setup)
return 0;
+ mutex_init(&dp->addr_lists_lock);
INIT_LIST_HEAD(&dp->fdbs);
INIT_LIST_HEAD(&dp->mdbs);
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 9d9fef668eba..adcfb2cb4e61 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -2413,7 +2413,6 @@ static void dsa_slave_switchdev_event_work(struct work_struct *work)
dp = dsa_to_port(ds, switchdev_work->port);
- rtnl_lock();
switch (switchdev_work->event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE:
if (switchdev_work->host_addr)
@@ -2448,7 +2447,6 @@ static void dsa_slave_switchdev_event_work(struct work_struct *work)
break;
}
- rtnl_unlock();
dev_put(switchdev_work->dev);
kfree(switchdev_work);
diff --git a/net/dsa/switch.c b/net/dsa/switch.c
index 2b1b21bde830..bb155a16d454 100644
--- a/net/dsa/switch.c
+++ b/net/dsa/switch.c
@@ -215,26 +215,30 @@ static int dsa_port_do_mdb_add(struct dsa_port *dp,
struct dsa_switch *ds = dp->ds;
struct dsa_mac_addr *a;
int port = dp->index;
- int err;
+ int err = 0;
/* No need to bother with refcounting for user ports */
if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
return ds->ops->port_mdb_add(ds, port, mdb);
+ mutex_lock(&dp->addr_lists_lock);
+
a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid);
if (a) {
refcount_inc(&a->refcount);
- return 0;
+ goto out;
}
a = kzalloc(sizeof(*a), GFP_KERNEL);
- if (!a)
- return -ENOMEM;
+ if (!a) {
+ err = -ENOMEM;
+ goto out;
+ }
err = ds->ops->port_mdb_add(ds, port, mdb);
if (err) {
kfree(a);
- return err;
+ goto out;
}
ether_addr_copy(a->addr, mdb->addr);
@@ -242,7 +246,10 @@ static int dsa_port_do_mdb_add(struct dsa_port *dp,
refcount_set(&a->refcount, 1);
list_add_tail(&a->list, &dp->mdbs);
- return 0;
+out:
+ mutex_unlock(&dp->addr_lists_lock);
+
+ return err;
}
static int dsa_port_do_mdb_del(struct dsa_port *dp,
@@ -251,29 +258,36 @@ static int dsa_port_do_mdb_del(struct dsa_port *dp,
struct dsa_switch *ds = dp->ds;
struct dsa_mac_addr *a;
int port = dp->index;
- int err;
+ int err = 0;
/* No need to bother with refcounting for user ports */
if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
return ds->ops->port_mdb_del(ds, port, mdb);
+ mutex_lock(&dp->addr_lists_lock);
+
a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid);
- if (!a)
- return -ENOENT;
+ if (!a) {
+ err = -ENOENT;
+ goto out;
+ }
if (!refcount_dec_and_test(&a->refcount))
- return 0;
+ goto out;
err = ds->ops->port_mdb_del(ds, port, mdb);
if (err) {
- refcount_inc(&a->refcount);
- return err;
+ refcount_set(&a->refcount, 1);
+ goto out;
}
list_del(&a->list);
kfree(a);
- return 0;
+out:
+ mutex_unlock(&dp->addr_lists_lock);
+
+ return err;
}
static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr,
@@ -282,26 +296,30 @@ static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr,
struct dsa_switch *ds = dp->ds;
struct dsa_mac_addr *a;
int port = dp->index;
- int err;
+ int err = 0;
/* No need to bother with refcounting for user ports */
if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
return ds->ops->port_fdb_add(ds, port, addr, vid);
+ mutex_lock(&dp->addr_lists_lock);
+
a = dsa_mac_addr_find(&dp->fdbs, addr, vid);
if (a) {
refcount_inc(&a->refcount);
- return 0;
+ goto out;
}
a = kzalloc(sizeof(*a), GFP_KERNEL);
- if (!a)
- return -ENOMEM;
+ if (!a) {
+ err = -ENOMEM;
+ goto out;
+ }
err = ds->ops->port_fdb_add(ds, port, addr, vid);
if (err) {
kfree(a);
- return err;
+ goto out;
}
ether_addr_copy(a->addr, addr);
@@ -309,7 +327,10 @@ static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr,
refcount_set(&a->refcount, 1);
list_add_tail(&a->list, &dp->fdbs);
- return 0;
+out:
+ mutex_unlock(&dp->addr_lists_lock);
+
+ return err;
}
static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr,
@@ -318,29 +339,36 @@ static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr,
struct dsa_switch *ds = dp->ds;
struct dsa_mac_addr *a;
int port = dp->index;
- int err;
+ int err = 0;
/* No need to bother with refcounting for user ports */
if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)))
return ds->ops->port_fdb_del(ds, port, addr, vid);
+ mutex_lock(&dp->addr_lists_lock);
+
a = dsa_mac_addr_find(&dp->fdbs, addr, vid);
- if (!a)
- return -ENOENT;
+ if (!a) {
+ err = -ENOENT;
+ goto out;
+ }
if (!refcount_dec_and_test(&a->refcount))
- return 0;
+ goto out;
err = ds->ops->port_fdb_del(ds, port, addr, vid);
if (err) {
- refcount_inc(&a->refcount);
- return err;
+ refcount_set(&a->refcount, 1);
+ goto out;
}
list_del(&a->list);
kfree(a);
- return 0;
+out:
+ mutex_unlock(&dp->addr_lists_lock);
+
+ return err;
}
static int dsa_switch_host_fdb_add(struct dsa_switch *ds,