Commit 62264f99 authored by David S. Miller's avatar David S. Miller

Merge branch 'bridge-FDB-Notify-about-removal-of-non-user-added-entries'

Petr Machata says:

====================
bridge: FDB: Notify about removal of non-user-added entries

Device drivers may generally need to keep in sync with bridge's FDB. In
particular, for its offload of tc mirror action where the mirrored-to
device is a gretap device, mlxsw needs to listen to a number of events,
FDB events among the others. SWITCHDEV_FDB_{ADD,DEL}_TO_DEVICE would be
a natural notification in that case.

However, for removal of FDB entries added due to device activity (as
opposed to explicit addition through "bridge fdb add" or similar), there
are no notifications.

Thus in patch #1, add the "added_by_user" field to switchdev
notifications sent for FDB activity. Adapt drivers to ignore activity on
non-user-added entries, to maintain the current behavior. Specifically
in case of mlxsw, allow mlxsw_sp_span_respin() call for any and all FDB
updates.

In patch #2, change the bridge driver to actually emit notifications for
these FDB entries. Take care not to send notification for bridge
updates that itself originate in SWITCHDEV_FDB_*_TO_BRIDGE events.

Changes from v1 to v2:
- Instead of introducing a new variant of fdb_delete(), add a new
  parameter to the existing function.
