summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge McCollister <george.mccollister@gmail.com>2021-07-08 21:06:57 +0530
committerVignesh Raghavendra <vigneshr@ti.com>2021-07-09 16:07:21 +0530
commit47c70d67629c54836e2a1831ce966bcf0593ad55 (patch)
tree706fe5075592438f7437046f43217d5949aa44e8
parentd2af4660bc465c6d0bfd970467ba8c89089e7015 (diff)
net: hsr: add offloading support
commit dcf0cd1cc58b8e88793ad6531db9b3a47324ca09 upstream. 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> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com> Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
-rw-r--r--Documentation/networking/netdev-features.rst21
-rw-r--r--include/linux/if_hsr.h27
-rw-r--r--include/linux/netdev_features.h9
-rw-r--r--net/ethtool/common.c4
-rw-r--r--net/hsr/hsr_device.c14
-rw-r--r--net/hsr/hsr_device.h1
-rw-r--r--net/hsr/hsr_forward.c27
-rw-r--r--net/hsr/hsr_forward.h1
-rw-r--r--net/hsr/hsr_framereg.c2
-rw-r--r--net/hsr/hsr_main.c11
-rw-r--r--net/hsr/hsr_main.h8
-rw-r--r--net/hsr/hsr_slave.c10
12 files changed, 109 insertions, 26 deletions
diff --git a/Documentation/networking/netdev-features.rst b/Documentation/networking/netdev-features.rst
index a2d7d7160e39..d7b15bb64deb 100644
--- a/Documentation/networking/netdev-features.rst
+++ b/Documentation/networking/netdev-features.rst
@@ -182,3 +182,24 @@ stricter than Hardware LRO. A packet stream merged by Hardware GRO must
be re-segmentable by GSO or TSO back to the exact original packet stream.
Hardware GRO is dependent on RXCSUM since every packet successfully merged
by hardware must also have the checksum verified by hardware.
+
+* hsr-tag-ins-offload
+
+This should be set for devices which insert an HSR (High-availability Seamless
+Redundancy) or PRP (Parallel Redundancy Protocol) tag automatically.
+
+* hsr-tag-rm-offload
+
+This should be set for devices which remove HSR (High-availability Seamless
+Redundancy) or PRP (Parallel Redundancy Protocol) tags automatically.
+
+* hsr-fwd-offload
+
+This should be set for devices which forward HSR (High-availability Seamless
+Redundancy) frames from one port to another in hardware.
+
+* hsr-dup-offload
+
+This should be set for devices which duplicate outgoing HSR (High-availability
+Seamless Redundancy) or PRP (Parallel Redundancy Protocol) tags automatically
+frames in hardware.
diff --git a/include/linux/if_hsr.h b/include/linux/if_hsr.h
new file mode 100644
index 000000000000..38bbc537d4e4
--- /dev/null
+++ b/include/linux/if_hsr.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_IF_HSR_H_
+#define _LINUX_IF_HSR_H_
+
+/* used to differentiate various protocols */
+enum hsr_version {
+ HSR_V0 = 0,
+ HSR_V1,
+ PRP_V1,
+};
+
+#if IS_ENABLED(CONFIG_HSR)
+extern bool is_hsr_master(struct net_device *dev);
+extern int hsr_get_version(struct net_device *dev, enum hsr_version *ver);
+#else
+static inline bool is_hsr_master(struct net_device *dev)
+{
+ return false;
+}
+static inline int hsr_get_version(struct net_device *dev,
+ enum hsr_version *ver)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_HSR */
+
+#endif /*_LINUX_IF_HSR_H_*/
diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h
index 0b17c4322b09..5254738e1b41 100644
--- a/include/linux/netdev_features.h
+++ b/include/linux/netdev_features.h
@@ -85,6 +85,11 @@ enum {
NETIF_F_HW_MACSEC_BIT, /* Offload MACsec operations */
+ NETIF_F_HW_HSR_TAG_INS_BIT, /* Offload HSR tag insertion */
+ NETIF_F_HW_HSR_TAG_RM_BIT, /* Offload HSR tag removal */
+ NETIF_F_HW_HSR_FWD_BIT, /* Offload HSR forwarding */
+ NETIF_F_HW_HSR_DUP_BIT, /* Offload HSR duplication */
+
/*
* Add your fresh new feature above and remember to update
* netdev_features_strings[] in net/core/ethtool.c and maybe
@@ -157,6 +162,10 @@ enum {
#define NETIF_F_GRO_FRAGLIST __NETIF_F(GRO_FRAGLIST)
#define NETIF_F_GSO_FRAGLIST __NETIF_F(GSO_FRAGLIST)
#define NETIF_F_HW_MACSEC __NETIF_F(HW_MACSEC)
+#define NETIF_F_HW_HSR_TAG_INS __NETIF_F(HW_HSR_TAG_INS)
+#define NETIF_F_HW_HSR_TAG_RM __NETIF_F(HW_HSR_TAG_RM)
+#define NETIF_F_HW_HSR_FWD __NETIF_F(HW_HSR_FWD)
+#define NETIF_F_HW_HSR_DUP __NETIF_F(HW_HSR_DUP)
/* Finds the next feature with the highest number of the range of start till 0.
*/
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index 1bd7872eb091..382829daae36 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -68,6 +68,10 @@ const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = {
[NETIF_F_HW_TLS_RX_BIT] = "tls-hw-rx-offload",
[NETIF_F_GRO_FRAGLIST_BIT] = "rx-gro-list",
[NETIF_F_HW_MACSEC_BIT] = "macsec-hw-offload",
+ [NETIF_F_HW_HSR_TAG_INS_BIT] = "hsr-tag-ins-offload",
+ [NETIF_F_HW_HSR_TAG_RM_BIT] = "hsr-tag-rm-offload",
+ [NETIF_F_HW_HSR_FWD_BIT] = "hsr-fwd-offload",
+ [NETIF_F_HW_HSR_DUP_BIT] = "hsr-dup-offload",
};
const char
diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c
index 6f4c34b6a5d6..96afec2f2db1 100644
--- a/net/hsr/hsr_device.c
+++ b/net/hsr/hsr_device.c
@@ -443,6 +443,7 @@ static struct hsr_proto_ops hsr_ops = {
.send_sv_frame = send_hsr_supervision_frame,
.create_tagged_frame = hsr_create_tagged_frame,
.get_untagged_frame = hsr_get_untagged_frame,
+ .drop_frame = hsr_drop_frame,
.fill_frame_info = hsr_fill_frame_info,
.invalid_dan_ingress_frame = hsr_invalid_dan_ingress_frame,
};
@@ -490,10 +491,11 @@ void hsr_dev_setup(struct net_device *dev)
/* Return true if dev is a HSR master; return false otherwise.
*/
-inline bool is_hsr_master(struct net_device *dev)
+bool is_hsr_master(struct net_device *dev)
{
return (dev->netdev_ops->ndo_start_xmit == hsr_dev_xmit);
}
+EXPORT_SYMBOL(is_hsr_master);
/* Default multicast address for HSR Supervision frames */
static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = {
@@ -546,16 +548,6 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
hsr->prot_version = protocol_version;
- /* FIXME: should I modify the value of these?
- *
- * - hsr_dev->flags - i.e.
- * IFF_MASTER/SLAVE?
- * - hsr_dev->priv_flags - i.e.
- * IFF_EBRIDGE?
- * IFF_TX_SKB_SHARING?
- * IFF_HSR_MASTER/SLAVE?
- */
-
/* Make sure the 1st call to netif_carrier_on() gets through */
netif_carrier_off(hsr_dev);
diff --git a/net/hsr/hsr_device.h b/net/hsr/hsr_device.h
index 868373822ee4..9060c92168f9 100644
--- a/net/hsr/hsr_device.h
+++ b/net/hsr/hsr_device.h
@@ -19,6 +19,5 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
unsigned char multicast_spec, u8 protocol_version,
struct netlink_ext_ack *extack);
void hsr_check_carrier_and_operstate(struct hsr_priv *hsr);
-bool is_hsr_master(struct net_device *dev);
int hsr_get_max_mtu(struct hsr_priv *hsr);
#endif /* __HSR_DEVICE_H */
diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c
index 90c72e4c0a8f..070088e4a66c 100644
--- a/net/hsr/hsr_forward.c
+++ b/net/hsr/hsr_forward.c
@@ -247,6 +247,8 @@ struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame,
/* set the lane id properly */
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);
}
/* Create the new skb with enough headroom to fit the HSR tag */
@@ -289,6 +291,8 @@ struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame,
return NULL;
}
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_copy_expand(frame->skb_std, 0,
@@ -341,6 +345,14 @@ bool prp_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)
port->type == HSR_PT_SLAVE_A));
}
+bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)
+{
+ if (port->dev->features & NETIF_F_HW_HSR_FWD)
+ return prp_drop_frame(frame, port);
+
+ return false;
+}
+
/* Forward the frame through all devices except:
* - Back through the receiving device
* - If it's a HSR frame: through a device where it has passed before
@@ -357,6 +369,7 @@ static void hsr_forward_do(struct hsr_frame_info *frame)
{
struct hsr_port *port;
struct sk_buff *skb;
+ bool sent = false;
hsr_for_each_port(frame->port_rcv->hsr, port) {
struct hsr_priv *hsr = port->hsr;
@@ -372,6 +385,12 @@ static void hsr_forward_do(struct hsr_frame_info *frame)
if (port->type != HSR_PT_MASTER && frame->is_local_exclusive)
continue;
+ /* If hardware duplicate generation is enabled, only send out
+ * one port.
+ */
+ if ((port->dev->features & NETIF_F_HW_HSR_DUP) && sent)
+ continue;
+
/* Don't send frame over port where it has been sent before.
* Also fro SAN, this shouldn't be done.
*/
@@ -403,10 +422,12 @@ static void hsr_forward_do(struct hsr_frame_info *frame)
}
skb->dev = port->dev;
- if (port->type == HSR_PT_MASTER)
+ if (port->type == HSR_PT_MASTER) {
hsr_deliver_master(skb, port->dev, frame->node_src);
- else
- hsr_xmit(skb, port, frame);
+ } else {
+ if (!hsr_xmit(skb, port, frame))
+ sent = true;
+ }
}
}
diff --git a/net/hsr/hsr_forward.h b/net/hsr/hsr_forward.h
index 618140d484ad..b6acaafa83fc 100644
--- a/net/hsr/hsr_forward.h
+++ b/net/hsr/hsr_forward.h
@@ -23,6 +23,7 @@ struct sk_buff *hsr_get_untagged_frame(struct hsr_frame_info *frame,
struct sk_buff *prp_get_untagged_frame(struct hsr_frame_info *frame,
struct hsr_port *port);
bool prp_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port);
+bool hsr_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port);
void prp_fill_frame_info(__be16 proto, struct sk_buff *skb,
struct hsr_frame_info *frame);
void hsr_fill_frame_info(__be16 proto, struct sk_buff *skb,
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
index 805f974923b9..bb1351c38397 100644
--- a/net/hsr/hsr_framereg.c
+++ b/net/hsr/hsr_framereg.c
@@ -279,6 +279,8 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
skb = frame->skb_hsr;
else if (frame->skb_prp)
skb = frame->skb_prp;
+ else if (frame->skb_std)
+ skb = frame->skb_std;
if (!skb)
return;
diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c
index 2fd1976e5b1c..f7e284f23b1f 100644
--- a/net/hsr/hsr_main.c
+++ b/net/hsr/hsr_main.c
@@ -131,6 +131,17 @@ struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt)
return NULL;
}
+int hsr_get_version(struct net_device *dev, enum hsr_version *ver)
+{
+ struct hsr_priv *hsr;
+
+ hsr = netdev_priv(dev);
+ *ver = hsr->prot_version;
+
+ return 0;
+}
+EXPORT_SYMBOL(hsr_get_version);
+
static struct notifier_block hsr_nb = {
.notifier_call = hsr_netdev_notify, /* Slave event notifications */
};
diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h
index f79ca55d6986..1fd67e850c95 100644
--- a/net/hsr/hsr_main.h
+++ b/net/hsr/hsr_main.h
@@ -13,6 +13,7 @@
#include <linux/netdevice.h>
#include <linux/list.h>
#include <linux/if_vlan.h>
+#include <linux/if_hsr.h>
/* Time constants as specified in the HSR specification (IEC-62439-3 2010)
* Table 8.
@@ -172,13 +173,6 @@ struct hsr_port {
enum hsr_port_type type;
};
-/* used by driver internally to differentiate various protocols */
-enum hsr_version {
- HSR_V0 = 0,
- HSR_V1,
- PRP_V1,
-};
-
struct hsr_frame_info;
struct hsr_node;
diff --git a/net/hsr/hsr_slave.c b/net/hsr/hsr_slave.c
index 36d5fcf09c61..c5227d42faf5 100644
--- a/net/hsr/hsr_slave.c
+++ b/net/hsr/hsr_slave.c
@@ -48,12 +48,14 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
goto finish_consume;
}
- /* For HSR, only tagged frames are expected, but for PRP
- * there could be non tagged frames as well from Single
- * attached nodes (SANs).
+ /* For HSR, only tagged frames are expected (unless the device offloads
+ * HSR tag removal), but for PRP there could be non tagged frames as
+ * well from Single attached nodes (SANs).
*/
protocol = eth_hdr(skb)->h_proto;
- if (hsr->proto_ops->invalid_dan_ingress_frame &&
+
+ if (!(port->dev->features & NETIF_F_HW_HSR_TAG_RM) &&
+ hsr->proto_ops->invalid_dan_ingress_frame &&
hsr->proto_ops->invalid_dan_ingress_frame(protocol))
goto finish_pass;