Commit 85826610 authored by Tobias Waldekranz's avatar Tobias Waldekranz Committed by David S. Miller

net: bridge: switchdev: recycle unused hwdoms

Since hwdoms have only been used thus far for equality comparisons, the
bridge has used the simplest possible assignment policy; using a
counter to keep track of the last value handed out.

With the upcoming transmit offloading, we need to perform set
operations efficiently based on hwdoms, e.g. we want to answer
questions like "has this skb been forwarded to any port within this
hwdom?"

Move to a bitmap-based allocation scheme that recycles hwdoms once all
members leaves the bridge. This means that we can use a single
unsigned long to keep track of the hwdoms that have received an skb.

v1->v2: convert the typedef DECLARE_BITMAP(br_hwdom_map_t, BR_HWDOM_MAX)
        into a plain unsigned long.
v2->v6: none
Signed-off-by: default avatarTobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f7cf972f
...@@ -349,6 +349,7 @@ static void del_nbp(struct net_bridge_port *p) ...@@ -349,6 +349,7 @@ static void del_nbp(struct net_bridge_port *p)
nbp_backup_clear(p); nbp_backup_clear(p);
nbp_update_port_count(br); nbp_update_port_count(br);
nbp_switchdev_del(p);
netdev_upper_dev_unlink(dev, br->dev); netdev_upper_dev_unlink(dev, br->dev);
...@@ -643,7 +644,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev, ...@@ -643,7 +644,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
if (err) if (err)
goto err5; goto err5;
err = nbp_switchdev_hwdom_set(p); err = nbp_switchdev_add(p);
if (err) if (err)
goto err6; goto err6;
...@@ -719,6 +720,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev, ...@@ -719,6 +720,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
list_del_rcu(&p->list); list_del_rcu(&p->list);
br_fdb_delete_by_port(br, p, 0, 1); br_fdb_delete_by_port(br, p, 0, 1);
nbp_update_port_count(br); nbp_update_port_count(br);
nbp_switchdev_del(p);
err6: err6:
netdev_upper_dev_unlink(dev, br->dev); netdev_upper_dev_unlink(dev, br->dev);
err5: err5:
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#define BR_MULTICAST_DEFAULT_HASH_MAX 4096 #define BR_MULTICAST_DEFAULT_HASH_MAX 4096
#define BR_HWDOM_MAX BITS_PER_LONG
#define BR_VERSION "2.3" #define BR_VERSION "2.3"
/* Control of forwarding link local multicast */ /* Control of forwarding link local multicast */
...@@ -517,6 +519,8 @@ struct net_bridge { ...@@ -517,6 +519,8 @@ struct net_bridge {
* identifiers in case a bridge spans multiple switchdev instances. * identifiers in case a bridge spans multiple switchdev instances.
*/ */
int last_hwdom; int last_hwdom;
/* Bit mask of hardware domain numbers in use */
unsigned long busy_hwdoms;
#endif #endif
struct hlist_head fdb_list; struct hlist_head fdb_list;
...@@ -1840,7 +1844,6 @@ static inline void br_sysfs_delbr(struct net_device *dev) { return; } ...@@ -1840,7 +1844,6 @@ static inline void br_sysfs_delbr(struct net_device *dev) { return; }
/* br_switchdev.c */ /* br_switchdev.c */
#ifdef CONFIG_NET_SWITCHDEV #ifdef CONFIG_NET_SWITCHDEV
int nbp_switchdev_hwdom_set(struct net_bridge_port *p);
void nbp_switchdev_frame_mark(const struct net_bridge_port *p, void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
struct sk_buff *skb); struct sk_buff *skb);
bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p, bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
...@@ -1854,17 +1857,15 @@ void br_switchdev_fdb_notify(struct net_bridge *br, ...@@ -1854,17 +1857,15 @@ void br_switchdev_fdb_notify(struct net_bridge *br,
int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid); int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid);
int nbp_switchdev_add(struct net_bridge_port *p);
void nbp_switchdev_del(struct net_bridge_port *p);
void br_switchdev_init(struct net_bridge *br);
static inline void br_switchdev_frame_unmark(struct sk_buff *skb) static inline void br_switchdev_frame_unmark(struct sk_buff *skb)
{ {
skb->offload_fwd_mark = 0; skb->offload_fwd_mark = 0;
} }
#else #else
static inline int nbp_switchdev_hwdom_set(struct net_bridge_port *p)
{
return 0;
}
static inline void nbp_switchdev_frame_mark(const struct net_bridge_port *p, static inline void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
struct sk_buff *skb) struct sk_buff *skb)
{ {
...@@ -1905,6 +1906,20 @@ br_switchdev_fdb_notify(struct net_bridge *br, ...@@ -1905,6 +1906,20 @@ br_switchdev_fdb_notify(struct net_bridge *br,
static inline void br_switchdev_frame_unmark(struct sk_buff *skb) static inline void br_switchdev_frame_unmark(struct sk_buff *skb)
{ {
} }
static inline int nbp_switchdev_add(struct net_bridge_port *p)
{
return 0;
}
static inline void nbp_switchdev_del(struct net_bridge_port *p)
{
}
static inline void br_switchdev_init(struct net_bridge *br)
{
}
#endif /* CONFIG_NET_SWITCHDEV */ #endif /* CONFIG_NET_SWITCHDEV */
/* br_arp_nd_proxy.c */ /* br_arp_nd_proxy.c */
......
...@@ -8,38 +8,6 @@ ...@@ -8,38 +8,6 @@
#include "br_private.h" #include "br_private.h"
static int br_switchdev_hwdom_get(struct net_bridge *br, struct net_device *dev)
{
struct net_bridge_port *p;
/* dev is yet to be added to the port list. */
list_for_each_entry(p, &br->port_list, list) {
if (netdev_port_same_parent_id(dev, p->dev))
return p->hwdom;
}
return ++br->last_hwdom;
}
int nbp_switchdev_hwdom_set(struct net_bridge_port *p)
{
struct netdev_phys_item_id ppid = { };
int err;
ASSERT_RTNL();
err = dev_get_port_parent_id(p->dev, &ppid, true);
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
p->hwdom = br_switchdev_hwdom_get(p->br, p->dev);
return 0;
}
void nbp_switchdev_frame_mark(const struct net_bridge_port *p, void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
struct sk_buff *skb) struct sk_buff *skb)
{ {
...@@ -156,3 +124,65 @@ int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid) ...@@ -156,3 +124,65 @@ int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
return switchdev_port_obj_del(dev, &v.obj); return switchdev_port_obj_del(dev, &v.obj);
} }
static int nbp_switchdev_hwdom_set(struct net_bridge_port *joining)
{
struct net_bridge *br = joining->br;
struct net_bridge_port *p;
int hwdom;
/* joining is yet to be added to the port list. */
list_for_each_entry(p, &br->port_list, list) {
if (netdev_port_same_parent_id(joining->dev, p->dev)) {
joining->hwdom = p->hwdom;
return 0;
}
}
hwdom = find_next_zero_bit(&br->busy_hwdoms, BR_HWDOM_MAX, 1);
if (hwdom >= BR_HWDOM_MAX)
return -EBUSY;
set_bit(hwdom, &br->busy_hwdoms);
joining->hwdom = hwdom;
return 0;
}
static void nbp_switchdev_hwdom_put(struct net_bridge_port *leaving)
{
struct net_bridge *br = leaving->br;
struct net_bridge_port *p;
/* leaving is no longer in the port list. */
list_for_each_entry(p, &br->port_list, list) {
if (p->hwdom == leaving->hwdom)
return;
}
clear_bit(leaving->hwdom, &br->busy_hwdoms);
}
int nbp_switchdev_add(struct net_bridge_port *p)
{
struct netdev_phys_item_id ppid = { };
int err;
ASSERT_RTNL();
err = dev_get_port_parent_id(p->dev, &ppid, true);
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
return nbp_switchdev_hwdom_set(p);
}
void nbp_switchdev_del(struct net_bridge_port *p)
{
ASSERT_RTNL();
if (p->hwdom)
nbp_switchdev_hwdom_put(p);
}
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