Skip to content

Commit 3e19ae7

Browse files
vladimirolteandavem330
authored andcommitted
net: bridge: use READ_ONCE() and WRITE_ONCE() compiler barriers for fdb->dst
Annotate the writer side of fdb->dst: - fdb_create() - br_fdb_update() - fdb_add_entry() - br_fdb_external_learn_add() with WRITE_ONCE() and the reader side: - br_fdb_test_addr() - br_fdb_update() - fdb_fill_info() - fdb_add_entry() - fdb_delete_by_addr_and_port() - br_fdb_external_learn_add() - br_switchdev_fdb_notify() with compiler barriers such that the readers do not attempt to reload fdb->dst multiple times, leading to potentially different destination ports when the fdb entry is updated concurrently. This is especially important in read-side sections where fdb->dst is used more than once, but let's convert all accesses for the sake of uniformity. Suggested-by: Nikolay Aleksandrov <[email protected]> Signed-off-by: Vladimir Oltean <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 84fe739 commit 3e19ae7

File tree

2 files changed

+21
-14
lines changed

2 files changed

+21
-14
lines changed

net/bridge/br_fdb.c

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -440,9 +440,14 @@ int br_fdb_test_addr(struct net_device *dev, unsigned char *addr)
440440
if (!port)
441441
ret = 0;
442442
else {
443+
const struct net_bridge_port *dst = NULL;
444+
443445
fdb = br_fdb_find_rcu(port->br, addr, 0);
444-
ret = fdb && fdb->dst && fdb->dst->dev != dev &&
445-
fdb->dst->state == BR_STATE_FORWARDING;
446+
if (fdb)
447+
dst = READ_ONCE(fdb->dst);
448+
449+
ret = dst && dst->dev != dev &&
450+
dst->state == BR_STATE_FORWARDING;
446451
}
447452
rcu_read_unlock();
448453

@@ -509,7 +514,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br,
509514
fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC);
510515
if (fdb) {
511516
memcpy(fdb->key.addr.addr, addr, ETH_ALEN);
512-
fdb->dst = source;
517+
WRITE_ONCE(fdb->dst, source);
513518
fdb->key.vlan_id = vid;
514519
fdb->flags = flags;
515520
fdb->updated = fdb->used = jiffies;
@@ -600,10 +605,10 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
600605
}
601606

602607
/* fastpath: update of existing entry */
603-
if (unlikely(source != fdb->dst &&
608+
if (unlikely(source != READ_ONCE(fdb->dst) &&
604609
!test_bit(BR_FDB_STICKY, &fdb->flags))) {
605610
br_switchdev_fdb_notify(fdb, RTM_DELNEIGH);
606-
fdb->dst = source;
611+
WRITE_ONCE(fdb->dst, source);
607612
fdb_modified = true;
608613
/* Take over HW learned entry */
609614
if (unlikely(test_bit(BR_FDB_ADDED_BY_EXT_LEARN,
@@ -650,6 +655,7 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
650655
const struct net_bridge_fdb_entry *fdb,
651656
u32 portid, u32 seq, int type, unsigned int flags)
652657
{
658+
const struct net_bridge_port *dst = READ_ONCE(fdb->dst);
653659
unsigned long now = jiffies;
654660
struct nda_cacheinfo ci;
655661
struct nlmsghdr *nlh;
@@ -665,7 +671,7 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
665671
ndm->ndm_pad2 = 0;
666672
ndm->ndm_flags = 0;
667673
ndm->ndm_type = 0;
668-
ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex;
674+
ndm->ndm_ifindex = dst ? dst->dev->ifindex : br->dev->ifindex;
669675
ndm->ndm_state = fdb_to_nud(br, fdb);
670676

671677
if (test_bit(BR_FDB_OFFLOADED, &fdb->flags))
@@ -964,8 +970,8 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
964970
if (flags & NLM_F_EXCL)
965971
return -EEXIST;
966972

967-
if (fdb->dst != source) {
968-
fdb->dst = source;
973+
if (READ_ONCE(fdb->dst) != source) {
974+
WRITE_ONCE(fdb->dst, source);
969975
modified = true;
970976
}
971977
}
@@ -1132,7 +1138,7 @@ static int fdb_delete_by_addr_and_port(struct net_bridge *br,
11321138
struct net_bridge_fdb_entry *fdb;
11331139

11341140
fdb = br_fdb_find(br, addr, vlan);
1135-
if (!fdb || fdb->dst != p)
1141+
if (!fdb || READ_ONCE(fdb->dst) != p)
11361142
return -ENOENT;
11371143

11381144
fdb_delete(br, fdb, true);
@@ -1281,8 +1287,8 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
12811287
} else {
12821288
fdb->updated = jiffies;
12831289

1284-
if (fdb->dst != p) {
1285-
fdb->dst = p;
1290+
if (READ_ONCE(fdb->dst) != p) {
1291+
WRITE_ONCE(fdb->dst, p);
12861292
modified = true;
12871293
}
12881294

net/bridge/br_switchdev.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
110110
void
111111
br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
112112
{
113+
const struct net_bridge_port *dst = READ_ONCE(fdb->dst);
113114
struct switchdev_notifier_fdb_info info = {
114115
.addr = fdb->key.addr.addr,
115116
.vid = fdb->key.vlan_id,
@@ -118,17 +119,17 @@ br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
118119
.offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags),
119120
};
120121

121-
if (!fdb->dst)
122+
if (!dst)
122123
return;
123124

124125
switch (type) {
125126
case RTM_DELNEIGH:
126127
call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE,
127-
fdb->dst->dev, &info.info, NULL);
128+
dst->dev, &info.info, NULL);
128129
break;
129130
case RTM_NEWNEIGH:
130131
call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE,
131-
fdb->dst->dev, &info.info, NULL);
132+
dst->dev, &info.info, NULL);
132133
break;
133134
}
134135
}

0 commit comments

Comments
 (0)