- Name the parameter swdev_notify, not notify.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 0e913f28 161d82de
...@@ -2270,6 +2270,8 @@ static void mlxsw_sp_switchdev_event_work(struct work_struct *work) ...@@ -2270,6 +2270,8 @@ static void mlxsw_sp_switchdev_event_work(struct work_struct *work)
switch (switchdev_work->event) { switch (switchdev_work->event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE: case SWITCHDEV_FDB_ADD_TO_DEVICE:
fdb_info = &switchdev_work->fdb_info; fdb_info = &switchdev_work->fdb_info;
if (!fdb_info->added_by_user)
break;
err = mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, true); err = mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, true);
if (err) if (err)
break; break;
...@@ -2279,6 +2281,8 @@ static void mlxsw_sp_switchdev_event_work(struct work_struct *work) ...@@ -2279,6 +2281,8 @@ static void mlxsw_sp_switchdev_event_work(struct work_struct *work)
break; break;
case SWITCHDEV_FDB_DEL_TO_DEVICE: case SWITCHDEV_FDB_DEL_TO_DEVICE:
fdb_info = &switchdev_work->fdb_info; fdb_info = &switchdev_work->fdb_info;
if (!fdb_info->added_by_user)
break;
mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, false); mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, false);
break; break;
case SWITCHDEV_FDB_ADD_TO_BRIDGE: /* fall through */ case SWITCHDEV_FDB_ADD_TO_BRIDGE: /* fall through */
......
...@@ -2783,6 +2783,8 @@ static int rocker_switchdev_event(struct notifier_block *unused, ...@@ -2783,6 +2783,8 @@ static int rocker_switchdev_event(struct notifier_block *unused,
switch (event) { switch (event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */ case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */
case SWITCHDEV_FDB_DEL_TO_DEVICE: case SWITCHDEV_FDB_DEL_TO_DEVICE:
if (!fdb_info->added_by_user)
break;
memcpy(&switchdev_work->fdb_info, ptr, memcpy(&switchdev_work->fdb_info, ptr,
sizeof(switchdev_work->fdb_info)); sizeof(switchdev_work->fdb_info));
switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
......
...@@ -155,6 +155,7 @@ struct switchdev_notifier_fdb_info { ...@@ -155,6 +155,7 @@ struct switchdev_notifier_fdb_info {
struct switchdev_notifier_info info; /* must be first */ struct switchdev_notifier_info info; /* must be first */
const unsigned char *addr; const unsigned char *addr;
u16 vid; u16 vid;
bool added_by_user;
}; };
static inline struct net_device * static inline struct net_device *
......
...@@ -145,7 +145,7 @@ static int br_switchdev_event(struct notifier_block *unused, ...@@ -145,7 +145,7 @@ static int br_switchdev_event(struct notifier_block *unused,
case SWITCHDEV_FDB_ADD_TO_BRIDGE: case SWITCHDEV_FDB_ADD_TO_BRIDGE:
fdb_info = ptr; fdb_info = ptr;
err = br_fdb_external_learn_add(br, p, fdb_info->addr, err = br_fdb_external_learn_add(br, p, fdb_info->addr,
fdb_info->vid); fdb_info->vid, false);
if (err) { if (err) {
err = notifier_from_errno(err); err = notifier_from_errno(err);
break; break;
...@@ -156,7 +156,7 @@ static int br_switchdev_event(struct notifier_block *unused, ...@@ -156,7 +156,7 @@ static int br_switchdev_event(struct notifier_block *unused,
case SWITCHDEV_FDB_DEL_TO_BRIDGE: case SWITCHDEV_FDB_DEL_TO_BRIDGE:
fdb_info = ptr; fdb_info = ptr;
err = br_fdb_external_learn_del(br, p, fdb_info->addr, err = br_fdb_external_learn_del(br, p, fdb_info->addr,
fdb_info->vid); fdb_info->vid, false);
if (err) if (err)
err = notifier_from_errno(err); err = notifier_from_errno(err);
break; break;
......
...@@ -40,7 +40,7 @@ static struct kmem_cache *br_fdb_cache __read_mostly; ...@@ -40,7 +40,7 @@ static struct kmem_cache *br_fdb_cache __read_mostly;
static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
const unsigned char *addr, u16 vid); const unsigned char *addr, u16 vid);
static void fdb_notify(struct net_bridge *br, static void fdb_notify(struct net_bridge *br,
const struct net_bridge_fdb_entry *, int); const struct net_bridge_fdb_entry *, int, bool);
int __init br_fdb_init(void) int __init br_fdb_init(void)
{ {
...@@ -195,7 +195,8 @@ static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr) ...@@ -195,7 +195,8 @@ static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr)
} }
} }
static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f,
bool swdev_notify)
{ {
trace_fdb_delete(br, f); trace_fdb_delete(br, f);
...@@ -205,7 +206,7 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) ...@@ -205,7 +206,7 @@ static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
hlist_del_init_rcu(&f->fdb_node); hlist_del_init_rcu(&f->fdb_node);
rhashtable_remove_fast(&br->fdb_hash_tbl, &f->rhnode, rhashtable_remove_fast(&br->fdb_hash_tbl, &f->rhnode,
br_fdb_rht_params); br_fdb_rht_params);
fdb_notify(br, f, RTM_DELNEIGH); fdb_notify(br, f, RTM_DELNEIGH, swdev_notify);
call_rcu(&f->rcu, fdb_rcu_free); call_rcu(&f->rcu, fdb_rcu_free);
} }
...@@ -241,7 +242,7 @@ static void fdb_delete_local(struct net_bridge *br, ...@@ -241,7 +242,7 @@ static void fdb_delete_local(struct net_bridge *br,
return; return;
} }
fdb_delete(br, f); fdb_delete(br, f, true);
} }
void br_fdb_find_delete_local(struct net_bridge *br, void br_fdb_find_delete_local(struct net_bridge *br,
...@@ -356,7 +357,7 @@ void br_fdb_cleanup(struct work_struct *work) ...@@ -356,7 +357,7 @@ void br_fdb_cleanup(struct work_struct *work)
} else { } else {
spin_lock_bh(&br->hash_lock); spin_lock_bh(&br->hash_lock);
if (!hlist_unhashed(&f->fdb_node)) if (!hlist_unhashed(&f->fdb_node))
fdb_delete(br, f); fdb_delete(br, f, true);
spin_unlock_bh(&br->hash_lock); spin_unlock_bh(&br->hash_lock);
} }
} }
...@@ -376,7 +377,7 @@ void br_fdb_flush(struct net_bridge *br) ...@@ -376,7 +377,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 (!f->is_static)
fdb_delete(br, f); fdb_delete(br, f, true);
} }
spin_unlock_bh(&br->hash_lock); spin_unlock_bh(&br->hash_lock);
} }
...@@ -405,7 +406,7 @@ void br_fdb_delete_by_port(struct net_bridge *br, ...@@ -405,7 +406,7 @@ void br_fdb_delete_by_port(struct net_bridge *br,
if (f->is_local) if (f->is_local)
fdb_delete_local(br, p, f); fdb_delete_local(br, p, f);
else else
fdb_delete(br, f); fdb_delete(br, f, true);
} }
spin_unlock_bh(&br->hash_lock); spin_unlock_bh(&br->hash_lock);
} }
...@@ -531,7 +532,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, ...@@ -531,7 +532,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
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); fdb_delete(br, fdb, true);
} }
fdb = fdb_create(br, source, addr, vid, 1, 1); fdb = fdb_create(br, source, addr, vid, 1, 1);
...@@ -539,7 +540,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source, ...@@ -539,7 +540,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
return -ENOMEM; return -ENOMEM;
fdb_add_hw_addr(br, addr); fdb_add_hw_addr(br, addr);
fdb_notify(br, fdb, RTM_NEWNEIGH); fdb_notify(br, fdb, RTM_NEWNEIGH, true);
return 0; return 0;
} }
...@@ -594,7 +595,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, ...@@ -594,7 +595,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
fdb->added_by_user = 1; fdb->added_by_user = 1;
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); fdb_notify(br, fdb, RTM_NEWNEIGH, true);
} }
} }
} else { } else {
...@@ -605,7 +606,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, ...@@ -605,7 +606,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
fdb->added_by_user = 1; fdb->added_by_user = 1;
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); fdb_notify(br, fdb, RTM_NEWNEIGH, true);
} }
/* else we lose race and someone else inserts /* else we lose race and someone else inserts
* it first, don't bother updating * it first, don't bother updating
...@@ -687,13 +688,15 @@ static inline size_t fdb_nlmsg_size(void) ...@@ -687,13 +688,15 @@ static inline size_t fdb_nlmsg_size(void)
} }
static void fdb_notify(struct net_bridge *br, static void fdb_notify(struct net_bridge *br,
const struct net_bridge_fdb_entry *fdb, int type) const struct net_bridge_fdb_entry *fdb, int type,
bool swdev_notify)
{ {
struct net *net = dev_net(br->dev); struct net *net = dev_net(br->dev);
struct sk_buff *skb; struct sk_buff *skb;
int err = -ENOBUFS; int err = -ENOBUFS;
br_switchdev_fdb_notify(fdb, type); if (swdev_notify)
br_switchdev_fdb_notify(fdb, type);
skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC); skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC);
if (skb == NULL) if (skb == NULL)
...@@ -832,7 +835,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source, ...@@ -832,7 +835,7 @@ static int fdb_add_entry(struct net_bridge *br, struct net_bridge_port *source,
fdb->used = jiffies; fdb->used = jiffies;
if (modified) { if (modified) {
fdb->updated = jiffies; fdb->updated = jiffies;
fdb_notify(br, fdb, RTM_NEWNEIGH); fdb_notify(br, fdb, RTM_NEWNEIGH, true);
} }
return 0; return 0;
...@@ -856,7 +859,7 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br, ...@@ -856,7 +859,7 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge *br,
rcu_read_unlock(); rcu_read_unlock();
local_bh_enable(); local_bh_enable();
} else if (ndm->ndm_flags & NTF_EXT_LEARNED) { } else if (ndm->ndm_flags & NTF_EXT_LEARNED) {
err = br_fdb_external_learn_add(br, p, addr, vid); err = br_fdb_external_learn_add(br, p, addr, vid, true);
} else { } else {
spin_lock_bh(&br->hash_lock); spin_lock_bh(&br->hash_lock);
err = fdb_add_entry(br, p, addr, ndm->ndm_state, err = fdb_add_entry(br, p, addr, ndm->ndm_state,
...@@ -945,7 +948,7 @@ static int fdb_delete_by_addr_and_port(struct net_bridge *br, ...@@ -945,7 +948,7 @@ static int fdb_delete_by_addr_and_port(struct net_bridge *br,
if (!fdb || fdb->dst != p) if (!fdb || fdb->dst != p)
return -ENOENT; return -ENOENT;
fdb_delete(br, fdb); fdb_delete(br, fdb, true);
return 0; return 0;
} }
...@@ -1065,7 +1068,8 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p) ...@@ -1065,7 +1068,8 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
} }
int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
const unsigned char *addr, u16 vid) const unsigned char *addr, u16 vid,
bool swdev_notify)
{ {
struct net_bridge_fdb_entry *fdb; struct net_bridge_fdb_entry *fdb;
bool modified = false; bool modified = false;
...@@ -1083,7 +1087,7 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, ...@@ -1083,7 +1087,7 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
goto err_unlock; goto err_unlock;
} }
fdb->added_by_external_learn = 1; fdb->added_by_external_learn = 1;
fdb_notify(br, fdb, RTM_NEWNEIGH); fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify);
} else { } else {
fdb->updated = jiffies; fdb->updated = jiffies;
...@@ -1102,7 +1106,7 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, ...@@ -1102,7 +1106,7 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
} }
if (modified) if (modified)
fdb_notify(br, fdb, RTM_NEWNEIGH); fdb_notify(br, fdb, RTM_NEWNEIGH, swdev_notify);
} }
err_unlock: err_unlock:
...@@ -1112,7 +1116,8 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, ...@@ -1112,7 +1116,8 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
} }
int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p, int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
const unsigned char *addr, u16 vid) const unsigned char *addr, u16 vid,
bool swdev_notify)
{ {
struct net_bridge_fdb_entry *fdb; struct net_bridge_fdb_entry *fdb;
int err = 0; int err = 0;
...@@ -1121,7 +1126,7 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p, ...@@ -1121,7 +1126,7 @@ int br_fdb_external_learn_del(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 && fdb->added_by_external_learn) if (fdb && fdb->added_by_external_learn)
fdb_delete(br, fdb); fdb_delete(br, fdb, swdev_notify);
else else
err = -ENOENT; err = -ENOENT;
......
...@@ -553,9 +553,11 @@ int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, ...@@ -553,9 +553,11 @@ int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p); int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p); void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p);
int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p,
const unsigned char *addr, u16 vid); const unsigned char *addr, u16 vid,
bool swdev_notify);
int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p, int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
const unsigned char *addr, u16 vid); const unsigned char *addr, u16 vid,
bool swdev_notify);
void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p, void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p,
const unsigned char *addr, u16 vid); const unsigned char *addr, u16 vid);
......
...@@ -102,13 +102,15 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p, ...@@ -102,13 +102,15 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
static void static void
br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac, br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac,
u16 vid, struct net_device *dev) u16 vid, struct net_device *dev,
bool added_by_user)
{ {
struct switchdev_notifier_fdb_info info; struct switchdev_notifier_fdb_info info;
unsigned long notifier_type; unsigned long notifier_type;
info.addr = mac; info.addr = mac;
info.vid = vid; info.vid = vid;
info.added_by_user = added_by_user;
notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE; notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE;
call_switchdev_notifiers(notifier_type, dev, &info.info); call_switchdev_notifiers(notifier_type, dev, &info.info);
} }
...@@ -116,19 +118,21 @@ br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac, ...@@ -116,19 +118,21 @@ br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac,
void void
br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
{ {
if (!fdb->added_by_user || !fdb->dst) if (!fdb->dst)
return; return;
switch (type) { switch (type) {
case RTM_DELNEIGH: case RTM_DELNEIGH:
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);
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);
break; break;
} }
} }
...@@ -1441,6 +1441,7 @@ static int dsa_slave_switchdev_event(struct notifier_block *unused, ...@@ -1441,6 +1441,7 @@ static int dsa_slave_switchdev_event(struct notifier_block *unused,
unsigned long event, void *ptr) unsigned long event, void *ptr)
{ {
struct net_device *dev = switchdev_notifier_info_to_dev(ptr); struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
struct switchdev_notifier_fdb_info *fdb_info = ptr;
struct dsa_switchdev_event_work *switchdev_work; struct dsa_switchdev_event_work *switchdev_work;
if (!dsa_slave_dev_check(dev)) if (!dsa_slave_dev_check(dev))
...@@ -1458,8 +1459,10 @@ static int dsa_slave_switchdev_event(struct notifier_block *unused, ...@@ -1458,8 +1459,10 @@ static int dsa_slave_switchdev_event(struct notifier_block *unused,
switch (event) { switch (event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */ case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */
case SWITCHDEV_FDB_DEL_TO_DEVICE: case SWITCHDEV_FDB_DEL_TO_DEVICE:
if (!fdb_info->added_by_user)
break;
if (dsa_slave_switchdev_fdb_work_init(switchdev_work, if (dsa_slave_switchdev_fdb_work_init(switchdev_work,
ptr)) fdb_info))
goto err_fdb_work_init; goto err_fdb_work_init;
dev_hold(dev); dev_hold(dev);
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