Skip to content

Commit 4bed397

Browse files
vladimirolteandavem330
authored andcommitted
net: dsa: ensure during dsa_fdb_offload_notify that dev_hold and dev_put are on the same dev
When (a) "dev" is a bridge port which the DSA switch tree offloads, but is otherwise not a dsa slave (such as a LAG netdev), or (b) "dev" is the bridge net device itself then strange things happen to the dev_hold/dev_put pair: dsa_schedule_work() will still be called with a DSA port that offloads that netdev, but dev_hold() will be called on the non-DSA netdev. Then the "if" condition in dsa_slave_switchdev_event_work() does not pass, because "dev" is not a DSA netdev, so dev_put() is not called. This results in the simple fact that we have a reference counting mismatch on the "dev" net device. This can be seen when we add support for host addresses installed on the bridge net device. ip link add br1 type bridge ip link set br1 address 00:01:02:03:04:05 ip link set swp0 master br1 ip link del br1 [ 968.512278] unregister_netdevice: waiting for br1 to become free. Usage count = 5 It seems foolish to do penny pinching and not add the net_device pointer in the dsa_switchdev_event_work structure, so let's finally do that. As an added bonus, when we start offloading local entries pointing towards the bridge, these will now properly appear as 'offloaded' in 'bridge fdb' (this was not possible before, because 'dev' was assumed to only be a DSA net device): 00:01:02:03:04:05 dev br0 vlan 1 offload master br0 permanent 00:01:02:03:04:05 dev br0 offload master br0 permanent Signed-off-by: Vladimir Oltean <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 81a619f commit 4bed397

File tree

2 files changed

+5
-5
lines changed

2 files changed

+5
-5
lines changed

net/dsa/dsa_priv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ struct dsa_notifier_mrp_ring_role_info {
116116
struct dsa_switchdev_event_work {
117117
struct dsa_switch *ds;
118118
int port;
119+
struct net_device *dev;
119120
struct work_struct work;
120121
unsigned long event;
121122
/* Specific for SWITCHDEV_FDB_ADD_TO_DEVICE and

net/dsa/slave.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2349,9 +2349,8 @@ static void dsa_slave_switchdev_event_work(struct work_struct *work)
23492349
}
23502350
rtnl_unlock();
23512351

2352+
dev_put(switchdev_work->dev);
23522353
kfree(switchdev_work);
2353-
if (dsa_is_user_port(ds, dp->index))
2354-
dev_put(dp->slave);
23552354
}
23562355

23572356
static int dsa_lower_dev_walk(struct net_device *lower_dev,
@@ -2469,15 +2468,15 @@ static int dsa_slave_switchdev_event(struct notifier_block *unused,
24692468
switchdev_work->ds = dp->ds;
24702469
switchdev_work->port = dp->index;
24712470
switchdev_work->event = event;
2471+
switchdev_work->dev = dev;
24722472

24732473
ether_addr_copy(switchdev_work->addr,
24742474
fdb_info->addr);
24752475
switchdev_work->vid = fdb_info->vid;
24762476
switchdev_work->host_addr = host_addr;
24772477

2478-
/* Hold a reference on the slave for dsa_fdb_offload_notify */
2479-
if (dsa_is_user_port(dp->ds, dp->index))
2480-
dev_hold(dev);
2478+
/* Hold a reference for dsa_fdb_offload_notify */
2479+
dev_hold(dev);
24812480
dsa_schedule_work(&switchdev_work->work);
24822481
break;
24832482
default:

0 commit comments

Comments
 (0)