Skip to content

Commit 9014fc3

Browse files
committed
Merge branch 'bridge-fdbs-bitops'
Nikolay Aleksandrov says: ==================== net: bridge: convert fdbs to use bitops We'd like to have a well-defined behaviour when changing fdb flags. The problem is that we've added new fields which are changed from all contexts without any locking. We are aware of the bit test/change races and these are fine (we can remove them later), but it is considered undefined behaviour to change bitfields from multiple threads and also on some architectures that can result in unexpected results, specifically when all fields between the changed ones are also bitfields. The conversion to bitops shows the intent clearly and makes them use functions with well-defined behaviour in such cases. There is no overhead for the fast-path, the bit changing functions are used only in special cases when learning and in the slow path. In addition this conversion allows us to simplify fdb flag handling and avoid bugs for future bits (e.g. a forgetting to clear the new bit when allocating a new fdb). All bridge selftests passed, also tried all of the converted bits manually in a VM. ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 8466a57 + 3fb01a3 commit 9014fc3

File tree

4 files changed

+85
-79
lines changed

4 files changed

+85
-79
lines changed

net/bridge/br_fdb.c

Lines changed: 65 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ static inline unsigned long hold_time(const struct net_bridge *br)
7575
static inline int has_expired(const struct net_bridge *br,
7676
const struct net_bridge_fdb_entry *fdb)
7777
{
78-
return !fdb->is_static && !fdb->added_by_external_learn &&
79-
time_before_eq(fdb->updated + hold_time(br), jiffies);
78+
return !test_bit(BR_FDB_STATIC, &fdb->flags) &&
79+
!test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags) &&
80+
time_before_eq(fdb->updated + hold_time(br), jiffies);
8081
}
8182

