Commit f266a683 authored by Arvid Brodin's avatar Arvid Brodin Committed by David S. Miller

net/hsr: Better frame dispatch

This patch removes the separate paths for frames coming from the outside, and
frames sent from the HSR device, and instead makes all frames go through
hsr_forward_skb() in hsr_forward.c. This greatly improves code readability and
also opens up the possibility for future support of the HSR Interlink device
that is the basis for HSR RedBoxes and HSR QuadBoxes, as well as VLAN
compatibility.

Other improvements:
* A reduction in the number of times an skb is copied on machines without
  HAVE_EFFICIENT_UNALIGNED_ACCESS, which improves throughput somewhat.
* Headers are now created using the standard eth_header(), and using the
  standard hard_header_len.
* Each HSR slave now gets its own private skb, so slave-specific fields can be
  correctly set.
Signed-off-by: default avatarArvid Brodin <arvid.brodin@alten.se>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4c3477dc
......@@ -4,5 +4,5 @@
obj-$(CONFIG_HSR) += hsr.o
hsr-y := hsr_main.o hsr_framereg.o hsr_device.o hsr_netlink.o \
hsr_slave.o
hsr-y := hsr_main.o hsr_framereg.o hsr_device.o \
hsr_netlink.o hsr_slave.o hsr_forward.o
......@@ -21,6 +21,7 @@
#include "hsr_slave.h"
#include "hsr_framereg.h"
#include "hsr_main.h"
#include "hsr_forward.h"
static bool is_admin_up(struct net_device *dev)
......@@ -231,141 +232,21 @@ static netdev_features_t hsr_fix_features(struct net_device *dev,
}
static void hsr_fill_tag(struct hsr_ethhdr *hsr_ethhdr, struct hsr_priv *hsr)
{
unsigned long irqflags;
/* IEC 62439-1:2010, p 48, says the 4-bit "path" field can take values
* between 0001-1001 ("ring identifier", for regular HSR frames),
* or 1111 ("HSR management", supervision frames). Unfortunately, the
* spec writers forgot to explain what a "ring identifier" is, or
* how it is used. So we just set this to 0001 for regular frames,
* and 1111 for supervision frames.
*/
set_hsr_tag_path(&hsr_ethhdr->hsr_tag, 0x1);
/* IEC 62439-1:2010, p 12: "The link service data unit in an Ethernet
* frame is the content of the frame located between the Length/Type
* field and the Frame Check Sequence."
*
* IEC 62439-3, p 48, specifies the "original LPDU" to include the
* original "LT" field (what "LT" means is not explained anywhere as
* far as I can see - perhaps "Length/Type"?). So LSDU_size might
* equal original length + 2.
* Also, the fact that this field is not used anywhere (might be used
* by a RedBox connecting HSR and PRP nets?) means I cannot test its
* correctness. Instead of guessing, I set this to 0 here, to make any
* problems immediately apparent. Anyone using this driver with PRP/HSR
* RedBoxes might need to fix this...
*/
set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, 0);
spin_lock_irqsave(&hsr->seqnr_lock, irqflags);
hsr_ethhdr->hsr_tag.sequence_nr = htons(hsr->sequence_nr);
hsr->sequence_nr++;
spin_unlock_irqrestore(&hsr->seqnr_lock, irqflags);
hsr_ethhdr->hsr_tag.encap_proto = hsr_ethhdr->ethhdr.h_proto;
hsr_ethhdr->ethhdr.h_proto = htons(ETH_P_PRP);
}
static int slave_xmit(struct hsr_priv *hsr, struct sk_buff *skb,
enum hsr_port_type type)
{
struct hsr_port *port;
struct hsr_ethhdr *hsr_ethhdr;
hsr_ethhdr = (struct hsr_ethhdr *) skb->data;
rcu_read_lock();
port = hsr_port_get_hsr(hsr, type);
if (!port) {
rcu_read_unlock();
return NET_XMIT_DROP;
}
skb->dev = port->dev;
hsr_addr_subst_dest(port->hsr, &hsr_ethhdr->ethhdr, port);
rcu_read_unlock();
/* Address substitution (IEC62439-3 pp 26, 50): replace mac
* address of outgoing frame with that of the outgoing slave's.
*/
ether_addr_copy(hsr_ethhdr->ethhdr.h_source, skb->dev->dev_addr);
return dev_queue_xmit(skb);
}
static int hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct hsr_priv *hsr;
struct hsr_priv *hsr = netdev_priv(dev);
struct hsr_port *master;
struct hsr_ethhdr *hsr_ethhdr;
struct sk_buff *skb2;
int res1, res2;
hsr = netdev_priv(dev);
hsr_ethhdr = (struct hsr_ethhdr *) skb->data;
if ((skb->protocol != htons(ETH_P_PRP)) ||
(hsr_ethhdr->ethhdr.h_proto != htons(ETH_P_PRP))) {
hsr_fill_tag(hsr_ethhdr, hsr);
skb->protocol = htons(ETH_P_PRP);
}
skb2 = pskb_copy(skb, GFP_ATOMIC);
res1 = slave_xmit(hsr, skb, HSR_PT_SLAVE_A);
if (skb2)
res2 = slave_xmit(hsr, skb2, HSR_PT_SLAVE_B);
else
res2 = NET_XMIT_DROP;
rcu_read_lock();
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN ||
res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) {
master->dev->stats.tx_packets++;
master->dev->stats.tx_bytes += skb->len;
} else {
master->dev->stats.tx_dropped++;
}
rcu_read_unlock();
skb->dev = master->dev;
hsr_forward_skb(skb, master);
return NETDEV_TX_OK;
}
static int hsr_header_create(struct sk_buff *skb, struct net_device *dev,
unsigned short type, const void *daddr,
const void *saddr, unsigned int len)
{
int res;
/* Make room for the HSR tag now. We will fill it in later (in
* hsr_dev_xmit)
*/
if (skb_headroom(skb) < HSR_HLEN + ETH_HLEN)
return -ENOBUFS;
skb_push(skb, HSR_HLEN);
/* To allow VLAN/HSR combos we should probably use
* res = dev_hard_header(skb, dev, type, daddr, saddr, len + HSR_HLEN);
* here instead. It would require other changes too, though - e.g.
* separate headers for each slave etc...
*/
res = eth_header(skb, dev, type, daddr, saddr, len + HSR_HLEN);
if (res <= 0)
return res;
skb_reset_mac_header(skb);
return res + HSR_HLEN;
}
static const struct header_ops hsr_header_ops = {
.create = hsr_header_create,
.create = eth_header,
.parse = eth_header_parse,
};
......@@ -382,19 +263,13 @@ static int hsr_pad(int size)
return min_size;
}
static void send_hsr_supervision_frame(struct net_device *hsr_dev, u8 type)
static void send_hsr_supervision_frame(struct hsr_port *master, u8 type)
{
struct hsr_priv *hsr;
struct hsr_port *master;
struct sk_buff *skb;
int hlen, tlen;
struct hsr_sup_tag *hsr_stag;
struct hsr_sup_payload *hsr_sp;
unsigned long irqflags;
int res;
hsr = netdev_priv(hsr_dev);
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
hlen = LL_RESERVED_SPACE(master->dev);
tlen = master->dev->needed_tailroom;
......@@ -410,33 +285,30 @@ static void send_hsr_supervision_frame(struct net_device *hsr_dev, u8 type)
skb->protocol = htons(ETH_P_PRP);
skb->priority = TC_PRIO_CONTROL;
res = dev_hard_header(skb, skb->dev, ETH_P_PRP,
hsr->sup_multicast_addr,
skb->dev->dev_addr, skb->len);
if (res <= 0)
if (dev_hard_header(skb, skb->dev, ETH_P_PRP,
master->hsr->sup_multicast_addr,
skb->dev->dev_addr, skb->len) <= 0)
goto out;
skb_reset_mac_header(skb);
skb_pull(skb, sizeof(struct ethhdr));
hsr_stag = (typeof(hsr_stag)) skb->data;
hsr_stag = (typeof(hsr_stag)) skb_put(skb, sizeof(*hsr_stag));
set_hsr_stag_path(hsr_stag, 0xf);
set_hsr_stag_HSR_Ver(hsr_stag, 0);
spin_lock_irqsave(&hsr->seqnr_lock, irqflags);
hsr_stag->sequence_nr = htons(hsr->sequence_nr);
hsr->sequence_nr++;
spin_unlock_irqrestore(&hsr->seqnr_lock, irqflags);
spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags);
hsr_stag->sequence_nr = htons(master->hsr->sequence_nr);
master->hsr->sequence_nr++;
spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);
hsr_stag->HSR_TLV_Type = type;
hsr_stag->HSR_TLV_Length = 12;
skb_push(skb, sizeof(struct ethhdr));
/* Payload: MacAddressA */
hsr_sp = (typeof(hsr_sp)) skb_put(skb, sizeof(*hsr_sp));
ether_addr_copy(hsr_sp->MacAddressA, master->dev->dev_addr);
dev_queue_xmit(skb);
hsr_forward_skb(skb, master);
return;
out:
......@@ -453,13 +325,15 @@ static void hsr_announce(unsigned long data)
struct hsr_port *master;
hsr = (struct hsr_priv *) data;
rcu_read_lock();
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
if (hsr->announce_count < 3) {
send_hsr_supervision_frame(master->dev, HSR_TLV_ANNOUNCE);
send_hsr_supervision_frame(master, HSR_TLV_ANNOUNCE);
hsr->announce_count++;
} else {
send_hsr_supervision_frame(master->dev, HSR_TLV_LIFE_CHECK);
send_hsr_supervision_frame(master, HSR_TLV_LIFE_CHECK);
}
if (hsr->announce_count < 3)
......@@ -471,6 +345,8 @@ static void hsr_announce(unsigned long data)
if (is_admin_up(master->dev))
add_timer(&hsr->announce_timer);
rcu_read_unlock();
}
......@@ -570,7 +446,7 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
spin_lock_init(&hsr->seqnr_lock);
/* Overflow soon to find bugs easier: */
hsr->sequence_nr = USHRT_MAX - 1024;
hsr->sequence_nr = HSR_SEQNR_START;
init_timer(&hsr->announce_timer);
hsr->announce_timer.function = hsr_announce;
......
This diff is collapsed.
/* Copyright 2011-2014 Autronica Fire and Security AS
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* Author(s):
* 2011-2014 Arvid Brodin, arvid.brodin@alten.se
*/
#ifndef __HSR_FORWARD_H
#define __HSR_FORWARD_H
#include <linux/netdevice.h>
#include "hsr_main.h"
void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port);
#endif /* __HSR_FORWARD_H */
This diff is collapsed.
......@@ -16,19 +16,22 @@
struct hsr_node;
struct hsr_node *hsr_find_node(struct list_head *node_db, struct sk_buff *skb);
struct hsr_node *hsr_merge_node(struct hsr_node *node, struct sk_buff *skb,
struct hsr_port *port);
void hsr_addr_subst_source(struct hsr_priv *hsr, struct sk_buff *skb);
void hsr_addr_subst_dest(struct hsr_priv *hsr, struct ethhdr *ethhdr,
struct hsr_node *hsr_add_node(struct list_head *node_db, unsigned char addr[],
u16 seq_out);
struct hsr_node *hsr_get_node(struct list_head *node_db, struct sk_buff *skb,
bool is_sup);
void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
struct hsr_port *port);
bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr);
void hsr_addr_subst_source(struct hsr_node *node, struct sk_buff *skb);
void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
struct hsr_port *port);
void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port);
int hsr_register_frame_out(struct hsr_node *node, struct hsr_port *port,
struct sk_buff *skb);
void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port,
u16 sequence_nr);
int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node,
u16 sequence_nr);
void hsr_prune_nodes(unsigned long data);
......
......@@ -20,26 +20,6 @@
#include "hsr_slave.h"
/* List of all registered virtual HSR devices */
static LIST_HEAD(hsr_list);
void register_hsr_master(struct hsr_priv *hsr)
{
list_add_tail_rcu(&hsr->hsr_list, &hsr_list);
}
void unregister_hsr_master(struct hsr_priv *hsr)
{
struct hsr_priv *hsr_it;
list_for_each_entry(hsr_it, &hsr_list, hsr_list)
if (hsr_it == hsr) {
list_del_rcu(&hsr_it->hsr_list);
return;
}
}
static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,
void *ptr)
{
......
......@@ -29,6 +29,7 @@
* each node differ before we notify of communication problem?
*/
#define MAX_SLAVE_DIFF 3000 /* ms */
#define HSR_SEQNR_START (USHRT_MAX - 1024)
/* How often shall we check for broken ring and remove node entries older than
......@@ -153,10 +154,9 @@ struct hsr_port {
};
struct hsr_priv {
struct list_head hsr_list; /* List of hsr devices */
struct rcu_head rcu_head;
struct list_head ports;
struct list_head node_db; /* Other HSR nodes */
struct list_head node_db; /* Known HSR nodes */
struct list_head self_node_db; /* MACs of slaves */
struct timer_list announce_timer; /* Supervision frame dispatch */
struct timer_list prune_timer;
......@@ -166,6 +166,18 @@ struct hsr_priv {
unsigned char sup_multicast_addr[ETH_ALEN];
};
#define hsr_for_each_port(hsr, port) \
list_for_each_entry_rcu((port), &(hsr)->ports, port_list)
struct hsr_port *hsr_port_get_hsr(struct hsr_priv *hsr, enum hsr_port_type pt);
/* Caller must ensure skb is a valid HSR frame */
static inline u16 hsr_get_skb_sequence_nr(struct sk_buff *skb)
{
struct hsr_ethhdr *hsr_ethhdr;
hsr_ethhdr = (struct hsr_ethhdr *) skb_mac_header(skb);
return ntohs(hsr_ethhdr->hsr_tag.sequence_nr);
}
#endif /* __HSR_PRIVATE_H */
......@@ -358,7 +358,7 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
return res;
}
/* Get a list of MacAddressA of all nodes known to this node (other than self).
/* Get a list of MacAddressA of all nodes known to this node (including self).
*/
static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info)
{
......
......@@ -14,9 +14,51 @@
#include <linux/if_arp.h>
#include "hsr_main.h"
#include "hsr_device.h"
#include "hsr_forward.h"
#include "hsr_framereg.h"
static rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct hsr_port *port;
if (!skb_mac_header_was_set(skb)) {
WARN_ONCE(1, "%s: skb invalid", __func__);
return RX_HANDLER_PASS;
}
rcu_read_lock(); /* hsr->node_db, hsr->ports */
port = hsr_port_get_rcu(skb->dev);
if (hsr_addr_is_self(port->hsr, eth_hdr(skb)->h_source)) {
/* Directly kill frames sent by ourselves */
kfree_skb(skb);
goto finish_consume;
}
if (eth_hdr(skb)->h_proto != htons(ETH_P_PRP))
goto finish_pass;
skb_push(skb, ETH_HLEN);
hsr_forward_skb(skb, port);
finish_consume:
rcu_read_unlock(); /* hsr->node_db, hsr->ports */
return RX_HANDLER_CONSUMED;
finish_pass:
rcu_read_unlock(); /* hsr->node_db, hsr->ports */
return RX_HANDLER_PASS;
}
bool hsr_port_exists(const struct net_device *dev)
{
return rcu_access_pointer(dev->rx_handler) == hsr_handle_frame;
}
static int hsr_check_dev_ok(struct net_device *dev)
{
/* Don't allow HSR on non-ethernet like devices */
......@@ -42,6 +84,11 @@ static int hsr_check_dev_ok(struct net_device *dev)
return -EINVAL;
}
if (dev->priv_flags & IFF_DONT_BRIDGE) {
netdev_info(dev, "This device does not support bridging.\n");
return -EOPNOTSUPP;
}
/* HSR over bonded devices has not been tested, but I'm not sure it
* won't work...
*/
......@@ -50,232 +97,6 @@ static int hsr_check_dev_ok(struct net_device *dev)
}
static struct sk_buff *hsr_pull_tag(struct sk_buff *skb)
{
struct hsr_tag *hsr_tag;
struct sk_buff *skb2;
skb2 = skb_share_check(skb, GFP_ATOMIC);
if (unlikely(!skb2))
goto err_free;
skb = skb2;
if (unlikely(!pskb_may_pull(skb, HSR_HLEN)))
goto err_free;
hsr_tag = (struct hsr_tag *) skb->data;
skb->protocol = hsr_tag->encap_proto;
skb_pull(skb, HSR_HLEN);
return skb;
err_free:
kfree_skb(skb);
return NULL;
}
/* The uses I can see for these HSR supervision frames are:
* 1) Use the frames that are sent after node initialization ("HSR_TLV.Type =
* 22") to reset any sequence_nr counters belonging to that node. Useful if
* the other node's counter has been reset for some reason.
* --
* Or not - resetting the counter and bridging the frame would create a
* loop, unfortunately.
*
* 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck
* frame is received from a particular node, we know something is wrong.
* We just register these (as with normal frames) and throw them away.
*
* 3) Allow different MAC addresses for the two slave interfaces, using the
* MacAddressA field.
*/
static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb)
{
struct hsr_sup_tag *hsr_stag;
if (!ether_addr_equal(eth_hdr(skb)->h_dest,
hsr->sup_multicast_addr))
return false;
hsr_stag = (struct hsr_sup_tag *) skb->data;
if (get_hsr_stag_path(hsr_stag) != 0x0f)
return false;
if ((hsr_stag->HSR_TLV_Type != HSR_TLV_ANNOUNCE) &&
(hsr_stag->HSR_TLV_Type != HSR_TLV_LIFE_CHECK))
return false;
if (hsr_stag->HSR_TLV_Length != 12)
return false;
return true;
}
/* Implementation somewhat according to IEC-62439-3, p. 43
*/
rx_handler_result_t hsr_handle_frame(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct hsr_port *port, *other_port, *master;
struct hsr_priv *hsr;
struct hsr_node *node;
bool deliver_to_self;
struct sk_buff *skb_deliver;
bool dup_out;
int ret;
if (eth_hdr(skb)->h_proto != htons(ETH_P_PRP))
return RX_HANDLER_PASS;
rcu_read_lock(); /* ports & node */
port = hsr_port_get_rcu(skb->dev);
hsr = port->hsr;
master = hsr_port_get_hsr(hsr, HSR_PT_MASTER);
node = hsr_find_node(&hsr->self_node_db, skb);
if (node) {
/* Always kill frames sent by ourselves */
kfree_skb(skb);
ret = RX_HANDLER_CONSUMED;
goto finish;
}
/* Is this frame a candidate for local reception? */
deliver_to_self = false;
if ((skb->pkt_type == PACKET_HOST) ||
(skb->pkt_type == PACKET_MULTICAST) ||
(skb->pkt_type == PACKET_BROADCAST))
deliver_to_self = true;
else if (ether_addr_equal(eth_hdr(skb)->h_dest,
master->dev->dev_addr)) {
skb->pkt_type = PACKET_HOST;
deliver_to_self = true;
}
node = hsr_find_node(&hsr->node_db, skb);
if (is_supervision_frame(hsr, skb)) {
skb_pull(skb, sizeof(struct hsr_sup_tag));
node = hsr_merge_node(node, skb, port);
if (!node) {
kfree_skb(skb);
master->dev->stats.rx_dropped++;
ret = RX_HANDLER_CONSUMED;
goto finish;
}
skb_push(skb, sizeof(struct hsr_sup_tag));
deliver_to_self = false;
}
if (!node) {
/* Source node unknown; this might be a HSR frame from
* another net (different multicast address). Ignore it.
*/
kfree_skb(skb);
ret = RX_HANDLER_CONSUMED;
goto finish;
}
if (port->type == HSR_PT_SLAVE_A)
other_port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
else
other_port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_A);
/* Register ALL incoming frames as outgoing through the other interface.
* This allows us to register frames as incoming only if they are valid
* for the receiving interface, without using a specific counter for
* incoming frames.
*/
if (other_port)
dup_out = hsr_register_frame_out(node, other_port, skb);
else
dup_out = 0;
if (!dup_out)
hsr_register_frame_in(node, port);
/* Forward this frame? */
if (dup_out || (skb->pkt_type == PACKET_HOST))
other_port = NULL;
if (hsr_register_frame_out(node, master, skb))
deliver_to_self = false;
if (!deliver_to_self && !other_port) {
kfree_skb(skb);
/* Circulated frame; silently remove it. */
ret = RX_HANDLER_CONSUMED;
goto finish;
}
skb_deliver = skb;
if (deliver_to_self && other_port) {
/* skb_clone() is not enough since we will strip the hsr tag
* and do address substitution below
*/
skb_deliver = pskb_copy(skb, GFP_ATOMIC);
if (!skb_deliver) {
deliver_to_self = false;
master->dev->stats.rx_dropped++;
}
}
if (deliver_to_self) {
bool multicast_frame;
skb_deliver = hsr_pull_tag(skb_deliver);
if (!skb_deliver) {
master->dev->stats.rx_dropped++;
goto forward;
}
#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
/* Move everything in the header that is after the HSR tag,
* to work around alignment problems caused by the 6-byte HSR
* tag. In practice, this removes/overwrites the HSR tag in
* the header and restores a "standard" packet.
*/
memmove(skb_deliver->data - HSR_HLEN, skb_deliver->data,
skb_headlen(skb_deliver));
/* Adjust skb members so they correspond with the move above.
* This cannot possibly underflow skb->data since hsr_pull_tag()
* above succeeded.
* At this point in the protocol stack, the transport and