Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: hsr: add offloading support

Add support for offloading of HSR/PRP (IEC 62439-3) tag insertion
tag removal, duplicate generation and forwarding.

For HSR, insertion involves the switch adding a 6 byte HSR header after
the 14 byte Ethernet header. For PRP it adds a 6 byte trailer.

Tag removal involves automatically stripping the HSR/PRP header/trailer
in the switch. This is possible when the switch also performs auto
deduplication using the HSR/PRP header/trailer (making it no longer
required).

Forwarding involves automatically forwarding between redundant ports in
an HSR. This is crucial because delay is accumulated as a frame passes
through each node in the ring.

Duplication involves the switch automatically sending a single frame
from the CPU port to both redundant ports. This is required because the
inserted HSR/PRP header/trailer must contain the same sequence number
on the frames sent out both redundant ports.

Export is_hsr_master so DSA can tell them apart from other devices in
dsa_slave_changeupper.

Signed-off-by: George McCollister <george.mccollister@gmail.com>
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

George McCollister and committed by
David S. Miller
dcf0cd1c 78be9217

+109 -26
+21
Documentation/networking/netdev-features.rst
··· 182 182 be re-segmentable by GSO or TSO back to the exact original packet stream. 183 183 Hardware GRO is dependent on RXCSUM since every packet successfully merged 184 184 by hardware must also have the checksum verified by hardware. 185 + 186 + * hsr-tag-ins-offload 187 + 188 + This should be set for devices which insert an HSR (High-availability Seamless 189 + Redundancy) or PRP (Parallel Redundancy Protocol) tag automatically. 190 + 191 + * hsr-tag-rm-offload 192 + 193 + This should be set for devices which remove HSR (High-availability Seamless 194 + Redundancy) or PRP (Parallel Redundancy Protocol) tags automatically. 195 + 196 + * hsr-fwd-offload 197 + 198 + This should be set for devices which forward HSR (High-availability Seamless 199 + Redundancy) frames from one port to another in hardware. 200 + 201 + * hsr-dup-offload 202 + 203 + This should be set for devices which duplicate outgoing HSR (High-availability 204 + Seamless Redundancy) or PRP (Parallel Redundancy Protocol) tags automatically 205 + frames in hardware.
+27
include/linux/if_hsr.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _LINUX_IF_HSR_H_ 3 + #define _LINUX_IF_HSR_H_ 4 + 5 + /* used to differentiate various protocols */ 6 + enum hsr_version { 7 + HSR_V0 = 0, 8 + HSR_V1, 9 + PRP_V1, 10 + }; 11 + 12 + #if IS_ENABLED(CONFIG_HSR) 13 + extern bool is_hsr_master(struct net_device *dev); 14 + extern int hsr_get_version(struct net_device *dev, enum hsr_version *ver); 15 + #else 16 + static inline bool is_hsr_master(struct net_device *dev) 17 + { 18 + return false; 19 + } 20 + static inline int hsr_get_version(struct net_device *dev, 21 + enum hsr_version *ver) 22 + { 23 + return -EINVAL; 24 + } 25 + #endif /* CONFIG_HSR */ 26 + 27 + #endif /*_LINUX_IF_HSR_H_*/
+9
include/linux/netdev_features.h
··· 86 86 NETIF_F_HW_MACSEC_BIT, /* Offload MACsec operations */ 87 87 NETIF_F_GRO_UDP_FWD_BIT, /* Allow UDP GRO for forwarding */ 88 88 89 + NETIF_F_HW_HSR_TAG_INS_BIT, /* Offload HSR tag insertion */ 90 + NETIF_F_HW_HSR_TAG_RM_BIT, /* Offload HSR tag removal */ 91 + NETIF_F_HW_HSR_FWD_BIT, /* Offload HSR forwarding */ 92 + NETIF_F_HW_HSR_DUP_BIT, /* Offload HSR duplication */ 93 + 89 94 /* 90 95 * Add your fresh new feature above and remember to update 91 96 * netdev_features_strings[] in net/core/ethtool.c and maybe ··· 164 159 #define NETIF_F_GSO_FRAGLIST __NETIF_F(GSO_FRAGLIST) 165 160 #define NETIF_F_HW_MACSEC __NETIF_F(HW_MACSEC) 166 161 #define NETIF_F_GRO_UDP_FWD __NETIF_F(GRO_UDP_FWD) 162 + #define NETIF_F_HW_HSR_TAG_INS __NETIF_F(HW_HSR_TAG_INS) 163 + #define NETIF_F_HW_HSR_TAG_RM __NETIF_F(HW_HSR_TAG_RM) 164 + #define NETIF_F_HW_HSR_FWD __NETIF_F(HW_HSR_FWD) 165 + #define NETIF_F_HW_HSR_DUP __NETIF_F(HW_HSR_DUP) 167 166 168 167 /* Finds the next feature with the highest number of the range of start till 0. 169 168 */
+4
net/ethtool/common.c
··· 69 69 [NETIF_F_GRO_FRAGLIST_BIT] = "rx-gro-list", 70 70 [NETIF_F_HW_MACSEC_BIT] = "macsec-hw-offload", 71 71 [NETIF_F_GRO_UDP_FWD_BIT] = "rx-udp-gro-forwarding", 72 + [NETIF_F_HW_HSR_TAG_INS_BIT] = "hsr-tag-ins-offload", 73 + [NETIF_F_HW_HSR_TAG_RM_BIT] = "hsr-tag-rm-offload", 74 + [NETIF_F_HW_HSR_FWD_BIT] = "hsr-fwd-offload", 75 + [NETIF_F_HW_HSR_DUP_BIT] = "hsr-dup-offload", 72 76 }; 73 77 74 78 const char
+3 -11
net/hsr/hsr_device.c
··· 417 417 .send_sv_frame = send_hsr_supervision_frame, 418 418 .create_tagged_frame = hsr_create_tagged_frame, 419 419 .get_untagged_frame = hsr_get_untagged_frame, 420 + .drop_frame = hsr_drop_frame, 420 421 .fill_frame_info = hsr_fill_frame_info, 421 422 .invalid_dan_ingress_frame = hsr_invalid_dan_ingress_frame, 422 423 }; ··· 465 464 466 465 /* Return true if dev is a HSR master; return false otherwise. 467 466 */ 468 - inline bool is_hsr_master(struct net_device *dev) 467 + bool is_hsr_master(struct net_device *dev) 469 468 { 470 469 return (dev->netdev_ops->ndo_start_xmit == hsr_dev_xmit); 471 470 } 471 + EXPORT_SYMBOL(is_hsr_master); 472 472 473 473 /* Default multicast address for HSR Supervision frames */ 474 474 static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = { ··· 521 519 hsr->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec; 522 520 523 521 hsr->prot_version = protocol_version; 524 - 525 - /* FIXME: should I modify the value of these? 526 - * 527 - * - hsr_dev->flags - i.e. 528 - * IFF_MASTER/SLAVE? 529 - * - hsr_dev->priv_flags - i.e. 530 - * IFF_EBRIDGE? 531 - * IFF_TX_SKB_SHARING? 532 - * IFF_HSR_MASTER/SLAVE? 533 - */ 534 522 535 523 /* Make sure the 1st call to netif_carrier_on() gets through */ 536 524 netif_carrier_off(hsr_dev);
-1
net/hsr/hsr_device.h
··· 19 19 unsigned char multicast_spec, u8 protocol_version, 20 20 struct netlink_ext_ack *extack); 21 21 void hsr_check_carrier_and_operstate(struct hsr_priv *hsr); 22 - bool is_hsr_master(struct net_device *dev); 23 22 int hsr_get_max_mtu(struct hsr_priv *hsr); 24 23 #endif /* __HSR_DEVICE_H */
+24 -3
net/hsr/hsr_forward.c
··· 249 249 /* set the lane id properly */ 250 250 hsr_set_path_id(hsr_ethhdr, port); 251 251 return skb_clone(frame->skb_hsr, GFP_ATOMIC); 252 + } else if (port->dev->features & NETIF_F_HW_HSR_TAG_INS) { 253 + return skb_clone(frame->skb_std, GFP_ATOMIC); 252 254 } 253 255 254 256 /* Create the new skb with enough headroom to fit the HSR tag */ ··· 293 291 return NULL; 294 292 } 295 293 return skb_clone(frame->skb_prp, GFP_ATOMIC); 294 + } else if (port->dev->features & NETIF_F_HW_HSR_TAG_INS) { 295 + return skb_clone(frame->skb_std, GFP_ATOMIC); 296 296 } 297 297 298 298 skb = skb_copy_expand(frame->skb_std, 0, ··· 347 343 port->type == HSR_PT_SLAVE_A)); 348 344 } 349 345 346 + bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port) 347 + { 348 + if (port->dev->features & NETIF_F_HW_HSR_FWD) 349 + return prp_drop_frame(frame, port); 350 + 351 + return false; 352 + } 353 + 350 354 /* Forward the frame through all devices except: 351 355 * - Back through the receiving device 352 356 * - If it's a HSR frame: through a device where it has passed before ··· 371 359 { 372 360 struct hsr_port *port; 373 361 struct sk_buff *skb; 362 + bool sent = false; 374 363 375 364 hsr_for_each_port(frame->port_rcv->hsr, port) { 376 365 struct hsr_priv *hsr = port->hsr; ··· 385 372 386 373 /* Deliver frames directly addressed to us to master only */ 387 374 if (port->type != HSR_PT_MASTER && frame->is_local_exclusive) 375 + continue; 376 + 377 + /* If hardware duplicate generation is enabled, only send out 378 + * one port. 379 + */ 380 + if ((port->dev->features & NETIF_F_HW_HSR_DUP) && sent) 388 381 continue; 389 382 390 383 /* Don't send frame over port where it has been sent before. ··· 424 405 } 425 406 426 407 skb->dev = port->dev; 427 - if (port->type == HSR_PT_MASTER) 408 + if (port->type == HSR_PT_MASTER) { 428 409 hsr_deliver_master(skb, port->dev, frame->node_src); 429 - else 430 - hsr_xmit(skb, port, frame); 410 + } else { 411 + if (!hsr_xmit(skb, port, frame)) 412 + sent = true; 413 + } 431 414 } 432 415 } 433 416
+1
net/hsr/hsr_forward.h
··· 23 23 struct sk_buff *prp_get_untagged_frame(struct hsr_frame_info *frame, 24 24 struct hsr_port *port); 25 25 bool prp_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port); 26 + bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port); 26 27 void prp_fill_frame_info(__be16 proto, struct sk_buff *skb, 27 28 struct hsr_frame_info *frame); 28 29 void hsr_fill_frame_info(__be16 proto, struct sk_buff *skb,
+2
net/hsr/hsr_framereg.c
··· 277 277 skb = frame->skb_hsr; 278 278 else if (frame->skb_prp) 279 279 skb = frame->skb_prp; 280 + else if (frame->skb_std) 281 + skb = frame->skb_std; 280 282 if (!skb) 281 283 return; 282 284
+11
net/hsr/hsr_main.c
··· 131 131 return NULL; 132 132 } 133 133 134 + int hsr_get_version(struct net_device *dev, enum hsr_version *ver) 135 + { 136 + struct hsr_priv *hsr; 137 + 138 + hsr = netdev_priv(dev); 139 + *ver = hsr->prot_version; 140 + 141 + return 0; 142 + } 143 + EXPORT_SYMBOL(hsr_get_version); 144 + 134 145 static struct notifier_block hsr_nb = { 135 146 .notifier_call = hsr_netdev_notify, /* Slave event notifications */ 136 147 };
+1 -7
net/hsr/hsr_main.h
··· 13 13 #include <linux/netdevice.h> 14 14 #include <linux/list.h> 15 15 #include <linux/if_vlan.h> 16 + #include <linux/if_hsr.h> 16 17 17 18 /* Time constants as specified in the HSR specification (IEC-62439-3 2010) 18 19 * Table 8. ··· 170 169 struct net_device *dev; 171 170 struct hsr_priv *hsr; 172 171 enum hsr_port_type type; 173 - }; 174 - 175 - /* used by driver internally to differentiate various protocols */ 176 - enum hsr_version { 177 - HSR_V0 = 0, 178 - HSR_V1, 179 - PRP_V1, 180 172 }; 181 173 182 174 struct hsr_frame_info;
+6 -4
net/hsr/hsr_slave.c
··· 48 48 goto finish_consume; 49 49 } 50 50 51 - /* For HSR, only tagged frames are expected, but for PRP 52 - * there could be non tagged frames as well from Single 53 - * attached nodes (SANs). 51 + /* For HSR, only tagged frames are expected (unless the device offloads 52 + * HSR tag removal), but for PRP there could be non tagged frames as 53 + * well from Single attached nodes (SANs). 54 54 */ 55 55 protocol = eth_hdr(skb)->h_proto; 56 - if (hsr->proto_ops->invalid_dan_ingress_frame && 56 + 57 + if (!(port->dev->features & NETIF_F_HW_HSR_TAG_RM) && 58 + hsr->proto_ops->invalid_dan_ingress_frame && 57 59 hsr->proto_ops->invalid_dan_ingress_frame(protocol)) 58 60 goto finish_pass; 59 61