8283
static void fdb_rcu_free(struct rcu_head *head)
@@ -197,7 +198,7 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f,
197198
{
198199
trace_fdb_delete(br, f);
199200

200-
if (f->is_static)
201+
if (test_bit(BR_FDB_STATIC, &f->flags))
201202
fdb_del_hw_addr(br, f->key.addr.addr);
202203

203204
hlist_del_init_rcu(&f->fdb_node);
@@ -224,7 +225,7 @@ static void fdb_delete_local(struct net_bridge *br,
224225
if (op != p && ether_addr_equal(op->dev->dev_addr, addr) &&
225226
(!vid || br_vlan_find(vg, vid))) {
226227
f->dst = op;
227-
f->added_by_user = 0;
228+
clear_bit(BR_FDB_ADDED_BY_USER, &f->flags);
228229
return;
229230
}
230231
}
@@ -235,7 +236,7 @@ static void fdb_delete_local(struct net_bridge *br,
235236
if (p && ether_addr_equal(br->dev->dev_addr, addr) &&
236237
(!vid || (v && br_vlan_should_use(v)))) {
237238
f->dst = NULL;
238-
f->added_by_user = 0;
239+
clear_bit(BR_FDB_ADDED_BY_USER, &f->flags);
239240
return;
240241
}
241242

@@ -250,7 +251,8 @@ void br_fdb_find_delete_local(struct net_bridge *br,
250251

251252
spin_lock_bh(&br->hash_lock);
252253
f = br_fdb_find(br, addr, vid);
253-
if (f && f->is_local && !f->added_by_user && f->dst == p)
254+
if (f && test_bit(BR_FDB_LOCAL, &f->flags) &&
255+
!test_bit(BR_FDB_ADDED_BY_USER, &f->flags) && f->dst == p)
254256
fdb_delete_local(br, p, f);
255257
spin_unlock_bh(&br->hash_lock);
256258
}
@@ -265,7 +267,8 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
265267
spin_lock_bh(&br->hash_lock);
266268
vg = nbp_vlan_group(p);
267269
hlist_for_each_entry(f, &br->fdb_list, fdb_node) {
268-
if (f->dst == p && f->is_local && !f->added_by_user) {
270+
if (f->dst == p && test_bit(BR_FDB_LOCAL, &f->flags) &&
271+
!test_bit(BR_FDB_ADDED_BY_USER, &f->flags)) {
269272
/* delete old one */
270273
fdb_delete_local(br, p, f);
271274

@@ -306,7 +309,8 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
306309

307310
/* If old entry was unassociated with any port, then delete it. */
308311
f = br_fdb_find(br, br->dev->dev_addr, 0);
309-
if (f && f->is_local && !f->dst && !f->added_by_user)
312+
if (f && test_bit(BR_FDB_LOCAL, &f->flags) &&
313+
!f->dst && !test_bit(BR_FDB_ADDED_BY_USER, &f->flags))
310314
fdb_delete_local(br, NULL, f);
311315

312316
fdb_insert(br, NULL, newaddr, 0);
@@ -321,7 +325,8 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
321325
if (!br_vlan_should_use(v))
322326
continue;
323327
f = br_fdb_find(br, br->dev->dev_addr, v->vid);
324-
if (f && f->is_local && !f->dst && !f->added_by_user)
328+
if (f && test_bit(BR_FDB_LOCAL, &f->flags) &&
329+
!f->dst && !test_bit(BR_FDB_ADDED_BY_USER, &f->flags))
325330
fdb_delete_local(br, NULL, f);
326331
fdb_insert(br, NULL, newaddr, v->vid);
327332
}
@@ -346,7 +351,8 @@ void br_fdb_cleanup(struct work_struct *work)
346351
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
347352
unsigned long this_timer;
348353

349-
if (f->is_static || f->added_by_external_learn)
354+
if (test_bit(BR_FDB_STATIC, &f->flags) ||
355+
test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &f->flags))
350356
continue;
351357
this_timer = f->updated + delay;
352358
if (time_after(this_timer, now)) {
@@ -373,7 +379,7 @@ void br_fdb_flush(struct net_bridge *br)
373379

374380
spin_lock_bh(&br->hash_lock);
375381
hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) {
376-
if (!f->is_static)
382+
if (!test_bit(BR_FDB_STATIC, &f->flags))
377383
fdb_delete(br, f, true);
378384
}
379385
spin_unlock_bh(&br->hash_lock);
@@ -397,10 +403,11 @@ void br_fdb_delete_by_port(struct net_bridge *br,
397403
continue;
398404

399405
if (!do_all)
400-
if (f->is_static || (vid && f->key.vlan_id != vid))
406+
if (test_bit(BR_FDB_STATIC, &f->flags) ||
407+
(vid && f->key.vlan_id != vid))
401408
continue;
402409

403-
if (f->is_local)
410+
if (test_bit(BR_FDB_LOCAL, &f->flags))
404411
fdb_delete_local(br, p, f);
405412
else
406413
fdb_delete(br, f, true);
@@ -469,8 +476,8 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
469476
fe->port_no = f->dst->port_no;
470477
fe->port_hi = f->dst->port_no >> 8;
471478

472-
fe->is_local = f->is_local;
473-
if (!f->is_static)
479+
fe->is_local = test_bit(BR_FDB_LOCAL, &f->flags);
480+
if (!test_bit(BR_FDB_STATIC, &f->flags))
474481
fe->ageing_timer_value = jiffies_delta_to_clock_t(jiffies - f->updated);
475482
++fe;
476483
++num;
@@ -484,8 +491,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br,
484491
struct net_bridge_port *source,
485492
const unsigned char *addr,
486493
__u16 vid,
487-
unsigned char is_local,
488-
unsigned char is_static)
494+
unsigned long flags)
489495
{
490496
struct net_bridge_fdb_entry *fdb;
491497

@@ -494,12 +500,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br,
494500
memcpy(fdb->key.addr.addr, addr, ETH_ALEN);
495501
fdb->dst = source;
496502
fdb->key.vlan_id = vid;
497-
fdb->is_local = is_local;
498-
fdb->is_static = is_static;
499-
fdb->added_by_user = 0;
500-
fdb->added_by_external_learn = 0;
501-
fdb->offloaded = 0;
502-
fdb->is_sticky = 0;
503+
fdb->flags = flags;
503504
fdb->updated = fdb->used = jiffies;
504505
if (rhashtable_lookup_insert_fast(&br->fdb_hash_tbl,
505506
&fdb->rhnode,
@@ -526,14 +527,15 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
526527
/* it is okay to have multiple ports with same
527528
* address, just use the first one.
528529
*/
529-
if (fdb->is_local)
530+
if (test_bit(BR_FDB_LOCAL, &fdb->flags))
530531
return 0;
531532
br_warn(br, "adding interface %s with same address as a received packet (addr:%pM, vlan:%u)\n",
532533
source ? source->dev->name : br->dev->name, addr, vid);
533534
fdb_delete(br, fdb, true);
534535
}
535536

536-
fdb = fdb_create(br, source, addr, vid, 1, 1);
537+
fdb = fdb_create(br, source, addr, vid,
538+
BIT(BR_FDB_LOCAL) | BIT(BR_FDB_STATIC));
537539
if (!fdb)
538540
return -ENOMEM;
539541

@@ -572,36 +574,37 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
572574
fdb = fdb_find_rcu(&br->fdb_hash_tbl, addr, vid);
573575
if (likely(fdb)) {
574576
/* attempt to update an entry for a local interface */
575-
if (unlikely(fdb->is_local)) {
577+
if (unlikely(test_bit(BR_FDB_LOCAL, &fdb->flags))) {
576578
if (net_ratelimit())
577579
br_warn(br, "received packet on %s with own address as source address (addr:%pM, vlan:%u)\n",
578580
source->dev->name, addr, vid);
579581
} else {
580582
unsigned long now = jiffies;
581583

582584
/* fastpath: update of existing entry */
583-
if (unlikely(source != fdb->dst && !fdb->is_sticky)) {
585+
if (unlikely(source != fdb->dst &&
586+
!test_bit(BR_FDB_STICKY, &fdb->flags))) {
584587
fdb->dst = source;
585588
fdb_modified = true;
586589
/* Take over HW learned entry */
587-
if (unlikely(fdb->added_by_external_learn))
588-
fdb->added_by_external_learn = 0;
590+
test_and_clear_bit(BR_FDB_ADDED_BY_EXT_LEARN,
591+
&fdb->flags);
589592
}
590593
if (now != fdb->updated)
591594
fdb->updated = now;
592595
if (unlikely(added_by_user))
593-
fdb->added_by_user = 1;
596+
set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
594597
if (unlikely(fdb_modified)) {
595598
trace_br_fdb_update(br, source, addr, vid, added_by_user);
596599
fdb_notify(br, fdb, RTM_NEWNEIGH, true);
597600
}
598601
}
599602
} else {
600603
spin_lock(&br->hash_lock);
601-
fdb = fdb_create(br, source, addr, vid, 0, 0);
604+
fdb = fdb_create(br, source, addr, vid, 0);
602605
if (fdb) {
603606
if (unlikely(added_by_user))
604-
fdb->added_by_user = 1;
607+
set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
605608
trace_br_fdb_update(br, source, addr, vid,
606609
added_by_user);
607610
fdb_notify(br, fdb, RTM_NEWNEIGH, true);
@@ -616,9 +619,9 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
616619
static int fdb_to_nud(const struct net_bridge *br,
617620
const struct net_bridge_fdb_entry *fdb)
618621
{
619-
if (fdb->is_local)
622+
if (test_bit(BR_FDB_LOCAL, &fdb->flags))
620623
return NUD_PERMANENT;
621-
else if (fdb->is_static)
624+
else if (test_bit(BR_FDB_STATIC, &fdb->flags))
622625
return NUD_NOARP;
623626
else if (has_expired(br, fdb))
624627
return NUD_STALE;
@@ -648,11 +651,11 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
648651
ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex;
649652
ndm->ndm_state = fdb_to_nud(br, fdb);
650653

651-
if (fdb->offloaded)
654+
if (test_bit(BR_FDB_OFFLOADED, &fdb->flags))
652655
ndm->ndm_flags |= NTF_OFFLOADED;
653-
if (fdb->added_by_external_learn)
656+
if (test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags))
654657
ndm->ndm_flags |= NTF_EXT_LEARNED;
655-
if (fdb->is_sticky)
658+
if (test_bit(BR_FDB_STICKY, &fdb->flags))
656659
ndm->ndm_flags |= NTF_STICKY;
657660

658661
if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->key.addr))
@@ -799,7 +802,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
799802
const u8 *addr, u16 state, u16 flags, u16 vid,
800803
u8 ndm_flags)
801804
{
802-
u8 is_sticky = !!(ndm_flags & NTF_STICKY);
805+
bool is_sticky = !!(ndm_flags & NTF_STICKY);
803806
struct net_bridge_fdb_entry *fdb;
804807
bool modified = false;
805808

@@ -823,7 +826,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
823826
if (!(flags & NLM_F_CREATE))
824827
return -ENOENT;
825828

826-
fdb = fdb_create(br, source, addr, vid, 0, 0);
829+
fdb = fdb_create(br, source, addr, vid, 0);
827830
if (!fdb)
828831
return -ENOMEM;
829832

@@ -840,34 +843,28 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
840843

841844
if (fdb_to_nud(br, fdb) != state) {
842845
if (state & NUD_PERMANENT) {
843-
fdb->is_local = 1;
844-
if (!fdb->is_static) {
845-
fdb->is_static = 1;
846+
set_bit(BR_FDB_LOCAL, &fdb->flags);
847+
if (!test_and_set_bit(BR_FDB_STATIC, &fdb->flags))
846848
fdb_add_hw_addr(br, addr);
847-
}
848849
} else if (state & NUD_NOARP) {
849-
fdb->is_local = 0;
850-
if (!fdb->is_static) {
851-
fdb->is_static = 1;
850+
clear_bit(BR_FDB_LOCAL, &fdb->flags);
851+
if (!test_and_set_bit(BR_FDB_STATIC, &fdb->flags))
852852
fdb_add_hw_addr(br, addr);
853-
}
854853
} else {
855-
fdb->is_local = 0;
856-
if (fdb->is_static) {
857-
fdb->is_static = 0;
854+
clear_bit(BR_FDB_LOCAL, &fdb->flags);
855+
if (test_and_clear_bit(BR_FDB_STATIC, &fdb->flags))
858856
fdb_del_hw_addr(br, addr);
859-
}
860857
}
861858

862859
modified = true;
863860
}
864861

865-
if (is_sticky != fdb->is_sticky) {
866-
fdb->is_sticky = is_sticky;
862+
if (is_sticky != test_bit(BR_FDB_STICKY, &fdb->flags)) {
863+
change_bit(BR_FDB_STICKY, &fdb->flags);
867864
modified = true;
868865
}
869866

870-
fdb->added_by_user = 1;
867+
set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
871868

872869
fdb->used = jiffies;
873870
if (modified) {
@@ -1064,7 +1061,7 @@ int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p)
10641061
rcu_read_lock();
10651062
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
10661063
/* We only care for static entries */
1067-
if (!f->is_static)
1064+
if (!test_bit(BR_FDB_STATIC, &f->flags))
10681065
continue;
10691066
err = dev_uc_add(p->dev, f->key.addr.addr);
10701067
if (err)
@@ -1078,7 +1075,7 @@ int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p)
10781075
rollback:
10791076
hlist_for_each_entry_rcu(tmp, &br->fdb_list, fdb_node) {
10801077
/* We only care for static entries */
1081-
if (!tmp->is_static)
1078+
if (!test_bit(BR_FDB_STATIC, &tmp->flags))
10821079
continue;
10831080
if (tmp == f)
10841081
break;
@@ -1097,7 +1094,7 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
10971094
rcu_read_lock();
10981095
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
10991096
/* We only care for static entries */
1100-
if (!f->is_static)
1097+
if (!test_bit(BR_FDB_STATIC, &f->flags))
11011098
continue;
11021099

11031100
dev_uc_del(p->dev, f->key.addr.addr);
@@ -1119,14 +1116,14 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
11191116

11201117
fdb = br_fdb_find(br, addr, vid);
11211118
if (!fdb) {
1122-
fdb = fdb_create(br, p, addr, vid, 0, 0);
1119+
fdb = fdb_create(br, p, addr, vid, 0);
11231120
if (!fdb) {
11241121
err = -ENOMEM;
11251122
goto err_unlock;
11261123
}
11271124
if (swdev_notify)
1128-
fdb->added_by_user = 1;
1129-
fdb->added_by_external_learn = 1;
1125+
set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
1126+
set_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags);
11301127
fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify);
11311128
} else {
11321129
fdb->updated = jiffies;
@@ -1136,17 +1133,17 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
11361133
modified = true;
11371134
}
11381135

1139-
if (fdb->added_by_external_learn) {
1136+
if (test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags)) {
11401137
/* Refresh entry */
11411138
fdb->used = jiffies;
1142-
} else if (!fdb->added_by_user) {
1139+
} else if (!test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags)) {
11431140
/* Take over SW learned entry */
1144-
fdb->added_by_external_learn = 1;
1141+
set_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags);
11451142
modified = true;
11461143
}
11471144

11481145
if (swdev_notify)
1149-
fdb->added_by_user = 1;
1146+
set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
11501147

11511148
if (modified)
11521149
fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify);
@@ -1168,7 +1165,7 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
11681165
spin_lock_bh(&br->hash_lock);
11691166

11701167
fdb = br_fdb_find(br, addr, vid);
1171-
if (fdb && fdb->added_by_external_learn)
1168+
if (fdb && test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags))
11721169
fdb_delete(br, fdb, swdev_notify);
11731170
else
11741171
err = -ENOENT;
@@ -1186,8 +1183,8 @@ void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p,
11861183
spin_lock_bh(&br->hash_lock);
11871184

11881185
fdb = br_fdb_find(br, addr, vid);
1189-
if (fdb)
1190-
fdb->offloaded = offloaded;
1186+
if (fdb && offloaded != test_bit(BR_FDB_OFFLOADED, &fdb->flags))
1187+
change_bit(BR_FDB_OFFLOADED, &fdb->flags);
11911188

11921189
spin_unlock_bh(&br->hash_lock);
11931190
}
@@ -1206,7 +1203,7 @@ void br_fdb_clear_offload(const struct net_device *dev, u16 vid)
12061203
spin_lock_bh(&p->br->hash_lock);
12071204
hlist_for_each_entry(f, &p->br->fdb_list, fdb_node) {
12081205
if (f->dst == p && f->key.vlan_id == vid)
1209-
f->offloaded = 0;
1206+
clear_bit(BR_FDB_OFFLOADED, &f->flags);
12101207
}
12111208
spin_unlock_bh(&p->br->hash_lock);
12121209
}

0 commit comments

Comments
 (0)