summaryrefslogtreecommitdiffstats
path: root/net/bridge/br_switchdev.c
diff options
context:
space:
mode:
authorTobias Waldekranz <tobias@waldekranz.com>2021-07-21 19:24:00 +0300
committerDavid S. Miller <davem@davemloft.net>2021-07-22 00:26:23 -0700
commit8582661048eb64341edf73dd2ca828b4f039c5c2 (patch)
treeece8b1dcaf1c398f100ee29f5e4629918649c4ce /net/bridge/br_switchdev.c
parentf7cf972f9375388838b0fbdaa007ce8494646990 (diff)
downloadlinux-8582661048eb64341edf73dd2ca828b4f039c5c2.tar.gz
linux-8582661048eb64341edf73dd2ca828b4f039c5c2.tar.bz2
linux-8582661048eb64341edf73dd2ca828b4f039c5c2.zip
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: Tobias Waldekranz <tobias@waldekranz.com> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/bridge/br_switchdev.c')
-rw-r--r--net/bridge/br_switchdev.c94
1 files changed, 62 insertions, 32 deletions
diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
index 833fd30482c2..f3120f13c293 100644
--- a/net/bridge/br_switchdev.c
+++ b/net/bridge/br_switchdev.c
@@ -8,38 +8,6 @@
#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,
struct sk_buff *skb)
{
@@ -156,3 +124,65 @@ int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
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);
+}