public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
To: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
Cc: netdev@vger.kernel.org, "(JC),
	Jayachandran" <j-rameshbabu@ti.com>,
	"David S. Miller" <davem@davemloft.net>,
	Andrew Lunn <andrew+netdev@lunn.ch>,
	Chintan Vankar <c-vankar@ti.com>,
	Danish Anwar <danishanwar@ti.com>, Daolin Qiu <d-qiu@ti.com>,
	Eric Dumazet <edumazet@google.com>,
	Felix Maurer <fmaurer@redhat.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Richard Cochran <richardcochran@gmail.com>,
	Simon Horman <horms@kernel.org>
Subject: Re: [PATCH RFC net-next v2 2/2] af_packet: Add port specific handling for HSR
Date: Tue, 17 Mar 2026 18:29:06 +0100	[thread overview]
Message-ID: <20260317172906.eG2LxlZ7@linutronix.de> (raw)
In-Reply-To: <willemdebruijn.kernel.1fbf050c50d62@gmail.com>

On 2026-03-16 16:12:41 [-0400], Willem de Bruijn wrote:
> > The suggested changes to af_packet, that remains a hard no?
> 
> Yes. Packet sockets are largely superseded by XDP and AF_XDP when it
> comes to new functionality. And were never open for such protocol
> specific logic.

ptp4l uses AF_PACKET and while I am not against AF_XDP I am not sure if
it helps. I don't have any protocol stack so extending SOL_PACKET was
the only option left.

> Protocol independent extensions, such as reading skb->mark as part of
> extended auxdata, could be up for debate. But again, we already have
> XDP and AF_XDP which allow passing arbitrary metadata to/from
> userspace. That is preferable over adding new structs to the ABI.

Is this struct xsk_tx_metadata and something similar for RX? If so, I
would require HSR fields for both.
My understanding is that you bind XDP to device+queue. I would require
four sockets (generic+event for both slaves) so this does not work,
right? Also I would need to extend HSR stack so support XDP in a similar
fashion as network driver do?

