Skip to content

Commit 123abc0

Browse files
vladimirolteandavem330
authored andcommitted
net: dsa: add support for bridge TX forwarding offload
For a DSA switch, to offload the forwarding process of a bridge device means to send the packets coming from the software bridge as data plane packets. This is contrary to everything that DSA has done so far, because the current taggers only know to send control packets (ones that target a specific destination port), whereas data plane packets are supposed to be forwarded according to the FDB lookup, much like packets ingressing on any regular ingress port. If the FDB lookup process returns multiple destination ports (flooding, multicast), then replication is also handled by the switch hardware - the bridge only sends a single packet and avoids the skb_clone(). DSA keeps for each bridge port a zero-based index (the number of the bridge). Multiple ports performing TX forwarding offload to the same bridge have the same dp->bridge_num value, and ports not offloading the TX data plane of a bridge have dp->bridge_num = -1. The tagger can check if the packet that is being transmitted on has skb->offload_fwd_mark = true or not. If it does, it can be sure that the packet belongs to the data plane of a bridge, further information about which can be obtained based on dp->bridge_dev and dp->bridge_num. It can then compose a DSA tag for injecting a data plane packet into that bridge number. For the switch driver side, we offer two new dsa_switch_ops methods, called .port_bridge_fwd_offload_{add,del}, which are modeled after .port_bridge_{join,leave}. These methods are provided in case the driver needs to configure the hardware to treat packets coming from that bridge software interface as data plane packets. The switchdev <-> bridge interaction happens during the netdev_master_upper_dev_link() call, so to switch drivers, the effect is that the .port_bridge_fwd_offload_add() method is called immediately after .port_bridge_join(). If the bridge number exceeds the number of bridges for which the switch driver can offload the TX data plane (and this includes the case where the driver can offload none), DSA falls back to simply returning tx_fwd_offload = false in the switchdev_bridge_port_offload() call. Signed-off-by: Vladimir Oltean <[email protected]> Reviewed-by: Florian Fainelli <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 5b22d36 commit 123abc0

File tree

4 files changed

+104
-1
lines changed

4 files changed

+104
-1
lines changed

include/net/dsa.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ struct dsa_switch_tree {
162162

163163
/* Track the largest switch index within a tree */
164164
unsigned int last_switch;
165+
166+
/* Track the bridges with forwarding offload enabled */
167+
unsigned long fwd_offloading_bridges;
165168
};
166169

167170
#define dsa_lags_foreach_id(_id, _dst) \
@@ -262,6 +265,7 @@ struct dsa_port {
262265
bool vlan_filtering;
263266
u8 stp_state;
264267
struct net_device *bridge_dev;
268+
int bridge_num;
265269
struct devlink_port devlink_port;
266270
bool devlink_port_setup;
267271
struct phylink *pl;
@@ -413,6 +417,12 @@ struct dsa_switch {
413417
*/
414418
unsigned int num_lag_ids;
415419

420+
/* Drivers that support bridge forwarding offload should set this to
421+
* the maximum number of bridges spanning the same switch tree that can
422+
* be offloaded.
423+
*/
424+
unsigned int num_fwd_offloading_bridges;
425+
416426
size_t num_ports;
417427
};
418428

@@ -696,6 +706,14 @@ struct dsa_switch_ops {
696706
struct net_device *bridge);
697707
void (*port_bridge_leave)(struct dsa_switch *ds, int port,
698708
struct net_device *bridge);
709+
/* Called right after .port_bridge_join() */
710+
int (*port_bridge_tx_fwd_offload)(struct dsa_switch *ds, int port,
711+
struct net_device *bridge,
712+
int bridge_num);
713+
/* Called right before .port_bridge_leave() */
714+
void (*port_bridge_tx_fwd_unoffload)(struct dsa_switch *ds, int port,
715+
struct net_device *bridge,
716+
int bridge_num);
699717
void (*port_stp_state_set)(struct dsa_switch *ds, int port,
700718
u8 state);
701719
void (*port_fast_age)(struct dsa_switch *ds, int port);

net/dsa/dsa2.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,7 @@ static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index)
10441044

10451045
dp->ds = ds;
10461046
dp->index = index;
1047+
dp->bridge_num = -1;
10471048

10481049
INIT_LIST_HEAD(&dp->list);
10491050
list_add_tail(&dp->list, &dst->ports);

net/dsa/dsa_priv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include <net/dsa.h>
1515
#include <net/gro_cells.h>
1616

