Merge branch 'br-fdb-refactoring'
Vladimir Oltean says: ==================== Bridge FDB refactoring This series refactors the br_fdb.c, br_switchdev.c and switchdev.c files to offer the same level of functionality with a bit less code, and to clarify the purpose of some functions. No functional change intended. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
6487c81939
|
@ -299,28 +299,16 @@ void switchdev_port_fwd_mark_set(struct net_device *dev,
|
|||
struct net_device *group_dev,
|
||||
bool joining);
|
||||
|
||||
int switchdev_handle_fdb_add_to_device(struct net_device *dev,
|
||||
int switchdev_handle_fdb_event_to_device(struct net_device *dev, unsigned long event,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
bool (*foreign_dev_check_cb)(const struct net_device *dev,
|
||||
const struct net_device *foreign_dev),
|
||||
int (*add_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
|
||||
unsigned long event, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info),
|
||||
int (*lag_add_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info));
|
||||
|
||||
int switchdev_handle_fdb_del_to_device(struct net_device *dev,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
bool (*foreign_dev_check_cb)(const struct net_device *dev,
|
||||
const struct net_device *foreign_dev),
|
||||
int (*del_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info),
|
||||
int (*lag_del_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
int (*lag_mod_cb)(struct net_device *dev, struct net_device *orig_dev,
|
||||
unsigned long event, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info));
|
||||
|
||||
int switchdev_handle_port_obj_add(struct net_device *dev,
|
||||
|
@ -426,32 +414,16 @@ call_switchdev_blocking_notifiers(unsigned long val,
|
|||
}
|
||||
|
||||
static inline int
|
||||
switchdev_handle_fdb_add_to_device(struct net_device *dev,
|
||||
switchdev_handle_fdb_event_to_device(struct net_device *dev, unsigned long event,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
bool (*foreign_dev_check_cb)(const struct net_device *dev,
|
||||
const struct net_device *foreign_dev),
|
||||
int (*add_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
|
||||
unsigned long event, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info),
|
||||
int (*lag_add_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
switchdev_handle_fdb_del_to_device(struct net_device *dev,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
bool (*foreign_dev_check_cb)(const struct net_device *dev,
|
||||
const struct net_device *foreign_dev),
|
||||
int (*del_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info),
|
||||
int (*lag_del_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
int (*lag_mod_cb)(struct net_device *dev, struct net_device *orig_dev,
|
||||
unsigned long event, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info))
|
||||
{
|
||||
return 0;
|
||||
|
|
|
@ -32,10 +32,6 @@ static const struct rhashtable_params br_fdb_rht_params = {
|
|||
};
|
||||
|
||||
static struct kmem_cache *br_fdb_cache __read_mostly;
|
||||
static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const unsigned char *addr, u16 vid);
|
||||
static void fdb_notify(struct net_bridge *br,
|
||||
const struct net_bridge_fdb_entry *, int, bool);
|
||||
|
||||
int __init br_fdb_init(void)
|
||||
{
|
||||
|
@ -87,6 +83,128 @@ static void fdb_rcu_free(struct rcu_head *head)
|
|||
kmem_cache_free(br_fdb_cache, ent);
|
||||
}
|
||||
|
||||
static int fdb_to_nud(const struct net_bridge *br,
|
||||
const struct net_bridge_fdb_entry *fdb)
|
||||
{
|
||||
if (test_bit(BR_FDB_LOCAL, &fdb->flags))
|
||||
return NUD_PERMANENT;
|
||||
else if (test_bit(BR_FDB_STATIC, &fdb->flags))
|
||||
return NUD_NOARP;
|
||||
else if (has_expired(br, fdb))
|
||||
return NUD_STALE;
|
||||
else
|
||||
return NUD_REACHABLE;
|
||||
}
|
||||
|
||||
static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
|
||||
const struct net_bridge_fdb_entry *fdb,
|
||||
u32 portid, u32 seq, int type, unsigned int flags)
|
||||
{
|
||||
const struct net_bridge_port *dst = READ_ONCE(fdb->dst);
|
||||
unsigned long now = jiffies;
|
||||
struct nda_cacheinfo ci;
|
||||
struct nlmsghdr *nlh;
|
||||
struct ndmsg *ndm;
|
||||
|
||||
nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags);
|
||||
if (nlh == NULL)
|
||||
return -EMSGSIZE;
|
||||
|
||||
ndm = nlmsg_data(nlh);
|
||||
ndm->ndm_family = AF_BRIDGE;
|
||||
ndm->ndm_pad1 = 0;
|
||||
ndm->ndm_pad2 = 0;
|
||||
ndm->ndm_flags = 0;
|
||||
ndm->ndm_type = 0;
|
||||
ndm->ndm_ifindex = dst ? dst->dev->ifindex : br->dev->ifindex;
|
||||
ndm->ndm_state = fdb_to_nud(br, fdb);
|
||||
|
||||
if (test_bit(BR_FDB_OFFLOADED, &fdb->flags))
|
||||
ndm->ndm_flags |= NTF_OFFLOADED;
|
||||
if (test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags))
|
||||
ndm->ndm_flags |= NTF_EXT_LEARNED;
|
||||
if (test_bit(BR_FDB_STICKY, &fdb->flags))
|
||||
ndm->ndm_flags |= NTF_STICKY;
|
||||
|
||||
if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->key.addr))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(skb, NDA_MASTER, br->dev->ifindex))
|
||||
goto nla_put_failure;
|
||||
ci.ndm_used = jiffies_to_clock_t(now - fdb->used);
|
||||
ci.ndm_confirmed = 0;
|
||||
ci.ndm_updated = jiffies_to_clock_t(now - fdb->updated);
|
||||
ci.ndm_refcnt = 0;
|
||||
if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (fdb->key.vlan_id && nla_put(skb, NDA_VLAN, sizeof(u16),
|
||||
&fdb->key.vlan_id))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (test_bit(BR_FDB_NOTIFY, &fdb->flags)) {
|
||||
struct nlattr *nest = nla_nest_start(skb, NDA_FDB_EXT_ATTRS);
|
||||
u8 notify_bits = FDB_NOTIFY_BIT;
|
||||
|
||||
if (!nest)
|
||||
goto nla_put_failure;
|
||||
if (test_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags))
|
||||
notify_bits |= FDB_NOTIFY_INACTIVE_BIT;
|
||||
|
||||
if (nla_put_u8(skb, NFEA_ACTIVITY_NOTIFY, notify_bits)) {
|
||||
nla_nest_cancel(skb, nest);
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
nla_nest_end(skb, nest);
|
||||
}
|
||||
|
||||
nlmsg_end(skb, nlh);
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_cancel(skb, nlh);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static inline size_t fdb_nlmsg_size(void)
|
||||
{
|
||||
return NLMSG_ALIGN(sizeof(struct ndmsg))
|
||||
+ nla_total_size(ETH_ALEN) /* NDA_LLADDR */
|
||||
+ nla_total_size(sizeof(u32)) /* NDA_MASTER */
|
||||
+ nla_total_size(sizeof(u16)) /* NDA_VLAN */
|
||||
+ nla_total_size(sizeof(struct nda_cacheinfo))
|
||||
+ nla_total_size(0) /* NDA_FDB_EXT_ATTRS */
|
||||
+ nla_total_size(sizeof(u8)); /* NFEA_ACTIVITY_NOTIFY */
|
||||
}
|
||||
|
||||
static void fdb_notify(struct net_bridge *br,
|
||||
const struct net_bridge_fdb_entry *fdb, int type,
|
||||
bool swdev_notify)
|
||||
{
|
||||
struct net *net = dev_net(br->dev);
|
||||
struct sk_buff *skb;
|
||||
int err = -ENOBUFS;
|
||||
|
||||
if (swdev_notify)
|
||||
br_switchdev_fdb_notify(br, fdb, type);
|
||||
|
||||
skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
goto errout;
|
||||
|
||||
err = fdb_fill_info(skb, br, fdb, 0, 0, type, 0);
|
||||
if (err < 0) {
|
||||
/* -EMSGSIZE implies BUG in fdb_nlmsg_size() */
|
||||
WARN_ON(err == -EMSGSIZE);
|
||||
kfree_skb(skb);
|
||||
goto errout;
|
||||
}
|
||||
rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
|
||||
return;
|
||||
errout:
|
||||
rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
|
||||
}
|
||||
|
||||
static struct net_bridge_fdb_entry *fdb_find_rcu(struct rhashtable *tbl,
|
||||
const unsigned char *addr,
|
||||
__u16 vid)
|
||||
|
@ -257,6 +375,66 @@ void br_fdb_find_delete_local(struct net_bridge *br,
|
|||
spin_unlock_bh(&br->hash_lock);
|
||||
}
|
||||
|
||||
static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br,
|
||||
struct net_bridge_port *source,
|
||||
const unsigned char *addr,
|
||||
__u16 vid,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
int err;
|
||||
|
||||
fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC);
|
||||
if (!fdb)
|
||||
return NULL;
|
||||
|
||||
memcpy(fdb->key.addr.addr, addr, ETH_ALEN);
|
||||
WRITE_ONCE(fdb->dst, source);
|
||||
fdb->key.vlan_id = vid;
|
||||
fdb->flags = flags;
|
||||
fdb->updated = fdb->used = jiffies;
|
||||
err = rhashtable_lookup_insert_fast(&br->fdb_hash_tbl, &fdb->rhnode,
|
||||
br_fdb_rht_params);
|
||||
if (err) {
|
||||
kmem_cache_free(br_fdb_cache, fdb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hlist_add_head_rcu(&fdb->fdb_node, &br->fdb_list);
|
||||
|
||||
return fdb;
|
||||
}
|
||||
|
||||
static int fdb_add_local(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const unsigned char *addr, u16 vid)
|
||||
{
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
|
||||
if (!is_valid_ether_addr(addr))
|
||||
return -EINVAL;
|
||||
|
||||
fdb = br_fdb_find(br, addr, vid);
|
||||
if (fdb) {
|
||||
/* it is okay to have multiple ports with same
|
||||
* address, just use the first one.
|
||||
*/
|
||||
if (test_bit(BR_FDB_LOCAL, &fdb->flags))
|
||||
return 0;
|
||||
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);
|
||||
fdb_delete(br, fdb, true);
|
||||
}
|
||||
|
||||
fdb = fdb_create(br, source, addr, vid,
|
||||
BIT(BR_FDB_LOCAL) | BIT(BR_FDB_STATIC));
|
||||
if (!fdb)
|
||||
return -ENOMEM;
|
||||
|
||||
fdb_add_hw_addr(br, addr);
|
||||
fdb_notify(br, fdb, RTM_NEWNEIGH, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
|
||||
{
|
||||
struct net_bridge_vlan_group *vg;
|
||||
|
@ -283,7 +461,7 @@ void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr)
|
|||
|
||||
insert:
|
||||
/* insert new address, may fail if invalid address or dup. */
|
||||
fdb_insert(br, p, newaddr, 0);
|
||||
fdb_add_local(br, p, newaddr, 0);
|
||||
|
||||
if (!vg || !vg->num_vlans)
|
||||
goto done;
|
||||
|
@ -293,7 +471,7 @@ insert:
|
|||
* from under us.
|
||||
*/
|
||||
list_for_each_entry(v, &vg->vlan_list, vlist)
|
||||
fdb_insert(br, p, newaddr, v->vid);
|
||||
fdb_add_local(br, p, newaddr, v->vid);
|
||||
|
||||
done:
|
||||
spin_unlock_bh(&br->hash_lock);
|
||||
|
@ -313,7 +491,7 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
|
|||
!f->dst && !test_bit(BR_FDB_ADDED_BY_USER, &f->flags))
|
||||
fdb_delete_local(br, NULL, f);
|
||||
|
||||
fdb_insert(br, NULL, newaddr, 0);
|
||||
fdb_add_local(br, NULL, newaddr, 0);
|
||||
vg = br_vlan_group(br);
|
||||
if (!vg || !vg->num_vlans)
|
||||
goto out;
|
||||
|
@ -328,7 +506,7 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
|
|||
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_insert(br, NULL, newaddr, v->vid);
|
||||
fdb_add_local(br, NULL, newaddr, v->vid);
|
||||
}
|
||||
out:
|
||||
spin_unlock_bh(&br->hash_lock);
|
||||
|
@ -503,71 +681,14 @@ int br_fdb_fillbuf(struct net_bridge *br, void *buf,
|
|||
return num;
|
||||
}
|
||||
|
||||
static struct net_bridge_fdb_entry *fdb_create(struct net_bridge *br,
|
||||
struct net_bridge_port *source,
|
||||
const unsigned char *addr,
|
||||
__u16 vid,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
|
||||
fdb = kmem_cache_alloc(br_fdb_cache, GFP_ATOMIC);
|
||||
if (fdb) {
|
||||
memcpy(fdb->key.addr.addr, addr, ETH_ALEN);
|
||||
WRITE_ONCE(fdb->dst, source);
|
||||
fdb->key.vlan_id = vid;
|
||||
fdb->flags = flags;
|
||||
fdb->updated = fdb->used = jiffies;
|
||||
if (rhashtable_lookup_insert_fast(&br->fdb_hash_tbl,
|
||||
&fdb->rhnode,
|
||||
br_fdb_rht_params)) {
|
||||
kmem_cache_free(br_fdb_cache, fdb);
|
||||
fdb = NULL;
|
||||
} else {
|
||||
hlist_add_head_rcu(&fdb->fdb_node, &br->fdb_list);
|
||||
}
|
||||
}
|
||||
return fdb;
|
||||
}
|
||||
|
||||
static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const unsigned char *addr, u16 vid)
|
||||
{
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
|
||||
if (!is_valid_ether_addr(addr))
|
||||
return -EINVAL;
|
||||
|
||||
fdb = br_fdb_find(br, addr, vid);
|
||||
if (fdb) {
|
||||
/* it is okay to have multiple ports with same
|
||||
* address, just use the first one.
|
||||
*/
|
||||
if (test_bit(BR_FDB_LOCAL, &fdb->flags))
|
||||
return 0;
|
||||
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);
|
||||
fdb_delete(br, fdb, true);
|
||||
}
|
||||
|
||||
fdb = fdb_create(br, source, addr, vid,
|
||||
BIT(BR_FDB_LOCAL) | BIT(BR_FDB_STATIC));
|
||||
if (!fdb)
|
||||
return -ENOMEM;
|
||||
|
||||
fdb_add_hw_addr(br, addr);
|
||||
fdb_notify(br, fdb, RTM_NEWNEIGH, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add entry for local address of interface */
|
||||
int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const unsigned char *addr, u16 vid)
|
||||
int br_fdb_add_local(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const unsigned char *addr, u16 vid)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock_bh(&br->hash_lock);
|
||||
ret = fdb_insert(br, source, addr, vid);
|
||||
ret = fdb_add_local(br, source, addr, vid);
|
||||
spin_unlock_bh(&br->hash_lock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -638,182 +759,6 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
|
|||
}
|
||||
}
|
||||
|
||||
static int fdb_to_nud(const struct net_bridge *br,
|
||||
const struct net_bridge_fdb_entry *fdb)
|
||||
{
|
||||
if (test_bit(BR_FDB_LOCAL, &fdb->flags))
|
||||
return NUD_PERMANENT;
|
||||
else if (test_bit(BR_FDB_STATIC, &fdb->flags))
|
||||
return NUD_NOARP;
|
||||
else if (has_expired(br, fdb))
|
||||
return NUD_STALE;
|
||||
else
|
||||
return NUD_REACHABLE;
|
||||
}
|
||||
|
||||
static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
|
||||
const struct net_bridge_fdb_entry *fdb,
|
||||
u32 portid, u32 seq, int type, unsigned int flags)
|
||||
{
|
||||
const struct net_bridge_port *dst = READ_ONCE(fdb->dst);
|
||||
unsigned long now = jiffies;
|
||||
struct nda_cacheinfo ci;
|
||||
struct nlmsghdr *nlh;
|
||||
struct ndmsg *ndm;
|
||||
|
||||
nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags);
|
||||
if (nlh == NULL)
|
||||
return -EMSGSIZE;
|
||||
|
||||
ndm = nlmsg_data(nlh);
|
||||
ndm->ndm_family = AF_BRIDGE;
|
||||
ndm->ndm_pad1 = 0;
|
||||
ndm->ndm_pad2 = 0;
|
||||
ndm->ndm_flags = 0;
|
||||
ndm->ndm_type = 0;
|
||||
ndm->ndm_ifindex = dst ? dst->dev->ifindex : br->dev->ifindex;
|
||||
ndm->ndm_state = fdb_to_nud(br, fdb);
|
||||
|
||||
if (test_bit(BR_FDB_OFFLOADED, &fdb->flags))
|
||||
ndm->ndm_flags |= NTF_OFFLOADED;
|
||||
if (test_bit(BR_FDB_ADDED_BY_EXT_LEARN, &fdb->flags))
|
||||
ndm->ndm_flags |= NTF_EXT_LEARNED;
|
||||
if (test_bit(BR_FDB_STICKY, &fdb->flags))
|
||||
ndm->ndm_flags |= NTF_STICKY;
|
||||
|
||||
if (nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->key.addr))
|
||||
goto nla_put_failure;
|
||||
if (nla_put_u32(skb, NDA_MASTER, br->dev->ifindex))
|
||||
goto nla_put_failure;
|
||||
ci.ndm_used = jiffies_to_clock_t(now - fdb->used);
|
||||
ci.ndm_confirmed = 0;
|
||||
ci.ndm_updated = jiffies_to_clock_t(now - fdb->updated);
|
||||
ci.ndm_refcnt = 0;
|
||||
if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (fdb->key.vlan_id && nla_put(skb, NDA_VLAN, sizeof(u16),
|
||||
&fdb->key.vlan_id))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (test_bit(BR_FDB_NOTIFY, &fdb->flags)) {
|
||||
struct nlattr *nest = nla_nest_start(skb, NDA_FDB_EXT_ATTRS);
|
||||
u8 notify_bits = FDB_NOTIFY_BIT;
|
||||
|
||||
if (!nest)
|
||||
goto nla_put_failure;
|
||||
if (test_bit(BR_FDB_NOTIFY_INACTIVE, &fdb->flags))
|
||||
notify_bits |= FDB_NOTIFY_INACTIVE_BIT;
|
||||
|
||||
if (nla_put_u8(skb, NFEA_ACTIVITY_NOTIFY, notify_bits)) {
|
||||
nla_nest_cancel(skb, nest);
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
nla_nest_end(skb, nest);
|
||||
}
|
||||
|
||||
nlmsg_end(skb, nlh);
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_cancel(skb, nlh);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
static inline size_t fdb_nlmsg_size(void)
|
||||
{
|
||||
return NLMSG_ALIGN(sizeof(struct ndmsg))
|
||||
+ nla_total_size(ETH_ALEN) /* NDA_LLADDR */
|
||||
+ nla_total_size(sizeof(u32)) /* NDA_MASTER */
|
||||
+ nla_total_size(sizeof(u16)) /* NDA_VLAN */
|
||||
+ nla_total_size(sizeof(struct nda_cacheinfo))
|
||||
+ nla_total_size(0) /* NDA_FDB_EXT_ATTRS */
|
||||
+ nla_total_size(sizeof(u8)); /* NFEA_ACTIVITY_NOTIFY */
|
||||
}
|
||||
|
||||
static int br_fdb_replay_one(struct net_bridge *br, struct notifier_block *nb,
|
||||
const struct net_bridge_fdb_entry *fdb,
|
||||
unsigned long action, const void *ctx)
|
||||
{
|
||||
const struct net_bridge_port *p = READ_ONCE(fdb->dst);
|
||||
struct switchdev_notifier_fdb_info item;
|
||||
int err;
|
||||
|
||||
item.addr = fdb->key.addr.addr;
|
||||
item.vid = fdb->key.vlan_id;
|
||||
item.added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
|
||||
item.offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags);
|
||||
item.is_local = test_bit(BR_FDB_LOCAL, &fdb->flags);
|
||||
item.info.dev = (!p || item.is_local) ? br->dev : p->dev;
|
||||
item.info.ctx = ctx;
|
||||
|
||||
err = nb->notifier_call(nb, action, &item);
|
||||
return notifier_to_errno(err);
|
||||
}
|
||||
|
||||
int br_fdb_replay(const struct net_device *br_dev, const void *ctx, bool adding,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
struct net_bridge *br;
|
||||
unsigned long action;
|
||||
int err = 0;
|
||||
|
||||
if (!nb)
|
||||
return 0;
|
||||
|
||||
if (!netif_is_bridge_master(br_dev))
|
||||
return -EINVAL;
|
||||
|
||||
br = netdev_priv(br_dev);
|
||||
|
||||
if (adding)
|
||||
action = SWITCHDEV_FDB_ADD_TO_DEVICE;
|
||||
else
|
||||
action = SWITCHDEV_FDB_DEL_TO_DEVICE;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
hlist_for_each_entry_rcu(fdb, &br->fdb_list, fdb_node) {
|
||||
err = br_fdb_replay_one(br, nb, fdb, action, ctx);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void fdb_notify(struct net_bridge *br,
|
||||
const struct net_bridge_fdb_entry *fdb, int type,
|
||||
bool swdev_notify)
|
||||
{
|
||||
struct net *net = dev_net(br->dev);
|
||||
struct sk_buff *skb;
|
||||
int err = -ENOBUFS;
|
||||
|
||||
if (swdev_notify)
|
||||
br_switchdev_fdb_notify(br, fdb, type);
|
||||
|
||||
skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
goto errout;
|
||||
|
||||
err = fdb_fill_info(skb, br, fdb, 0, 0, type, 0);
|
||||
if (err < 0) {
|
||||
/* -EMSGSIZE implies BUG in fdb_nlmsg_size() */
|
||||
WARN_ON(err == -EMSGSIZE);
|
||||
kfree_skb(skb);
|
||||
goto errout;
|
||||
}
|
||||
rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
|
||||
return;
|
||||
errout:
|
||||
rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
|
||||
}
|
||||
|
||||
/* Dump information about entries, in response to GETNEIGH */
|
||||
int br_fdb_dump(struct sk_buff *skb,
|
||||
struct netlink_callback *cb,
|
||||
|
|
|
@ -670,7 +670,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
|
|||
else
|
||||
netdev_set_rx_headroom(dev, br_hr);
|
||||
|
||||
if (br_fdb_insert(br, p, dev->dev_addr, 0))
|
||||
if (br_fdb_add_local(br, p, dev->dev_addr, 0))
|
||||
netdev_err(dev, "failed insert local address bridge forwarding table\n");
|
||||
|
||||
if (br->dev->addr_assign_type != NET_ADDR_SET) {
|
||||
|
|
|
@ -767,8 +767,8 @@ struct net_bridge_fdb_entry *br_fdb_find_rcu(struct net_bridge *br,
|
|||
int br_fdb_test_addr(struct net_device *dev, unsigned char *addr);
|
||||
int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long count,
|
||||
unsigned long off);
|
||||
int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const unsigned char *addr, u16 vid);
|
||||
int br_fdb_add_local(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const unsigned char *addr, u16 vid);
|
||||
void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
|
||||
const unsigned char *addr, u16 vid, unsigned long flags);
|
||||
|
||||
|
@ -792,8 +792,6 @@ int br_fdb_external_learn_del(struct net_bridge *br, struct net_bridge_port *p,
|
|||
bool swdev_notify);
|
||||
void br_fdb_offloaded_set(struct net_bridge *br, struct net_bridge_port *p,
|
||||
const unsigned char *addr, u16 vid, bool offloaded);
|
||||
int br_fdb_replay(const struct net_device *br_dev, const void *ctx, bool adding,
|
||||
struct notifier_block *nb);
|
||||
|
||||
/* br_forward.c */
|
||||
enum br_pkt_type {
|
||||
|
|
|
@ -122,28 +122,38 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void br_switchdev_fdb_populate(struct net_bridge *br,
|
||||
struct switchdev_notifier_fdb_info *item,
|
||||
const struct net_bridge_fdb_entry *fdb,
|
||||
const void *ctx)
|
||||
{
|
||||
const struct net_bridge_port *p = READ_ONCE(fdb->dst);
|
||||
|
||||
item->addr = fdb->key.addr.addr;
|
||||
item->vid = fdb->key.vlan_id;
|
||||
item->added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags);
|
||||
item->offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags);
|
||||
item->is_local = test_bit(BR_FDB_LOCAL, &fdb->flags);
|
||||
item->info.dev = (!p || item->is_local) ? br->dev : p->dev;
|
||||
item->info.ctx = ctx;
|
||||
}
|
||||
|
||||
void
|
||||
br_switchdev_fdb_notify(struct net_bridge *br,
|
||||
const struct net_bridge_fdb_entry *fdb, int type)
|
||||
{
|
||||
const struct net_bridge_port *dst = READ_ONCE(fdb->dst);
|
||||
struct switchdev_notifier_fdb_info info = {
|
||||
.addr = fdb->key.addr.addr,
|
||||
.vid = fdb->key.vlan_id,
|
||||
.added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags),
|
||||
.is_local = test_bit(BR_FDB_LOCAL, &fdb->flags),
|
||||
.offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags),
|
||||
};
|
||||
struct net_device *dev = (!dst || info.is_local) ? br->dev : dst->dev;
|
||||
struct switchdev_notifier_fdb_info item;
|
||||
|
||||
br_switchdev_fdb_populate(br, &item, fdb, NULL);
|
||||
|
||||
switch (type) {
|
||||
case RTM_DELNEIGH:
|
||||
call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE,
|
||||
dev, &info.info, NULL);
|
||||
item.info.dev, &item.info, NULL);
|
||||
break;
|
||||
case RTM_NEWNEIGH:
|
||||
call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE,
|
||||
dev, &info.info, NULL);
|
||||
item.info.dev, &item.info, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -270,6 +280,53 @@ static void nbp_switchdev_del(struct net_bridge_port *p)
|
|||
}
|
||||
}
|
||||
|
||||
static int br_fdb_replay_one(struct net_bridge *br, struct notifier_block *nb,
|
||||
const struct net_bridge_fdb_entry *fdb,
|
||||
unsigned long action, const void *ctx)
|
||||
{
|
||||
struct switchdev_notifier_fdb_info item;
|
||||
int err;
|
||||
|
||||
br_switchdev_fdb_populate(br, &item, fdb, ctx);
|
||||
|
||||
err = nb->notifier_call(nb, action, &item);
|
||||
return notifier_to_errno(err);
|
||||
}
|
||||
|
||||
static int br_fdb_replay(const struct net_device *br_dev, const void *ctx,
|
||||
bool adding, struct notifier_block *nb)
|
||||
{
|
||||
struct net_bridge_fdb_entry *fdb;
|
||||
struct net_bridge *br;
|
||||
unsigned long action;
|
||||
int err = 0;
|
||||
|
||||
if (!nb)
|
||||
return 0;
|
||||
|
||||
if (!netif_is_bridge_master(br_dev))
|
||||
return -EINVAL;
|
||||
|
||||
br = netdev_priv(br_dev);
|
||||
|
||||
if (adding)
|
||||
action = SWITCHDEV_FDB_ADD_TO_DEVICE;
|
||||
else
|
||||
action = SWITCHDEV_FDB_DEL_TO_DEVICE;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
hlist_for_each_entry_rcu(fdb, &br->fdb_list, fdb_node) {
|
||||
err = br_fdb_replay_one(br, nb, fdb, action, ctx);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nbp_switchdev_sync_objs(struct net_bridge_port *p, const void *ctx,
|
||||
struct notifier_block *atomic_nb,
|
||||
struct notifier_block *blocking_nb,
|
||||
|
|
|
@ -293,7 +293,7 @@ static int __vlan_add(struct net_bridge_vlan *v, u16 flags,
|
|||
|
||||
/* Add the dev mac and count the vlan only if it's usable */
|
||||
if (br_vlan_should_use(v)) {
|
||||
err = br_fdb_insert(br, p, dev->dev_addr, v->vid);
|
||||
err = br_fdb_add_local(br, p, dev->dev_addr, v->vid);
|
||||
if (err) {
|
||||
br_err(br, "failed insert local address into bridge forwarding table\n");
|
||||
goto out_filt;
|
||||
|
@ -683,8 +683,7 @@ static int br_vlan_add_existing(struct net_bridge *br,
|
|||
goto err_flags;
|
||||
}
|
||||
/* It was only kept for port vlans, now make it real */
|
||||
err = br_fdb_insert(br, NULL, br->dev->dev_addr,
|
||||
vlan->vid);
|
||||
err = br_fdb_add_local(br, NULL, br->dev->dev_addr, vlan->vid);
|
||||
if (err) {
|
||||
br_err(br, "failed to insert local address into bridge forwarding table\n");
|
||||
goto err_fdb_insert;
|
||||
|
|
|
@ -2468,10 +2468,9 @@ static bool dsa_foreign_dev_check(const struct net_device *dev,
|
|||
}
|
||||
|
||||
static int dsa_slave_fdb_event(struct net_device *dev,
|
||||
const struct net_device *orig_dev,
|
||||
const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info,
|
||||
unsigned long event)
|
||||
struct net_device *orig_dev,
|
||||
unsigned long event, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info)
|
||||
{
|
||||
struct dsa_switchdev_event_work *switchdev_work;
|
||||
struct dsa_port *dp = dsa_slave_to_port(dev);
|
||||
|
@ -2525,24 +2524,6 @@ static int dsa_slave_fdb_event(struct net_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dsa_slave_fdb_add_to_device(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info)
|
||||
{
|
||||
return dsa_slave_fdb_event(dev, orig_dev, ctx, fdb_info,
|
||||
SWITCHDEV_FDB_ADD_TO_DEVICE);
|
||||
}
|
||||
|
||||
static int
|
||||
dsa_slave_fdb_del_to_device(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info)
|
||||
{
|
||||
return dsa_slave_fdb_event(dev, orig_dev, ctx, fdb_info,
|
||||
SWITCHDEV_FDB_DEL_TO_DEVICE);
|
||||
}
|
||||
|
||||
/* Called under rcu_read_lock() */
|
||||
static int dsa_slave_switchdev_event(struct notifier_block *unused,
|
||||
unsigned long event, void *ptr)
|
||||
|
@ -2557,18 +2538,12 @@ static int dsa_slave_switchdev_event(struct notifier_block *unused,
|
|||
dsa_slave_port_attr_set);
|
||||
return notifier_from_errno(err);
|
||||
case SWITCHDEV_FDB_ADD_TO_DEVICE:
|
||||
err = switchdev_handle_fdb_add_to_device(dev, ptr,
|
||||
dsa_slave_dev_check,
|
||||
dsa_foreign_dev_check,
|
||||
dsa_slave_fdb_add_to_device,
|
||||
NULL);
|
||||
return notifier_from_errno(err);
|
||||
case SWITCHDEV_FDB_DEL_TO_DEVICE:
|
||||
err = switchdev_handle_fdb_del_to_device(dev, ptr,
|
||||
dsa_slave_dev_check,
|
||||
dsa_foreign_dev_check,
|
||||
dsa_slave_fdb_del_to_device,
|
||||
NULL);
|
||||
err = switchdev_handle_fdb_event_to_device(dev, event, ptr,
|
||||
dsa_slave_dev_check,
|
||||
dsa_foreign_dev_check,
|
||||
dsa_slave_fdb_event,
|
||||
NULL);
|
||||
return notifier_from_errno(err);
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
|
|
|
@ -428,17 +428,17 @@ switchdev_lower_dev_find(struct net_device *dev,
|
|||
return switchdev_priv.lower_dev;
|
||||
}
|
||||
|
||||
static int __switchdev_handle_fdb_add_to_device(struct net_device *dev,
|
||||
const struct net_device *orig_dev,
|
||||
static int __switchdev_handle_fdb_event_to_device(struct net_device *dev,
|
||||
struct net_device *orig_dev, unsigned long event,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
bool (*foreign_dev_check_cb)(const struct net_device *dev,
|
||||
const struct net_device *foreign_dev),
|
||||
int (*add_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
|
||||
unsigned long event, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info),
|
||||
int (*lag_add_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
int (*lag_mod_cb)(struct net_device *dev, struct net_device *orig_dev,
|
||||
unsigned long event, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info))
|
||||
{
|
||||
const struct switchdev_notifier_info *info = &fdb_info->info;
|
||||
|
@ -447,17 +447,17 @@ static int __switchdev_handle_fdb_add_to_device(struct net_device *dev,
|
|||
int err = -EOPNOTSUPP;
|
||||
|
||||
if (check_cb(dev))
|
||||
return add_cb(dev, orig_dev, info->ctx, fdb_info);
|
||||
return mod_cb(dev, orig_dev, event, info->ctx, fdb_info);
|
||||
|
||||
if (netif_is_lag_master(dev)) {
|
||||
if (!switchdev_lower_dev_find(dev, check_cb, foreign_dev_check_cb))
|
||||
goto maybe_bridged_with_us;
|
||||
|
||||
/* This is a LAG interface that we offload */
|
||||
if (!lag_add_cb)
|
||||
if (!lag_mod_cb)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return lag_add_cb(dev, orig_dev, info->ctx, fdb_info);
|
||||
return lag_mod_cb(dev, orig_dev, event, info->ctx, fdb_info);
|
||||
}
|
||||
|
||||
/* Recurse through lower interfaces in case the FDB entry is pointing
|
||||
|
@ -481,10 +481,10 @@ static int __switchdev_handle_fdb_add_to_device(struct net_device *dev,
|
|||
foreign_dev_check_cb))
|
||||
continue;
|
||||
|
||||
err = __switchdev_handle_fdb_add_to_device(lower_dev, orig_dev,
|
||||
fdb_info, check_cb,
|
||||
foreign_dev_check_cb,
|
||||
add_cb, lag_add_cb);
|
||||
err = __switchdev_handle_fdb_event_to_device(lower_dev, orig_dev,
|
||||
event, fdb_info, check_cb,
|
||||
foreign_dev_check_cb,
|
||||
mod_cb, lag_mod_cb);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
}
|
||||
|
@ -503,140 +503,34 @@ maybe_bridged_with_us:
|
|||
if (!switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb))
|
||||
return 0;
|
||||
|
||||
return __switchdev_handle_fdb_add_to_device(br, orig_dev, fdb_info,
|
||||
check_cb, foreign_dev_check_cb,
|
||||
add_cb, lag_add_cb);
|
||||
return __switchdev_handle_fdb_event_to_device(br, orig_dev, event, fdb_info,
|
||||
check_cb, foreign_dev_check_cb,
|
||||
mod_cb, lag_mod_cb);
|
||||
}
|
||||
|
||||
int switchdev_handle_fdb_add_to_device(struct net_device *dev,
|
||||
int switchdev_handle_fdb_event_to_device(struct net_device *dev, unsigned long event,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
bool (*foreign_dev_check_cb)(const struct net_device *dev,
|
||||
const struct net_device *foreign_dev),
|
||||
int (*add_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
int (*mod_cb)(struct net_device *dev, struct net_device *orig_dev,
|
||||
unsigned long event, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info),
|
||||
int (*lag_add_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
int (*lag_mod_cb)(struct net_device *dev, struct net_device *orig_dev,
|
||||
unsigned long event, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info))
|
||||
{
|
||||
int err;
|
||||
|
||||
err = __switchdev_handle_fdb_add_to_device(dev, dev, fdb_info,
|
||||
check_cb,
|
||||
foreign_dev_check_cb,
|
||||
add_cb, lag_add_cb);
|
||||
err = __switchdev_handle_fdb_event_to_device(dev, dev, event, fdb_info,
|
||||
check_cb, foreign_dev_check_cb,
|
||||
mod_cb, lag_mod_cb);
|
||||
if (err == -EOPNOTSUPP)
|
||||
err = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(switchdev_handle_fdb_add_to_device);
|
||||
|
||||
static int __switchdev_handle_fdb_del_to_device(struct net_device *dev,
|
||||
const struct net_device *orig_dev,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
bool (*foreign_dev_check_cb)(const struct net_device *dev,
|
||||
const struct net_device *foreign_dev),
|
||||
int (*del_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info),
|
||||
int (*lag_del_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info))
|
||||
{
|
||||
const struct switchdev_notifier_info *info = &fdb_info->info;
|
||||
struct net_device *br, *lower_dev;
|
||||
struct list_head *iter;
|
||||
int err = -EOPNOTSUPP;
|
||||
|
||||
if (check_cb(dev))
|
||||
return del_cb(dev, orig_dev, info->ctx, fdb_info);
|
||||
|
||||
if (netif_is_lag_master(dev)) {
|
||||
if (!switchdev_lower_dev_find(dev, check_cb, foreign_dev_check_cb))
|
||||
goto maybe_bridged_with_us;
|
||||
|
||||
/* This is a LAG interface that we offload */
|
||||
if (!lag_del_cb)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return lag_del_cb(dev, orig_dev, info->ctx, fdb_info);
|
||||
}
|
||||
|
||||
/* Recurse through lower interfaces in case the FDB entry is pointing
|
||||
* towards a bridge device.
|
||||
*/
|
||||
if (netif_is_bridge_master(dev)) {
|
||||
if (!switchdev_lower_dev_find(dev, check_cb, foreign_dev_check_cb))
|
||||
return 0;
|
||||
|
||||
/* This is a bridge interface that we offload */
|
||||
netdev_for_each_lower_dev(dev, lower_dev, iter) {
|
||||
/* Do not propagate FDB entries across bridges */
|
||||
if (netif_is_bridge_master(lower_dev))
|
||||
continue;
|
||||
|
||||
/* Bridge ports might be either us, or LAG interfaces
|
||||
* that we offload.
|
||||
*/
|
||||
if (!check_cb(lower_dev) &&
|
||||
!switchdev_lower_dev_find(lower_dev, check_cb,
|
||||
foreign_dev_check_cb))
|
||||
continue;
|
||||
|
||||
err = __switchdev_handle_fdb_del_to_device(lower_dev, orig_dev,
|
||||
fdb_info, check_cb,
|
||||
foreign_dev_check_cb,
|
||||
del_cb, lag_del_cb);
|
||||
if (err && err != -EOPNOTSUPP)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
maybe_bridged_with_us:
|
||||
/* Event is neither on a bridge nor a LAG. Check whether it is on an
|
||||
* interface that is in a bridge with us.
|
||||
*/
|
||||
br = netdev_master_upper_dev_get_rcu(dev);
|
||||
if (!br || !netif_is_bridge_master(br))
|
||||
return 0;
|
||||
|
||||
if (!switchdev_lower_dev_find(br, check_cb, foreign_dev_check_cb))
|
||||
return 0;
|
||||
|
||||
return __switchdev_handle_fdb_del_to_device(br, orig_dev, fdb_info,
|
||||
check_cb, foreign_dev_check_cb,
|
||||
del_cb, lag_del_cb);
|
||||
}
|
||||
|
||||
int switchdev_handle_fdb_del_to_device(struct net_device *dev,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info,
|
||||
bool (*check_cb)(const struct net_device *dev),
|
||||
bool (*foreign_dev_check_cb)(const struct net_device *dev,
|
||||
const struct net_device *foreign_dev),
|
||||
int (*del_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info),
|
||||
int (*lag_del_cb)(struct net_device *dev,
|
||||
const struct net_device *orig_dev, const void *ctx,
|
||||
const struct switchdev_notifier_fdb_info *fdb_info))
|
||||
{
|
||||
int err;
|
||||
|
||||
err = __switchdev_handle_fdb_del_to_device(dev, dev, fdb_info,
|
||||
check_cb,
|
||||
foreign_dev_check_cb,
|
||||
del_cb, lag_del_cb);
|
||||
if (err == -EOPNOTSUPP)
|
||||
err = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(switchdev_handle_fdb_del_to_device);
|
||||
EXPORT_SYMBOL_GPL(switchdev_handle_fdb_event_to_device);
|
||||
|
||||
static int __switchdev_handle_port_obj_add(struct net_device *dev,
|
||||
struct switchdev_notifier_port_obj_info *port_obj_info,
|
||||
|
|
Loading…
Reference in New Issue