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
next prev parent reply other threads:[~2026-03-17 17:29 UTC|newest]
Thread overview: 23+ 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-12 4:09 ` kernel test robot
2026-03-12 4:48 ` kernel test robot
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
2026-04-02 16:32 ` Sebastian Andrzej Siewior
2026-04-06 14:47 ` Willem de Bruijn
2026-04-16 16:18 ` Sebastian Andrzej Siewior
2026-04-21 7:41 ` Willem de Bruijn
2026-04-22 13:27 ` 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.