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>
This commit is contained in:
parent
78be9217c4
commit
dcf0cd1cc5
|
@ -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.
|
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
|
Hardware GRO is dependent on RXCSUM since every packet successfully merged
|
||||||
by hardware must also have the checksum verified by hardware.
|
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.
|
||||||
|
|
|
@ -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_*/
|
|
@ -86,6 +86,11 @@ enum {
|
||||||
NETIF_F_HW_MACSEC_BIT, /* Offload MACsec operations */
|
NETIF_F_HW_MACSEC_BIT, /* Offload MACsec operations */
|
||||||
NETIF_F_GRO_UDP_FWD_BIT, /* Allow UDP GRO for forwarding */
|
NETIF_F_GRO_UDP_FWD_BIT, /* Allow UDP GRO for forwarding */
|
||||||
|
|
||||||
|
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
|
* Add your fresh new feature above and remember to update
|
||||||
* netdev_features_strings[] in net/core/ethtool.c and maybe
|
* netdev_features_strings[] in net/core/ethtool.c and maybe
|
||||||
|
@ -159,6 +164,10 @@ enum {
|
||||||
#define NETIF_F_GSO_FRAGLIST __NETIF_F(GSO_FRAGLIST)
|
#define NETIF_F_GSO_FRAGLIST __NETIF_F(GSO_FRAGLIST)
|
||||||
#define NETIF_F_HW_MACSEC __NETIF_F(HW_MACSEC)
|
#define NETIF_F_HW_MACSEC __NETIF_F(HW_MACSEC)
|
||||||
#define NETIF_F_GRO_UDP_FWD __NETIF_F(GRO_UDP_FWD)
|
#define NETIF_F_GRO_UDP_FWD __NETIF_F(GRO_UDP_FWD)
|
||||||
|
#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.
|
/* Finds the next feature with the highest number of the range of start till 0.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -69,6 +69,10 @@ const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = {
|
||||||
[NETIF_F_GRO_FRAGLIST_BIT] = "rx-gro-list",
|
[NETIF_F_GRO_FRAGLIST_BIT] = "rx-gro-list",
|
||||||
[NETIF_F_HW_MACSEC_BIT] = "macsec-hw-offload",
|
[NETIF_F_HW_MACSEC_BIT] = "macsec-hw-offload",
|
||||||
[NETIF_F_GRO_UDP_FWD_BIT] = "rx-udp-gro-forwarding",
|
[NETIF_F_GRO_UDP_FWD_BIT] = "rx-udp-gro-forwarding",
|
||||||
|
[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
|
const char
|
||||||
|
|
|
@ -417,6 +417,7 @@ static struct hsr_proto_ops hsr_ops = {
|
||||||
.send_sv_frame = send_hsr_supervision_frame,
|
.send_sv_frame = send_hsr_supervision_frame,
|
||||||
.create_tagged_frame = hsr_create_tagged_frame,
|
.create_tagged_frame = hsr_create_tagged_frame,
|
||||||
.get_untagged_frame = hsr_get_untagged_frame,
|
.get_untagged_frame = hsr_get_untagged_frame,
|
||||||
|
.drop_frame = hsr_drop_frame,
|
||||||
.fill_frame_info = hsr_fill_frame_info,
|
.fill_frame_info = hsr_fill_frame_info,
|
||||||
.invalid_dan_ingress_frame = hsr_invalid_dan_ingress_frame,
|
.invalid_dan_ingress_frame = hsr_invalid_dan_ingress_frame,
|
||||||
};
|
};
|
||||||
|
@ -464,10 +465,11 @@ void hsr_dev_setup(struct net_device *dev)
|
||||||
|
|
||||||
/* Return true if dev is a HSR master; return false otherwise.
|
/* 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);
|
return (dev->netdev_ops->ndo_start_xmit == hsr_dev_xmit);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(is_hsr_master);
|
||||||
|
|
||||||
/* Default multicast address for HSR Supervision frames */
|
/* Default multicast address for HSR Supervision frames */
|
||||||
static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = {
|
static const unsigned char def_multicast_addr[ETH_ALEN] __aligned(2) = {
|
||||||
|
@ -520,16 +522,6 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
|
||||||
|
|
||||||
hsr->prot_version = protocol_version;
|
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 */
|
/* Make sure the 1st call to netif_carrier_on() gets through */
|
||||||
netif_carrier_off(hsr_dev);
|
netif_carrier_off(hsr_dev);
|
||||||
|
|
||||||
|
|
|
@ -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,
|
unsigned char multicast_spec, u8 protocol_version,
|
||||||
struct netlink_ext_ack *extack);
|
struct netlink_ext_ack *extack);
|
||||||
void hsr_check_carrier_and_operstate(struct hsr_priv *hsr);
|
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);
|
int hsr_get_max_mtu(struct hsr_priv *hsr);
|
||||||
#endif /* __HSR_DEVICE_H */
|
#endif /* __HSR_DEVICE_H */
|
||||||
|
|
|
@ -249,6 +249,8 @@ struct sk_buff *hsr_create_tagged_frame(struct hsr_frame_info *frame,
|
||||||
/* set the lane id properly */
|
/* set the lane id properly */
|
||||||
hsr_set_path_id(hsr_ethhdr, port);
|
hsr_set_path_id(hsr_ethhdr, port);
|
||||||
return skb_clone(frame->skb_hsr, GFP_ATOMIC);
|
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 */
|
/* Create the new skb with enough headroom to fit the HSR tag */
|
||||||
|
@ -291,6 +293,8 @@ struct sk_buff *prp_create_tagged_frame(struct hsr_frame_info *frame,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return skb_clone(frame->skb_prp, GFP_ATOMIC);
|
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,
|
skb = skb_copy_expand(frame->skb_std, 0,
|
||||||
|
@ -343,6 +347,14 @@ bool prp_drop_frame(struct hsr_frame_info *frame, struct hsr_port *port)
|
||||||
port->type == HSR_PT_SLAVE_A));
|
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:
|
/* Forward the frame through all devices except:
|
||||||
* - Back through the receiving device
|
* - Back through the receiving device
|
||||||
* - If it's a HSR frame: through a device where it has passed before
|
* - If it's a HSR frame: through a device where it has passed before
|
||||||
|
@ -359,6 +371,7 @@ static void hsr_forward_do(struct hsr_frame_info *frame)
|
||||||
{
|
{
|
||||||
struct hsr_port *port;
|
struct hsr_port *port;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
|
bool sent = false;
|
||||||
|
|
||||||
hsr_for_each_port(frame->port_rcv->hsr, port) {
|
hsr_for_each_port(frame->port_rcv->hsr, port) {
|
||||||
struct hsr_priv *hsr = port->hsr;
|
struct hsr_priv *hsr = port->hsr;
|
||||||
|
@ -374,6 +387,12 @@ static void hsr_forward_do(struct hsr_frame_info *frame)
|
||||||
if (port->type != HSR_PT_MASTER && frame->is_local_exclusive)
|
if (port->type != HSR_PT_MASTER && frame->is_local_exclusive)
|
||||||
continue;
|
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.
|
/* Don't send frame over port where it has been sent before.
|
||||||
* Also fro SAN, this shouldn't be done.
|
* Also fro SAN, this shouldn't be done.
|
||||||
*/
|
*/
|
||||||
|
@ -405,10 +424,12 @@ static void hsr_forward_do(struct hsr_frame_info *frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
skb->dev = port->dev;
|
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);
|
hsr_deliver_master(skb, port->dev, frame->node_src);
|
||||||
else
|
} else {
|
||||||
hsr_xmit(skb, port, frame);
|
if (!hsr_xmit(skb, port, frame))
|
||||||
|
sent = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 sk_buff *prp_get_untagged_frame(struct hsr_frame_info *frame,
|
||||||
struct hsr_port *port);
|
struct hsr_port *port);
|
||||||
bool prp_drop_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,
|
void prp_fill_frame_info(__be16 proto, struct sk_buff *skb,
|
||||||
struct hsr_frame_info *frame);
|
struct hsr_frame_info *frame);
|
||||||
void hsr_fill_frame_info(__be16 proto, struct sk_buff *skb,
|
void hsr_fill_frame_info(__be16 proto, struct sk_buff *skb,
|
||||||
|
|
|
@ -277,6 +277,8 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
|
||||||
skb = frame->skb_hsr;
|
skb = frame->skb_hsr;
|
||||||
else if (frame->skb_prp)
|
else if (frame->skb_prp)
|
||||||
skb = frame->skb_prp;
|
skb = frame->skb_prp;
|
||||||
|
else if (frame->skb_std)
|
||||||
|
skb = frame->skb_std;
|
||||||
if (!skb)
|
if (!skb)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,17 @@ struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt)
|
||||||
return NULL;
|
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 = {
|
static struct notifier_block hsr_nb = {
|
||||||
.notifier_call = hsr_netdev_notify, /* Slave event notifications */
|
.notifier_call = hsr_netdev_notify, /* Slave event notifications */
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/if_vlan.h>
|
#include <linux/if_vlan.h>
|
||||||
|
#include <linux/if_hsr.h>
|
||||||
|
|
||||||
/* Time constants as specified in the HSR specification (IEC-62439-3 2010)
|
/* Time constants as specified in the HSR specification (IEC-62439-3 2010)
|
||||||
* Table 8.
|
* Table 8.
|
||||||
|
@ -171,13 +172,6 @@ struct hsr_port {
|
||||||
enum hsr_port_type type;
|
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_frame_info;
|
||||||
struct hsr_node;
|
struct hsr_node;
|
||||||
|
|
||||||
|
|
|
@ -48,12 +48,14 @@ static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
|
||||||
goto finish_consume;
|
goto finish_consume;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For HSR, only tagged frames are expected, but for PRP
|
/* For HSR, only tagged frames are expected (unless the device offloads
|
||||||
* there could be non tagged frames as well from Single
|
* HSR tag removal), but for PRP there could be non tagged frames as
|
||||||
* attached nodes (SANs).
|
* well from Single attached nodes (SANs).
|
||||||
*/
|
*/
|
||||||
protocol = eth_hdr(skb)->h_proto;
|
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))
|
hsr->proto_ops->invalid_dan_ingress_frame(protocol))
|
||||||
goto finish_pass;
|
goto finish_pass;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue