On Mon, Jul 05, 2021 at 10:32:04AM +0200, Tobias Waldekranz wrote: > > Many DSA taggers use port bit field in their TX tags, which allows > > replication in hardware. (multiple bits set = send to multiple ports) > > I wonder if the tagger API can be updated to support this. > > I think you could, but it would be tricky. > > The bridge does not operate using vectors/bitfields, rather it is > procedural code that you have to loop through before knowing the set of > destination ports. > > This series just sends the skb to the first port in the hardware domain > and trusts the HW to calculate the same port set as the code in > br_forward.c would have. > > To do what you suggest, the bridge would have to translate each nbp into > a position in a bitfield (or call out to the underlying driver to do it) > as it is looping through ports, then send the aggregated mask along with > the skb. Knowing if a port is the first one you have come across for a > given domain is very easy (just maintain a bitfield), knowing if it is > the last one is harder. So you would likely end up having to queue up > the actual transmission until after the loop has been executed, which > hard to combine with the "lazy cloning" that you really want to get > decent performance. In addition to changing the bridge in order to get the entire bit mask, one also has to somehow propagate that bit mask per skb down to the driver which might be tricky in itself. There is currently no bridge specific data structure passed between the bridge and the switchdev driver, it is just the struct net_device *sb_dev. A hacky solution I might imagine is for the bridge to kzalloc() a small data structure like: struct bridge_fwd_offload_accel_priv { struct net_device *sb_dev; /* Must be first! */ unsigned long port_mask; }; and call as follows: int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb) { struct bridge_fwd_offload_accel_priv *accel_priv = NULL; if (br_switchdev_accels_skb(skb)) { accel_priv = kzalloc(sizeof(*accel_priv), GFP_ATOMIC); if (!accel_priv) return -ENOMEM; accel_priv->sb_dev = BR_INPUT_SKB_CB(skb)->brdev; accel_priv->port_mask = port_mask; } dev_queue_xmit_accel(skb, accel_priv); } This way, the code in net/core/dev.c can be left unmodified. We give it an accel_priv pointer but it can think it is only looking at a sb_dev pointer, since that is the first element in the structure. But then the switchdev driver must kfree(accel_priv) in the xmit function. Not really nice, but for a cuter solution, I think we would need to extend struct sk_buff.