> If you only want to attach to a single (hsr0) device, I still think
> passing the data inline might work. Either by overriding existing
> fields such as a MAC addr (a hack for sure) or something in the HSR
> header (it as the direction? or by inserting a custom header (or
> trailer) akin to virtio_net_hdr for tun/tap. But custom to your
> workload.

But here you have PACKET_VNET_HDR_SZ to configure virtio_net_hdr. This
is kind of what I ask for with PACKET_HSR_INFO + PACKET_HSR_BIND_PORT
(and I added static branches so nobody has to suffer with CONFIG_HSR
enabled but no HSR enabled socket)..

The diff below would be what means to bypass AF_PACKET entirely and use
eth0/ eth1 to send via SLAVE_A/ B and hsr0 with SO_MARK to send with
system's HSR header but only on one of the two ports.
With HSR-offloading enabled we need to attach the SO_MARK hints also on
eth0/ eth1 devices, too. Otherwise the offloading part will send the
packet on both ports and attach a header (as designed).

Now I have now 8 sockets in userland instead of 4.
I added icssg to show the offloading case:

diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c
index 79df641b4fb97..a2e50cae01686 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_common.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_common.c
@@ -909,13 +909,17 @@ enum netdev_tx icssg_ndo_start_xmit(struct sk_buff *skb, struct net_device *ndev
 	 */
 	dst_tag_id = emac->port_id | (q_idx << 8);
 
-	if (prueth->is_hsr_offload_mode &&
-	    (ndev->features & NETIF_F_HW_HSR_DUP))
-		dst_tag_id = PRUETH_UNDIRECTED_PKT_DST_TAG;
+	if (prueth->is_hsr_offload_mode) {
+		bool has_header;
+		bool has_port;
 
-	if (prueth->is_hsr_offload_mode &&
-	    (ndev->features & NETIF_F_HW_HSR_TAG_INS))
-		epib[1] |= PRUETH_UNDIRECTED_PKT_TAG_INS;
+		hsr_skb_get_header_port_mark(skb, &has_header, &has_port);
+		if (ndev->features & NETIF_F_HW_HSR_DUP && !has_port)
+			dst_tag_id = PRUETH_UNDIRECTED_PKT_DST_TAG;
+
+		if (ndev->features & NETIF_F_HW_HSR_TAG_INS && !has_header)
+			epib[1] |= PRUETH_UNDIRECTED_PKT_TAG_INS;
+	}
 
 	cppi5_desc_set_tags_ids(&first_desc->hdr, 0, dst_tag_id);
 	k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma);
diff --git a/include/linux/if_hsr.h b/include/linux/if_hsr.h
index d7941fd880329..814b30f65f102 100644
--- a/include/linux/if_hsr.h
+++ b/include/linux/if_hsr.h
@@ -22,6 +22,11 @@ enum hsr_port_type {
 	HSR_PT_PORTS,	/* This must be the last item in the enum */
 };
 
+struct hsr_ptp_cb {
+	u8	ptp;
+	u8	port;
+};
+
 /* HSR Tag.
  * As defined in IEC-62439-3:2010, the HSR tag is really { ethertype = 0x88FB,
  * path, LSDU_size, sequence Nr }. But we let eth_header() create { h_dest,
@@ -43,6 +48,57 @@ extern bool is_hsr_master(struct net_device *dev);
 extern int hsr_get_version(struct net_device *dev, enum hsr_version *ver);
 struct net_device *hsr_get_port_ndev(struct net_device *ndev,
 				     enum hsr_port_type pt);
+
+static inline unsigned int hsr_skb_get_TX_port(struct sk_buff *skb)
+{
+	struct hsr_ptp_cb *cb;
+
+	if (!skb)
+		return 0;
+
+	cb = (struct hsr_ptp_cb*)skb->cb;
+	if (!cb->ptp)
+		return 0;
+	return cb->port;
+}
+
+static inline void hsr_skb_set_TX_port(struct sk_buff *skb,
+				       enum hsr_port_type port)
+{
+	struct hsr_ptp_cb *cb;
+
+	cb = (struct hsr_ptp_cb*)skb->cb;
+	cb->ptp = 1;
+	cb->port = port;
+}
+
+static inline void hsr_skb_non_ptp(struct sk_buff *skb)
+{
+	struct hsr_ptp_cb *cb;
+
+	cb = (struct hsr_ptp_cb*)skb->cb;
+	cb->ptp = 0;
+}
+
+static inline void hsr_skb_get_header_port_mark(struct sk_buff *skb, bool *has_header,
+						bool *has_port)
+{
+	u32 mark = skb->mark;
+	u32 val;
+
+	val = mark & 0b11;
+	if (val == 1 || val == 2)
+		*has_port = true;
+	else
+		*has_port = false;
+
+	val = mark & 0b100;
+	if (val)
+		*has_header = true;
+	else
+		*has_header = false;
+}
+
 #else
 static inline bool is_hsr_master(struct net_device *dev)
 {
@@ -59,6 +115,13 @@ static inline struct net_device *hsr_get_port_ndev(struct net_device *ndev,
 {
 	return ERR_PTR(-EINVAL);
 }
+
+static inline void hsr_skb_get_header_port_mark(struct sk_buff *skb, bool *has_header,
+						bool *has_port)
+{
+	*has_port = false;
+	*has_header = false;
+}
 #endif /* CONFIG_HSR */
 
 #endif /*_LINUX_IF_HSR_H_*/
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
index 2c43776b7c4fb..e01d3a33ac941 100644
--- a/net/hsr/hsr_device.c
+++ b/net/hsr/hsr_device.c
@@ -229,6 +229,14 @@ static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 	master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
 	if (master) {
 		skb->dev = master->dev;
+
+		if (skb->mark == 1)
+			hsr_skb_set_TX_port(skb, HSR_PT_SLAVE_A);
+		else if (skb->mark == 2)
+			hsr_skb_set_TX_port(skb, HSR_PT_SLAVE_B);
+		else
+			hsr_skb_non_ptp(skb);
+
 		skb_reset_mac_header(skb);
 		skb_reset_mac_len(skb);
 		spin_lock_bh(&hsr->seqnr_lock);
@@ -355,6 +363,7 @@ static void send_hsr_supervision_frame(struct hsr_port *port,
 		return;
 	}
 
+	hsr_skb_non_ptp(skb);
 	hsr_forward_skb(skb, port);
 	spin_unlock_bh(&hsr->seqnr_lock);
 	return;
@@ -396,6 +405,7 @@ static void send_prp_supervision_frame(struct hsr_port *master,
 		return;
 	}
 
+	hsr_skb_non_ptp(skb);
 	hsr_forward_skb(skb, master);
 	spin_unlock_bh(&hsr->seqnr_lock);
 }
diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index c67c0d35921de..d0d6a8342bb33 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -12,6 +12,7 @@
 #include <linux/skbuff.h>
 #include <linux/etherdevice.h>
 #include <linux/if_vlan.h>
+#include <net/sock.h>
 #include "hsr_main.h"
 #include "hsr_framereg.h"
 
@@ -333,7 +334,10 @@ struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame,
 		hsr_set_path_id(hsr_ethhdr, port);
 		return skb_clone(frame->skb_hsr, GFP_ATOMIC);
 	} else if (port->dev->features & NETIF_F_HW_HSR_TAG_INS) {
-		return skb_clone(frame->skb_std, GFP_ATOMIC);
+		skb = skb_clone(frame->skb_std, GFP_ATOMIC);
+		if (hsr_skb_get_TX_port(skb))
+			skb_set_owner_w(skb, frame->skb_std->sk);
+		return skb;
 	}
 
 	/* Create the new skb with enough headroom to fit the HSR tag */
@@ -355,6 +359,15 @@ struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame,
 	memmove(dst, src, movelen);
 	skb_reset_mac_header(skb);
 
+	if (hsr_skb_get_TX_port(skb)) {
+		/* Packets are bound to a port and the sender may expect time
+		 * information.
+		 */
+		skb_shinfo(skb)->tx_flags = skb_shinfo(frame->skb_std)->tx_flags;
+		skb_shinfo(skb)->tskey = skb_shinfo(frame->skb_std)->tskey;
+		skb_set_owner_w(skb, frame->skb_std->sk);
+	}
+
 	/* skb_put_padto free skb on error and hsr_fill_tag returns NULL in
 	 * that case
 	 */
@@ -377,12 +390,23 @@ struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame,
 		}
 		return skb_clone(frame->skb_prp, GFP_ATOMIC);
 	} else if (port->dev->features & NETIF_F_HW_HSR_TAG_INS) {
-		return skb_clone(frame->skb_std, GFP_ATOMIC);
+		skb = skb_clone(frame->skb_std, GFP_ATOMIC);
+		if (hsr_skb_get_TX_port(skb))
+			skb_set_owner_w(skb, frame->skb_std->sk);
+		return skb;
 	}
 
 	skb = skb_copy_expand(frame->skb_std, skb_headroom(frame->skb_std),
 			      skb_tailroom(frame->skb_std) + HSR_HLEN,
 			      GFP_ATOMIC);
+	if (hsr_skb_get_TX_port(skb)) {
+		/* Packets are bound to a port and the sender may expect time
+		 * information.
+		 */
+		skb_shinfo(skb)->tx_flags = skb_shinfo(frame->skb_std)->tx_flags;
+		skb_shinfo(skb)->tskey = skb_shinfo(frame->skb_std)->tskey;
+		skb_set_owner_w(skb, frame->skb_std->sk);
+	}
 	return prp_fill_rct(skb, frame, port);
 }
 
@@ -508,10 +532,13 @@ bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)
  */
 static void hsr_forward_do(struct hsr_frame_info *frame)
 {
+	unsigned int req_tx_port;
 	struct hsr_port *port;
 	struct sk_buff *skb;
 	bool sent = false;
 
+	req_tx_port = hsr_skb_get_TX_port(frame->skb_std);
+
 	hsr_for_each_port(frame->port_rcv->hsr, port) {
 		struct hsr_priv *hsr = port->hsr;
 		/* Don't send frame back the way it came */
@@ -532,6 +559,16 @@ static void hsr_forward_do(struct hsr_frame_info *frame)
 		if ((port->dev->features & NETIF_F_HW_HSR_DUP) && sent)
 			continue;
 
+		/* RX PTP packets have the received port recorded */
+		if (frame->skb_hsr)
+			skb = frame->skb_hsr;
+		else if (frame->skb_prp)
+			skb = frame->skb_prp;
+
+		/* PTP TX packets have an outgoing port specified */
+		if (req_tx_port != HSR_PT_NONE && req_tx_port != port->type)
+			continue;
+
 		/* Don't send frame over port where it has been sent before.
 		 * Also for SAN, this shouldn't be done.
 		 */
diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c
index 464f683e016db..f4879d1ee8ab6 100644
--- a/net/hsr/hsr_slave.c
+++ b/net/hsr/hsr_slave.c
@@ -44,8 +44,7 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
 
 	if (hsr_addr_is_self(port->hsr, eth_hdr(skb)->h_source)) {
 		/* Directly kill frames sent by ourselves */
-		kfree_skb(skb);
-		goto finish_consume;
+		goto finish_free_consume;
 	}
 
 	/* For HSR, only tagged frames are expected (unless the device offloads
@@ -70,15 +69,36 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
 	/* Only the frames received over the interlink port will assign a
 	 * sequence number and require synchronisation vs other sender.
 	 */
+	hsr_skb_non_ptp(skb);
 	if (port->type == HSR_PT_INTERLINK) {
 		spin_lock_bh(&hsr->seqnr_lock);
 		hsr_forward_skb(skb, port);
 		spin_unlock_bh(&hsr->seqnr_lock);
 	} else {
+		struct hsr_ethhdr *hsr_ethhdr;
+
+		/* PTP packets are not supposed to be forwarded via HSR/ PRP
+		 * as-is. The latency introduced by forwarding renders
+		 * the time information useless.
+		 */
+		if ((!hsr->prot_version && protocol == htons(ETH_P_PRP)) ||
+		    protocol == htons(ETH_P_HSR)) {
+			/* HSR */
+			hsr_ethhdr = (struct hsr_ethhdr *)skb_mac_header(skb);
+			if (hsr_ethhdr->hsr_tag.encap_proto == htons(ETH_P_1588))
+				goto finish_free_consume;
+		} else {
+			if (protocol == htons(ETH_P_1588))
+				goto finish_free_consume;
+		}
+
 		hsr_forward_skb(skb, port);
 	}
 
-finish_consume:
+	return RX_HANDLER_CONSUMED;
+
+finish_free_consume:
+	kfree_skb(skb);
 	return RX_HANDLER_CONSUMED;
 
 finish_pass:

Sebastian

  reply	other threads:[~2026-03-17 17:29 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-09 15:52 [PATCH RFC net-next v2 0/2] hsr: Add additional info to send/ receive skbs Sebastian Andrzej Siewior
2026-03-09 15:52 ` [PATCH RFC net-next v2 1/2] hsr: Allow to send a specific port and with HSR header Sebastian Andrzej Siewior
2026-03-09 15:52 ` [PATCH RFC net-next v2 2/2] af_packet: Add port specific handling for HSR Sebastian Andrzej Siewior
2026-03-10  1:38   ` Willem de Bruijn
2026-03-10 10:55     ` Sebastian Andrzej Siewior
2026-03-10 21:35       ` Willem de Bruijn
2026-03-12 15:42         ` Sebastian Andrzej Siewior
2026-03-12 21:43           ` Willem de Bruijn
2026-03-13  9:22             ` Sebastian Andrzej Siewior
2026-03-13 16:04               ` Sebastian Andrzej Siewior
2026-03-16 20:12                 ` Willem de Bruijn
2026-03-17 17:29                   ` Sebastian Andrzej Siewior [this message]
2026-03-19 13:29                     ` Willem de Bruijn
2026-03-19 14:26                       ` Sebastian Andrzej Siewior
2026-03-19 16:27                         ` Willem de Bruijn
2026-03-24 16:38                           ` Sebastian Andrzej Siewior

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260317172906.eG2LxlZ7@linutronix.de \
    --to=bigeasy@linutronix.de \
    --cc=andrew+netdev@lunn.ch \
    --cc=c-vankar@ti.com \
    --cc=d-qiu@ti.com \
    --cc=danishanwar@ti.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=fmaurer@redhat.com \
    --cc=horms@kernel.org \
    --cc=j-rameshbabu@ti.com \
    --cc=kuba@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=richardcochran@gmail.com \
    --cc=willemdebruijn.kernel@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox