Commit 9014fc31 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 8466a57d 3fb01a31
...@@ -75,8 +75,9 @@ static inline unsigned long hold_time(const struct net_bridge *br) ...@@ -75,8 +75,9 @@ static inline unsigned long hold_time(const struct net_bridge *br)
static inline int has_expired(const struct net_bridge *br, static inline int has_expired(const struct net_bridge *br,
const struct net_bridge_fdb_entry *fdb) const struct net_bridge_fdb_entry *fdb)
{ {
return !fdb->is_static && !fdb->added_by_external_learn && return !test_bit(BR_FDB_STATIC, &fdb->flags) &&
time_before_eq(fdb->updated + hold_time(br), jiffies); !test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags) &&
time_before_eq(fdb->updated + hold_time(br), jiffies);
} }
static void fdb_rcu_free(struct rcu_head *head) 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, ...@@ -197,7 +198,7 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f,
{ {
trace_fdb_delete(br, f); trace_fdb_delete(br, f);
if (f->is_static) if (test_bit(BR_FDB_STATIC, &f->flags))
fdb_del_hw_addr(br, f->key.addr.addr); fdb_del_hw_addr(br, f->key.addr.addr);
hlist_del_init_rcu(&f->fdb_node); hlist_del_init_rcu(&f->fdb_node);
...@@ -224,7 +225,7 @@ static void fdb_delete_local(struct net_bridge *br, ...@@ -224,7 +225,7 @@ static void fdb_delete_local(struct net_bridge *br,
if (op != p && ether_addr_equal(op->dev->dev_addr, addr) && if (op != p && ether_addr_equal(op->dev->dev_addr, addr) &&
(!vid || br_vlan_find(vg, vid))) { (!vid || br_vlan_find(vg, vid))) {
f->dst = op; f->dst = op;
f->added_by_user = 0; clear_bit(BR_FDB_ADDED_BY_USER, &f->flags);
return; return;
} }
} }
...@@ -235,7 +236,7 @@ static void fdb_delete_local(struct net_bridge *br, ...@@ -235,7 +236,7 @@ static void fdb_delete_local(struct net_bridge *br,
if (p && ether_addr_equal(br->dev->dev_addr, addr) && if (p && ether_addr_equal(br->dev->dev_addr, addr) &&
(!vid || (v && br_vlan_should_use(v)))) { (!vid || (v && br_vlan_should_use(v)))) {
f->dst = NULL; f->dst = NULL;
f->added_by_user = 0; clear_bit(BR_FDB_ADDED_BY_USER, &f->flags);
return; return;
} }
...@@ -250,7 +251,8 @@ void br_fdb_find_delete_local(struct net_bridge *br, ...@@ -250,7 +251,8 @@ void br_fdb_find_delete_local(struct net_bridge *br,
spin_lock_bh(&br->hash_lock); spin_lock_bh(&br->hash_lock);
f = br_fdb_find(br, addr, vid); f = br_fdb_find(br, addr, vid);
if (f && f->is_local && !f->added_by_user && f->dst == p) if (f && test_bit(BR_FDB_LOCAL, &f->flags) &&
!test_bit(BR_FDB_ADDED_BY_USER, &f->flags) && f->dst == p)
fdb_delete_local(br, p, f); fdb_delete_local(br, p, f);
spin_unlock_bh(&br->hash_lock); spin_unlock_bh(&br->hash_lock);
} }
...@@ -265,7 +267,8 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr) ...@@ -265,7 +267,8 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
spin_lock_bh(&br->hash_lock); spin_lock_bh(&br->hash_lock);
vg = nbp_vlan_group(p); vg = nbp_vlan_group(p);
hlist_for_each_entry(f, &br->fdb_list, fdb_node) { hlist_for_each_entry(f, &br->fdb_list, fdb_node) {
if (f->dst == p && f->is_local && !f->added_by_user) { if (f->dst == p && test_bit(BR_FDB_LOCAL, &f->flags) &&
!test_bit(BR_FDB_ADDED_BY_USER, &f->flags)) {
/* delete old one */ /* delete old one */
fdb_delete_local(br, p, f); fdb_delete_local(br, p, f);
...@@ -306,7 +309,8 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) ...@@ -306,7 +309,8 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
/* If old entry was unassociated with any port, then delete it. */ /* If old entry was unassociated with any port, then delete it. */
f = br_fdb_find(br, br->dev->dev_addr, 0); f = br_fdb_find(br, br->dev->dev_addr, 0);
if (f && f->is_local && !f->dst && !f->added_by_user) if (f && test_bit(BR_FDB_LOCAL, &f->flags) &&
!f->dst && !test_bit(BR_FDB_ADDED_BY_USER, &f->flags))
fdb_delete_local(br, NULL, f); fdb_delete_local(br, NULL, f);
fdb_insert(br, NULL, newaddr, 0); fdb_insert(br, NULL, newaddr, 0);
...@@ -321,7 +325,8 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr) ...@@ -321,7 +325,8 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
if (!br_vlan_should_use(v)) if (!br_vlan_should_use(v))
continue; continue;
f = br_fdb_find(br, br->dev->dev_addr, v->vid); f = br_fdb_find(br, br->dev->dev_addr, v->vid);
if (f && f->is_local && !f->dst && !f->added_by_user) if (f && test_bit(BR_FDB_LOCAL, &f->flags) &&
!f->dst && !test_bit(BR_FDB_ADDED_BY_USER, &f->flags))
fdb_delete_local(br, NULL, f); fdb_delete_local(br, NULL, f);
fdb_insert(br, NULL, newaddr, v->vid); fdb_insert(br, NULL, newaddr, v->vid);
} }
...@@ -346,7 +351,8 @@ void br_fdb_cleanup(struct work_struct *work) ...@@ -346,7 +351,8 @@ void br_fdb_cleanup(struct work_struct *work)
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
unsigned long this_timer; unsigned long this_timer;
if (f->is_static || f->added_by_external_learn) if (test_bit(BR_FDB_STATIC, &f->flags) ||
test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &f->flags))
continue; continue;
this_timer = f->updated + delay; this_timer = f->updated + delay;
if (time_after(this_timer, now)) { if (time_after(this_timer, now)) {
...@@ -373,7 +379,7 @@ void br_fdb_flush(struct net_bridge *br) ...@@ -373,7 +379,7 @@ void br_fdb_flush(struct net_bridge *br)
spin_lock_bh(&br->hash_lock); spin_lock_bh(&br->hash_lock);
hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) { hlist_for_each_entry_safe(f, tmp, &br->fdb_list, fdb_node) {
if (!f->is_static) if (!test_bit(BR_FDB_STATIC, &f->flags))
fdb_delete(br, f, true); fdb_delete(br, f, true);
} }
spin_unlock_bh(&br->hash_lock); spin_unlock_bh(&br->hash_lock);
...@@ -397,10 +403,11 @@ void br_fdb_delete_by_port(struct net_bridge *br, ...@@ -397,10 +403,11 @@ void br_fdb_delete_by_port(struct net_bridge *br,
continue; continue;
if (!do_all) if (!do_all)
if (f->is_static || (vid && f->key.vlan_id != vid)) if (test_bit(BR_FDB_STATIC, &f->flags) ||
(vid && f->key.vlan_id != vid))
continue; continue;
if (f->is_local) if (test_bit(BR_FDB_LOCAL, &f->flags))
fdb_delete_local(br, p, f); fdb_delete_local(br, p, f);
else else
fdb_delete(br, f, true); fdb_delete(br, f, true);
...@@ -469,8 +476,8 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf, ...@@ -469,8 +476,8 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
fe->port_no = f->dst->port_no; fe->port_no = f->dst->port_no;
fe->port_hi = f->dst->port_no >> 8; fe->port_hi = f->dst->port_no >> 8;
fe->is_local = f->is_local; fe->is_local = test_bit(BR_FDB_LOCAL, &f->flags);
if (!f->is_static) if (!test_bit(BR_FDB_STATIC, &f->flags))
fe->ageing_timer_value = jiffies_delta_to_clock_t(jiffies - f->updated); fe->ageing_timer_value = jiffies_delta_to_clock_t(jiffies - f->updated);
++fe; ++fe;
++num; ++num;
...@@ -484,8 +491,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br, ...@@ -484,8 +491,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br,
struct net_bridge_port *source, struct net_bridge_port *source,
const unsigned char *addr, const unsigned char *addr,
__u16 vid, __u16 vid,
unsigned char is_local, unsigned long flags)
unsigned char is_static)
{ {
struct net_bridge_fdb_entry *fdb; struct net_bridge_fdb_entry *fdb;
...@@ -494,12 +500,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br, ...@@ -494,12 +500,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br,
memcpy(fdb->key.addr.addr, addr, ETH_ALEN); memcpy(fdb->key.addr.addr, addr, ETH_ALEN);
fdb->dst = source; fdb->dst = source;
fdb->key.vlan_id = vid; fdb->key.vlan_id = vid;
fdb->is_local = is_local; fdb->flags = flags;
fdb->is_static = is_static;
fdb->added_by_user = 0;
fdb->added_by_external_learn = 0;
fdb->offloaded = 0;
fdb->is_sticky = 0;
fdb->updated = fdb->used = jiffies; fdb->updated = fdb->used = jiffies;
if (rhashtable_lookup_insert_fast(&br->fdb_hash_tbl, if (rhashtable_lookup_insert_fast(&br->fdb_hash_tbl,
&fdb->rhnode, &fdb->rhnode,
...@@ -526,14 +527,15 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, ...@@ -526,14 +527,15 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
/* it is okay to have multiple ports with same /* it is okay to have multiple ports with same
* address, just use the first one. * address, just use the first one.
*/ */
if (fdb->is_local) if (test_bit(BR_FDB_LOCAL, &fdb->flags))
return 0; return 0;
br_warn(br, "adding interface %s with same address as a received packet (addr:%pM, vlan:%u)\n", br_warn(br, "adding interface %s with same address as a received packet (addr:%pM, vlan:%u)\n",
source ? source->dev->name : br->dev->name, addr, vid); source ? source->dev->name : br->dev->name, addr, vid);
fdb_delete(br, fdb, true); fdb_delete(br, fdb, true);
} }
fdb = fdb_create(br, source, addr, vid, 1, 1); fdb = fdb_create(br, source, addr, vid,
BIT(BR_FDB_LOCAL) | BIT(BR_FDB_STATIC));
if (!fdb) if (!fdb)
return -ENOMEM; return -ENOMEM;
...@@ -572,7 +574,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, ...@@ -572,7 +574,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
fdb = fdb_find_rcu(&br->fdb_hash_tbl, addr, vid); fdb = fdb_find_rcu(&br->fdb_hash_tbl, addr, vid);
if (likely(fdb)) { if (likely(fdb)) {
/* attempt to update an entry for a local interface */ /* attempt to update an entry for a local interface */
if (unlikely(fdb->is_local)) { if (unlikely(test_bit(BR_FDB_LOCAL, &fdb->flags))) {
if (net_ratelimit()) if (net_ratelimit())
br_warn(br, "received packet on %s with own address as source address (addr:%pM, vlan:%u)\n", br_warn(br, "received packet on %s with own address as source address (addr:%pM, vlan:%u)\n",
source->dev->name, addr, vid); source->dev->name, addr, vid);
...@@ -580,17 +582,18 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, ...@@ -580,17 +582,18 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
unsigned long now = jiffies; unsigned long now = jiffies;
/* fastpath: update of existing entry */ /* fastpath: update of existing entry */
if (unlikely(source != fdb->dst && !fdb->is_sticky)) { if (unlikely(source != fdb->dst &&
!test_bit(BR_FDB_STICKY, &fdb->flags))) {
fdb->dst = source; fdb->dst = source;
fdb_modified = true; fdb_modified = true;
/* Take over HW learned entry */ /* Take over HW learned entry */
if (unlikely(fdb->added_by_external_learn)) test_and_clear_bit(BR_FDB_ADDED_BY_EXT_LEARN,
fdb->added_by_external_learn = 0; &fdb->flags);
} }
if (now != fdb->updated) if (now != fdb->updated)
fdb->updated = now; fdb->updated = now;
if (unlikely(added_by_user)) if (unlikely(added_by_user))
fdb->added_by_user = 1; set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
if (unlikely(fdb_modified)) { if (unlikely(fdb_modified)) {
trace_br_fdb_update(br, source, addr, vid, added_by_user); trace_br_fdb_update(br, source, addr, vid, added_by_user);
fdb_notify(br, fdb, RTM_NEWNEIGH, true); fdb_notify(br, fdb, RTM_NEWNEIGH, true);
...@@ -598,10 +601,10 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, ...@@ -598,10 +601,10 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
} }
} else { } else {
spin_lock(&br->hash_lock); spin_lock(&br->hash_lock);
fdb = fdb_create(br, source, addr, vid, 0, 0); fdb = fdb_create(br, source, addr, vid, 0);
if (fdb) { if (fdb) {
if (unlikely(added_by_user)) if (unlikely(added_by_user))
fdb->added_by_user = 1; set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
trace_br_fdb_update(br, source, addr, vid, trace_br_fdb_update(br, source, addr, vid,
added_by_user); added_by_user);
fdb_notify(br, fdb, RTM_NEWNEIGH, true); fdb_notify(br, fdb, RTM_NEWNEIGH, true);
...@@ -616,9 +619,9 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, ...@@ -616,9 +619,9 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
static int fdb_to_nud(const struct net_bridge *br, static int fdb_to_nud(const struct net_bridge *br,
const struct net_bridge_fdb_entry *fdb) const struct net_bridge_fdb_entry *fdb)
{ {
if (fdb->is_local) if (test_bit(BR_FDB_LOCAL, &fdb->flags))
return NUD_PERMANENT; return NUD_PERMANENT;
else if (fdb->is_static) else if (test_bit(BR_FDB_STATIC, &fdb->flags))
return NUD_NOARP; return NUD_NOARP;
else if (has_expired(br, fdb)) else if (has_expired(br, fdb))
return NUD_STALE; return NUD_STALE;
...@@ -648,11 +651,11 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br, ...@@ -648,11 +651,11 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex; ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex;
ndm->ndm_state = fdb_to_nud(br, fdb); ndm->ndm_state = fdb_to_nud(br, fdb);
if (fdb->offloaded) if (test_bit(BR_FDB_OFFLOADED, &fdb->flags))
ndm->ndm_flags |= NTF_OFFLOADED; ndm->ndm_flags |= NTF_OFFLOADED;
if (fdb->added_by_external_learn) if (test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags))
ndm->ndm_flags |= NTF_EXT_LEARNED; ndm->ndm_flags |= NTF_EXT_LEARNED;
if (fdb->is_sticky) if (test_bit(BR_FDB_STICKY, &fdb->flags))
ndm->ndm_flags |= NTF_STICKY; ndm->ndm_flags |= NTF_STICKY;
if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->key.addr)) 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, ...@@ -799,7 +802,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
const u8 *addr, u16 state, u16 flags, u16 vid, const u8 *addr, u16 state, u16 flags, u16 vid,
u8 ndm_flags) u8 ndm_flags)
{ {
u8 is_sticky = !!(ndm_flags & NTF_STICKY); bool is_sticky = !!(ndm_flags & NTF_STICKY);
struct net_bridge_fdb_entry *fdb; struct net_bridge_fdb_entry *fdb;
bool modified = false; bool modified = false;
...@@ -823,7 +826,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, ...@@ -823,7 +826,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
if (!(flags & NLM_F_CREATE)) if (!(flags & NLM_F_CREATE))
return -ENOENT; return -ENOENT;
fdb = fdb_create(br, source, addr, vid, 0, 0); fdb = fdb_create(br, source, addr, vid, 0);
if (!fdb) if (!fdb)
return -ENOMEM; return -ENOMEM;
...@@ -840,34 +843,28 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, ...@@ -840,34 +843,28 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
if (fdb_to_nud(br, fdb) != state) { if (fdb_to_nud(br, fdb) != state) {
if (state & NUD_PERMANENT) { if (state & NUD_PERMANENT) {
fdb->is_local = 1; set_bit(BR_FDB_LOCAL, &fdb->flags);
if (!fdb->is_static) { if (!test_and_set_bit(BR_FDB_STATIC, &fdb->flags))
fdb->is_static = 1;
fdb_add_hw_addr(br, addr); fdb_add_hw_addr(br, addr);
}
} else if (state & NUD_NOARP) { } else if (state & NUD_NOARP) {
fdb->is_local = 0; clear_bit(BR_FDB_LOCAL, &fdb->flags);
if (!fdb->is_static) { if (!test_and_set_bit(BR_FDB_STATIC, &fdb->flags))
fdb->is_static = 1;
fdb_add_hw_addr(br, addr); fdb_add_hw_addr(br, addr);
}
} else { } else {
fdb->is_local = 0; clear_bit(BR_FDB_LOCAL, &fdb->flags);
if (fdb->is_static) { if (test_and_clear_bit(BR_FDB_STATIC, &fdb->flags))
fdb->is_static = 0;
fdb_del_hw_addr(br, addr); fdb_del_hw_addr(br, addr);
}
} }
modified = true; modified = true;
} }
if (is_sticky != fdb->is_sticky) { if (is_sticky != test_bit(BR_FDB_STICKY, &fdb->flags)) {
fdb->is_sticky = is_sticky; change_bit(BR_FDB_STICKY, &fdb->flags);
modified = true; modified = true;
} }
fdb->added_by_user = 1; set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
fdb->used = jiffies; fdb->used = jiffies;
if (modified) { if (modified) {
...@@ -1064,7 +1061,7 @@ int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p) ...@@ -1064,7 +1061,7 @@ int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p)
rcu_read_lock(); rcu_read_lock();
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
/* We only care for static entries */ /* We only care for static entries */
if (!f->is_static) if (!test_bit(BR_FDB_STATIC, &f->flags))
continue; continue;
err = dev_uc_add(p->dev, f->key.addr.addr); err = dev_uc_add(p->dev, f->key.addr.addr);
if (err) if (err)
...@@ -1078,7 +1075,7 @@ int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p) ...@@ -1078,7 +1075,7 @@ int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p)
rollback: rollback:
hlist_for_each_entry_rcu(tmp, &br->fdb_list, fdb_node) { hlist_for_each_entry_rcu(tmp, &br->fdb_list, fdb_node) {
/* We only care for static entries */ /* We only care for static entries */
if (!tmp->is_static) if (!test_bit(BR_FDB_STATIC, &tmp->flags))
continue; continue;
if (tmp == f) if (tmp == f)
break; break;
...@@ -1097,7 +1094,7 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p) ...@@ -1097,7 +1094,7 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
rcu_read_lock(); rcu_read_lock();
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
/* We only care for static entries */ /* We only care for static entries */
if (!f->is_static) if (!test_bit(BR_FDB_STATIC, &f->flags))
continue; continue;
dev_uc_del(p->dev, f->key.addr.addr); 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, ...@@ -1119,14 +1116,14 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
fdb = br_fdb_find(br, addr, vid); fdb = br_fdb_find(br, addr, vid);
if (!fdb) { if (!fdb) {
fdb = fdb_create(br, p, addr, vid, 0, 0); fdb = fdb_create(br, p, addr, vid, 0);
if (!fdb) { if (!fdb) {
err = -ENOMEM; err = -ENOMEM;
goto err_unlock; goto err_unlock;
} }
if (swdev_notify) if (swdev_notify)
fdb->added_by_user = 1; set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
fdb->added_by_external_learn = 1; set_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags);
fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify); fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify);
} else { } else {
fdb->updated = jiffies; fdb->updated = jiffies;
...@@ -1136,17 +1133,17 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, ...@@ -1136,17 +1133,17 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
modified = true; modified = true;
} }
if (fdb->added_by_external_learn) { if (test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags)) {
/* Refresh entry */ /* Refresh entry */
fdb->used = jiffies; fdb->used = jiffies;
} else if (!fdb->added_by_user) { } else if (!test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags)) {
/* Take over SW learned entry */ /* Take over SW learned entry */
fdb->added_by_external_learn = 1; set_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags);
modified = true; modified = true;
} }
if (swdev_notify) if (swdev_notify)
fdb->added_by_user = 1; set_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
if (modified) if (modified)
fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify); 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, ...@@ -1168,7 +1165,7 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
spin_lock_bh(&br->hash_lock); spin_lock_bh(&br->hash_lock);
fdb = br_fdb_find(br, addr, vid); fdb = br_fdb_find(br, addr, vid);
if (fdb && fdb->added_by_external_learn) if (fdb && test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags))
fdb_delete(br, fdb, swdev_notify); fdb_delete(br, fdb, swdev_notify);
else else
err = -ENOENT; err = -ENOENT;
...@@ -1186,8 +1183,8 @@ void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p, ...@@ -1186,8 +1183,8 @@ void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p,
spin_lock_bh(&br->hash_lock); spin_lock_bh(&br->hash_lock);
fdb = br_fdb_find(br, addr, vid); fdb = br_fdb_find(br, addr, vid);
if (fdb) if (fdb && offloaded != test_bit(BR_FDB_OFFLOADED, &fdb->flags))
fdb->offloaded = offloaded; change_bit(BR_FDB_OFFLOADED, &fdb->flags);
spin_unlock_bh(&br->hash_lock); spin_unlock_bh(&br->hash_lock);
} }
...@@ -1206,7 +1203,7 @@ void br_fdb_clear_offload(const struct net_device *dev, u16 vid) ...@@ -1206,7 +1203,7 @@ void br_fdb_clear_offload(const struct net_device *dev, u16 vid)
spin_lock_bh(&p->br->hash_lock); spin_lock_bh(&p->br->hash_lock);
hlist_for_each_entry(f, &p->br->fdb_list, fdb_node) { hlist_for_each_entry(f, &p->br->fdb_list, fdb_node) {
if (f->dst == p && f->key.vlan_id == vid) if (f->dst == p && f->key.vlan_id == vid)
f->offloaded = 0; clear_bit(BR_FDB_OFFLOADED, &f->flags);
} }
spin_unlock_bh(&p->br->hash_lock); spin_unlock_bh(&p->br->hash_lock);
} }
......
...@@ -151,7 +151,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb ...@@ -151,7 +151,7 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
if (dst) { if (dst) {
unsigned long now = jiffies; unsigned long now = jiffies;
if (dst->is_local) if (test_bit(BR_FDB_LOCAL, &dst->flags))
return br_pass_frame_up(skb); return br_pass_frame_up(skb);
if (now != dst->used) if (now != dst->used)
......
...@@ -172,6 +172,16 @@ struct net_bridge_vlan_group { ...@@ -172,6 +172,16 @@ struct net_bridge_vlan_group {
u16 pvid; u16 pvid;
}; };
/* bridge fdb flags */
enum {
BR_FDB_LOCAL,
BR_FDB_STATIC,
BR_FDB_STICKY,
BR_FDB_ADDED_BY_USER,
BR_FDB_ADDED_BY_EXT_LEARN,
BR_FDB_OFFLOADED,
};
struct net_bridge_fdb_key { struct net_bridge_fdb_key {
mac_addr addr; mac_addr addr;
u16 vlan_id; u16 vlan_id;
...@@ -183,12 +193,7 @@ struct net_bridge_fdb_entry { ...@@ -183,12 +193,7 @@ struct net_bridge_fdb_entry {
struct net_bridge_fdb_key key; struct net_bridge_fdb_key key;
struct hlist_node fdb_node; struct hlist_node fdb_node;
unsigned char is_local:1, unsigned long flags;
is_static:1,
is_sticky:1,
added_by_user:1,
added_by_external_learn:1,
offloaded:1;
/* write-heavy members should not affect lookups */ /* write-heavy members should not affect lookups */
unsigned long updated ____cacheline_aligned_in_smp; unsigned long updated ____cacheline_aligned_in_smp;
......
...@@ -129,15 +129,19 @@ br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) ...@@ -129,15 +129,19 @@ br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr, br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr,
fdb->key.vlan_id, fdb->key.vlan_id,
fdb->dst->dev, fdb->dst->dev,
fdb->added_by_user, test_bit(BR_FDB_ADDED_BY_USER,
fdb->offloaded); &fdb->flags),
test_bit(BR_FDB_OFFLOADED,
&fdb->flags));
break; break;
case RTM_NEWNEIGH: case RTM_NEWNEIGH:
br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr, br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr,
fdb->key.vlan_id, fdb->key.vlan_id,
fdb->dst->dev, fdb->dst->dev,
fdb->added_by_user, test_bit(BR_FDB_ADDED_BY_USER,
fdb->offloaded); &fdb->flags),
test_bit(BR_FDB_OFFLOADED,
&fdb->flags));
break; break;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment