On Mon, 06 Jul 2026 13:41:28 +0000, Xin Xie <[email protected]> wrote:
> A PRP RedBox proxies SANs that sit behind an interlink port: their frames
> must reach the PRP network with the SAN source MAC preserved, and PRP
> unicast must be steered between the LAN and the SAN segment correctly.
> 
> Add the PRP interlink forwarding rules to prp_drop_frame() and give RedBox
> nodes a second duplicate-discard slot so the two LAN copies of a frame
> destined to a SAN collapse to a single delivery out the interlink.
> 
> The destination classification (is the unicast DA a PRP-network node or a
> proxied SAN) is resolved once per frame in fill_frame_info(), gated to PRP
> RedBox devices, and cached in struct hsr_frame_info, so prp_drop_frame()
> stays O(1) and does not walk the node tables for every candidate egress
> port in the softIRQ path. HSR RedBox frame classification is untouched.
> 
> Factor the LAN A/B duplicate test into prp_is_lan_dup() so the new PRP
> interlink rules do not change hsr_drop_frame() behaviour, including the
> NETIF_F_HW_HSR_FWD path which keeps using the LAN-duplicate test only.
> 
> Publish the RedBox state before the first hsr_add_port(): the slave and
> interlink rx handlers are live from hsr_add_port() on and rtnl does not
> stop softirq processing, so a frame could otherwise be handled while
> hsr->redbox is still false. hsr_add_node() sizes each node's per-port
> sequence state from hsr->redbox; a node learned in that window would get
> a single-port sequence block, breaking the interlink duplicate discard
> (WARN_ON_ONCE plus duplicate delivery to the SAN) and letting the
> supervision sequence-block merge read beyond the source node's allocated
> sequence bitmap. Publishing the flag before any port exists makes the
> per-node sizing uniform by construction. This is safe: the proxy
> announce timer is only armed from hsr_check_announce() once the master
> is running, the packet-path readers of hsr->redbox tolerate an empty
> proxy node database and an absent interlink port, and the
> prune_proxy_timer is still armed only after the interlink port has been
> attached successfully.
> 
> Additionally bound the supervision sequence-block merge by the smaller
> of the two nodes' seq_port_cnt as defense in depth against mismatched
> node sizes.
> 
> Signed-off-by: Xin Xie <[email protected]>
>
> diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
> index 5555b71ab19b..5af491ed2b72 100644
> --- a/net/hsr/hsr_device.c
> +++ b/net/hsr/hsr_device.c
> @@ -768,6 +768,15 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct 
> net_device *slave[2],
>       /* Make sure the 1st call to netif_carrier_on() gets through */
>       netif_carrier_off(hsr_dev);
>  
> +     /* Publish the RedBox state before any port is attached: the rx
> +      * handlers are live from hsr_add_port() on, and hsr_add_node()
> +      * sizes each node's per-port sequence state from hsr->redbox.
> +      */
> +     if (interlink) {
> +             hsr->redbox = true;
> +             ether_addr_copy(hsr->macaddress_redbox, interlink->dev_addr);
> +     }
> +
>       res = hsr_add_port(hsr, hsr_dev, HSR_PT_MASTER, extack);
>       if (res)
>               goto err_add_master;
> @@ -805,8 +814,6 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct 
> net_device *slave[2],
>               if (res)
>                       goto err_unregister;
>  
> -             hsr->redbox = true;
> -             ether_addr_copy(hsr->macaddress_redbox, interlink->dev_addr);
>               mod_timer(&hsr->prune_proxy_timer,
>                         jiffies + msecs_to_jiffies(PRUNE_PROXY_PERIOD));
>       }
> diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
> index 0774981a65c1..efcd0acef38c 100644
> --- a/net/hsr/hsr_forward.c
> +++ b/net/hsr/hsr_forward.c
> @@ -440,12 +440,37 @@ static int hsr_xmit(struct sk_buff *skb, struct 
> hsr_port *port,
>       return dev_queue_xmit(skb);
>  }
>  
> +static bool prp_is_lan_dup(struct hsr_frame_info *frame,
> +                        struct hsr_port *port)
> +{
> +     enum hsr_port_type rx = frame->port_rcv->type;
> +
> +     return (rx == HSR_PT_SLAVE_A && port->type == HSR_PT_SLAVE_B) ||
> +            (rx == HSR_PT_SLAVE_B && port->type == HSR_PT_SLAVE_A);
> +}
> +
>  bool prp_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)
>  {
> -     return ((frame->port_rcv->type == HSR_PT_SLAVE_A &&
> -              port->type == HSR_PT_SLAVE_B) ||
> -             (frame->port_rcv->type == HSR_PT_SLAVE_B &&
> -              port->type == HSR_PT_SLAVE_A));
> +     enum hsr_port_type rx = frame->port_rcv->type;
> +
> +     /* Supervision frames are not delivered to a SAN on the interlink. */
> +     if (frame->is_supervision && port->type == HSR_PT_INTERLINK)
> +             return true;
> +
> +     if (prp_is_lan_dup(frame, port))
> +             return true;

Since we already have rx (enum hsr_port_type) here, can we pass this
directly to prp_is_lan_dup() function instead of passing frame?

> +
> +     /* LAN to interlink: keep PRP-network unicast off the SAN segment. */
> +     if ((rx == HSR_PT_SLAVE_A || rx == HSR_PT_SLAVE_B) &&
> +         port->type == HSR_PT_INTERLINK)
> +             return frame->dst_in_node_db;
> +
> +     /* Interlink to LAN: keep SAN-to-SAN unicast local. */
> +     if ((port->type == HSR_PT_SLAVE_A || port->type == HSR_PT_SLAVE_B) &&
> +         rx == HSR_PT_INTERLINK)
> +             return frame->dst_in_proxy_node_db;
> +
> +     return false;
>  }
>  
>  bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)
> @@ -453,7 +478,7 @@ bool hsr_drop_frame(struct hsr_frame_info *frame, struct 
> hsr_port *port)
>       struct sk_buff *skb;
>  
>       if (port->dev->features & NETIF_F_HW_HSR_FWD)
> -             return prp_drop_frame(frame, port);
> +             return prp_is_lan_dup(frame, port);
>  

Of course these calls will need to pass rx too.

Reply via email to