17+
#define DSA_MAX_NUM_OFFLOADING_BRIDGES BITS_PER_LONG
18+
1719
enum {
1820
DSA_NOTIFIER_AGEING_TIME,
1921
DSA_NOTIFIER_BRIDGE_JOIN,

net/dsa/port.c

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,83 @@ static void dsa_port_switchdev_unsync_attrs(struct dsa_port *dp)
230230
*/
231231
}
232232

233+
static int dsa_tree_find_bridge_num(struct dsa_switch_tree *dst,
234+
struct net_device *bridge_dev)
235+
{
236+
struct dsa_port *dp;
237+
238+
/* When preparing the offload for a port, it will have a valid
239+
* dp->bridge_dev pointer but a not yet valid dp->bridge_num.
240+
* However there might be other ports having the same dp->bridge_dev
241+
* and a valid dp->bridge_num, so just ignore this port.
242+
*/
243+
list_for_each_entry(dp, &dst->ports, list)
244+
if (dp->bridge_dev == bridge_dev && dp->bridge_num != -1)
245+
return dp->bridge_num;
246+
247+
return -1;
248+
}
249+
250+
static void dsa_port_bridge_tx_fwd_unoffload(struct dsa_port *dp,
251+
struct net_device *bridge_dev)
252+
{
253+
struct dsa_switch_tree *dst = dp->ds->dst;
254+
int bridge_num = dp->bridge_num;
255+
struct dsa_switch *ds = dp->ds;
256+
257+
/* No bridge TX forwarding offload => do nothing */
258+
if (!ds->ops->port_bridge_tx_fwd_unoffload || dp->bridge_num == -1)
259+
return;
260+
261+
dp->bridge_num = -1;
262+
263+
/* Check if the bridge is still in use, otherwise it is time
264+
* to clean it up so we can reuse this bridge_num later.
265+
*/
266+
if (!dsa_tree_find_bridge_num(dst, bridge_dev))
267+
clear_bit(bridge_num, &dst->fwd_offloading_bridges);
268+
269+
/* Notify the chips only once the offload has been deactivated, so
270+
* that they can update their configuration accordingly.
271+
*/
272+
ds->ops->port_bridge_tx_fwd_unoffload(ds, dp->index, bridge_dev,
273+
bridge_num);
274+
}
275+
276+
static bool dsa_port_bridge_tx_fwd_offload(struct dsa_port *dp,
277+
struct net_device *bridge_dev)
278+
{
279+
struct dsa_switch_tree *dst = dp->ds->dst;
280+
struct dsa_switch *ds = dp->ds;
281+
int bridge_num, err;
282+
283+
if (!ds->ops->port_bridge_tx_fwd_offload)
284+
return false;
285+
286+
bridge_num = dsa_tree_find_bridge_num(dst, bridge_dev);
287+
if (bridge_num < 0) {
288+
/* First port that offloads TX forwarding for this bridge */
289+
bridge_num = find_first_zero_bit(&dst->fwd_offloading_bridges,
290+
DSA_MAX_NUM_OFFLOADING_BRIDGES);
291+
if (bridge_num >= ds->num_fwd_offloading_bridges)
292+
return false;
293+
294+
set_bit(bridge_num, &dst->fwd_offloading_bridges);
295+
}
296+
297+
dp->bridge_num = bridge_num;
298+
299+
/* Notify the driver */
300+
err = ds->ops->port_bridge_tx_fwd_offload(ds, dp->index, bridge_dev,
301+
bridge_num);
302+
if (err) {
303+
dsa_port_bridge_tx_fwd_unoffload(dp, bridge_dev);
304+
return false;
305+
}
306+
307+
return true;
308+
}
309+
233310
int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
234311
struct netlink_ext_ack *extack)
235312
{
@@ -241,6 +318,7 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
241318
};
242319
struct net_device *dev = dp->slave;
243320
struct net_device *brport_dev;
321+
bool tx_fwd_offload;
244322
int err;
245323

246324
/* Here the interface is already bridged. Reflect the current
@@ -254,10 +332,12 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
254332
if (err)
255333
goto out_rollback;
256334

335+
tx_fwd_offload = dsa_port_bridge_tx_fwd_offload(dp, br);
336+
257337
err = switchdev_bridge_port_offload(brport_dev, dev, dp,
258338
&dsa_slave_switchdev_notifier,
259339
&dsa_slave_switchdev_blocking_notifier,
260-
false, extack);
340+
tx_fwd_offload, extack);
261341
if (err)
262342
goto out_rollback_unbridge;
263343

@@ -302,6 +382,8 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
302382
*/
303383
dp->bridge_dev = NULL;
304384

385+
dsa_port_bridge_tx_fwd_unoffload(dp, br);
386+
305387
err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info);
306388
if (err)
307389
pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");

0 commit comments

Comments
 (0)