Commit 3f5a4aa1 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'hsr'

Sebastian Andrzej Siewior says:

====================
I started playing with HSR and run into a problem. Tested latest
upstream -rc and noticed more problems. Now it appears to work.
For testing I have a small three node setup with iperf and ping. While
iperf doesn't complain ping reports missing packets and duplicates.
====================

Link: https://lore.kernel.org/r/20221129164815.128922-1-bigeasy@linutronix.de/Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 7d802c80 7d0455e9
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/jhash.h>
#include "hsr_main.h" #include "hsr_main.h"
#include "hsr_framereg.h" #include "hsr_framereg.h"
...@@ -21,7 +20,6 @@ hsr_node_table_show(struct seq_file *sfp, void *data) ...@@ -21,7 +20,6 @@ hsr_node_table_show(struct seq_file *sfp, void *data)
{ {
struct hsr_priv *priv = (struct hsr_priv *)sfp->private; struct hsr_priv *priv = (struct hsr_priv *)sfp->private;
struct hsr_node *node; struct hsr_node *node;
int i;
seq_printf(sfp, "Node Table entries for (%s) device\n", seq_printf(sfp, "Node Table entries for (%s) device\n",
(priv->prot_version == PRP_V1 ? "PRP" : "HSR")); (priv->prot_version == PRP_V1 ? "PRP" : "HSR"));
...@@ -33,28 +31,22 @@ hsr_node_table_show(struct seq_file *sfp, void *data) ...@@ -33,28 +31,22 @@ hsr_node_table_show(struct seq_file *sfp, void *data)
seq_puts(sfp, "DAN-H\n"); seq_puts(sfp, "DAN-H\n");
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(node, &priv->node_db, mac_list) {
for (i = 0 ; i < priv->hash_buckets; i++) { /* skip self node */
hlist_for_each_entry_rcu(node, &priv->node_db[i], mac_list) { if (hsr_addr_is_self(priv, node->macaddress_A))
/* skip self node */ continue;
if (hsr_addr_is_self(priv, node->macaddress_A)) seq_printf(sfp, "%pM ", &node->macaddress_A[0]);
continue; seq_printf(sfp, "%pM ", &node->macaddress_B[0]);
seq_printf(sfp, "%pM ", &node->macaddress_A[0]); seq_printf(sfp, "%10lx, ", node->time_in[HSR_PT_SLAVE_A]);
seq_printf(sfp, "%pM ", &node->macaddress_B[0]); seq_printf(sfp, "%10lx, ", node->time_in[HSR_PT_SLAVE_B]);
seq_printf(sfp, "%10lx, ", seq_printf(sfp, "%14x, ", node->addr_B_port);
node->time_in[HSR_PT_SLAVE_A]);
seq_printf(sfp, "%10lx, ", if (priv->prot_version == PRP_V1)
node->time_in[HSR_PT_SLAVE_B]); seq_printf(sfp, "%5x, %5x, %5x\n",
seq_printf(sfp, "%14x, ", node->addr_B_port); node->san_a, node->san_b,
(node->san_a == 0 && node->san_b == 0));
if (priv->prot_version == PRP_V1) else
seq_printf(sfp, "%5x, %5x, %5x\n", seq_printf(sfp, "%5x\n", 1);
node->san_a, node->san_b,
(node->san_a == 0 &&
node->san_b == 0));
else
seq_printf(sfp, "%5x\n", 1);
}
} }
rcu_read_unlock(); rcu_read_unlock();
return 0; return 0;
......
...@@ -219,7 +219,9 @@ static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -219,7 +219,9 @@ static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
skb->dev = master->dev; skb->dev = master->dev;
skb_reset_mac_header(skb); skb_reset_mac_header(skb);
skb_reset_mac_len(skb); skb_reset_mac_len(skb);
spin_lock_bh(&hsr->seqnr_lock);
hsr_forward_skb(skb, master); hsr_forward_skb(skb, master);
spin_unlock_bh(&hsr->seqnr_lock);
} else { } else {
dev_core_stats_tx_dropped_inc(dev); dev_core_stats_tx_dropped_inc(dev);
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
...@@ -278,7 +280,6 @@ static void send_hsr_supervision_frame(struct hsr_port *master, ...@@ -278,7 +280,6 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
__u8 type = HSR_TLV_LIFE_CHECK; __u8 type = HSR_TLV_LIFE_CHECK;
struct hsr_sup_payload *hsr_sp; struct hsr_sup_payload *hsr_sp;
struct hsr_sup_tag *hsr_stag; struct hsr_sup_tag *hsr_stag;
unsigned long irqflags;
struct sk_buff *skb; struct sk_buff *skb;
*interval = msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL); *interval = msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL);
...@@ -299,7 +300,7 @@ static void send_hsr_supervision_frame(struct hsr_port *master, ...@@ -299,7 +300,7 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
set_hsr_stag_HSR_ver(hsr_stag, hsr->prot_version); set_hsr_stag_HSR_ver(hsr_stag, hsr->prot_version);
/* From HSRv1 on we have separate supervision sequence numbers. */ /* From HSRv1 on we have separate supervision sequence numbers. */
spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags); spin_lock_bh(&hsr->seqnr_lock);
if (hsr->prot_version > 0) { if (hsr->prot_version > 0) {
hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr); hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr);
hsr->sup_sequence_nr++; hsr->sup_sequence_nr++;
...@@ -307,7 +308,6 @@ static void send_hsr_supervision_frame(struct hsr_port *master, ...@@ -307,7 +308,6 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
hsr_stag->sequence_nr = htons(hsr->sequence_nr); hsr_stag->sequence_nr = htons(hsr->sequence_nr);
hsr->sequence_nr++; hsr->sequence_nr++;
} }
spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);
hsr_stag->tlv.HSR_TLV_type = type; hsr_stag->tlv.HSR_TLV_type = type;
/* TODO: Why 12 in HSRv0? */ /* TODO: Why 12 in HSRv0? */
...@@ -318,11 +318,13 @@ static void send_hsr_supervision_frame(struct hsr_port *master, ...@@ -318,11 +318,13 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload)); hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload));
ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr); ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr);
if (skb_put_padto(skb, ETH_ZLEN)) if (skb_put_padto(skb, ETH_ZLEN)) {
spin_unlock_bh(&hsr->seqnr_lock);
return; return;
}
hsr_forward_skb(skb, master); hsr_forward_skb(skb, master);
spin_unlock_bh(&hsr->seqnr_lock);
return; return;
} }
...@@ -332,7 +334,6 @@ static void send_prp_supervision_frame(struct hsr_port *master, ...@@ -332,7 +334,6 @@ static void send_prp_supervision_frame(struct hsr_port *master,
struct hsr_priv *hsr = master->hsr; struct hsr_priv *hsr = master->hsr;
struct hsr_sup_payload *hsr_sp; struct hsr_sup_payload *hsr_sp;
struct hsr_sup_tag *hsr_stag; struct hsr_sup_tag *hsr_stag;
unsigned long irqflags;
struct sk_buff *skb; struct sk_buff *skb;
skb = hsr_init_skb(master); skb = hsr_init_skb(master);
...@@ -347,7 +348,7 @@ static void send_prp_supervision_frame(struct hsr_port *master, ...@@ -347,7 +348,7 @@ static void send_prp_supervision_frame(struct hsr_port *master,
set_hsr_stag_HSR_ver(hsr_stag, (hsr->prot_version ? 1 : 0)); set_hsr_stag_HSR_ver(hsr_stag, (hsr->prot_version ? 1 : 0));
/* From HSRv1 on we have separate supervision sequence numbers. */ /* From HSRv1 on we have separate supervision sequence numbers. */
spin_lock_irqsave(&master->hsr->seqnr_lock, irqflags); spin_lock_bh(&hsr->seqnr_lock);
hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr); hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr);
hsr->sup_sequence_nr++; hsr->sup_sequence_nr++;
hsr_stag->tlv.HSR_TLV_type = PRP_TLV_LIFE_CHECK_DD; hsr_stag->tlv.HSR_TLV_type = PRP_TLV_LIFE_CHECK_DD;
...@@ -358,13 +359,12 @@ static void send_prp_supervision_frame(struct hsr_port *master, ...@@ -358,13 +359,12 @@ static void send_prp_supervision_frame(struct hsr_port *master,
ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr); ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr);
if (skb_put_padto(skb, ETH_ZLEN)) { if (skb_put_padto(skb, ETH_ZLEN)) {
spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags); spin_unlock_bh(&hsr->seqnr_lock);
return; return;
} }
spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);
hsr_forward_skb(skb, master); hsr_forward_skb(skb, master);
spin_unlock_bh(&hsr->seqnr_lock);
} }
/* Announce (supervision frame) timer function /* Announce (supervision frame) timer function
...@@ -444,7 +444,7 @@ void hsr_dev_setup(struct net_device *dev) ...@@ -444,7 +444,7 @@ void hsr_dev_setup(struct net_device *dev)
dev->header_ops = &hsr_header_ops; dev->header_ops = &hsr_header_ops;
dev->netdev_ops = &hsr_device_ops; dev->netdev_ops = &hsr_device_ops;
SET_NETDEV_DEVTYPE(dev, &hsr_type); SET_NETDEV_DEVTYPE(dev, &hsr_type);
dev->priv_flags |= IFF_NO_QUEUE; dev->priv_flags |= IFF_NO_QUEUE | IFF_DISABLE_NETPOLL;
dev->needs_free_netdev = true; dev->needs_free_netdev = true;
...@@ -485,16 +485,11 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], ...@@ -485,16 +485,11 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
{ {
bool unregister = false; bool unregister = false;
struct hsr_priv *hsr; struct hsr_priv *hsr;
int res, i; int res;
hsr = netdev_priv(hsr_dev); hsr = netdev_priv(hsr_dev);
INIT_LIST_HEAD(&hsr->ports); INIT_LIST_HEAD(&hsr->ports);
INIT_HLIST_HEAD(&hsr->self_node_db); INIT_LIST_HEAD(&hsr->node_db);
hsr->hash_buckets = HSR_HSIZE;
get_random_bytes(&hsr->hash_seed, sizeof(hsr->hash_seed));
for (i = 0; i < hsr->hash_buckets; i++)
INIT_HLIST_HEAD(&hsr->node_db[i]);
spin_lock_init(&hsr->list_lock); spin_lock_init(&hsr->list_lock);
eth_hw_addr_set(hsr_dev, slave[0]->dev_addr); eth_hw_addr_set(hsr_dev, slave[0]->dev_addr);
......
...@@ -500,7 +500,6 @@ static void handle_std_frame(struct sk_buff *skb, ...@@ -500,7 +500,6 @@ static void handle_std_frame(struct sk_buff *skb,
{ {
struct hsr_port *port = frame->port_rcv; struct hsr_port *port = frame->port_rcv;
struct hsr_priv *hsr = port->hsr; struct hsr_priv *hsr = port->hsr;
unsigned long irqflags;
frame->skb_hsr = NULL; frame->skb_hsr = NULL;
frame->skb_prp = NULL; frame->skb_prp = NULL;
...@@ -510,10 +509,9 @@ static void handle_std_frame(struct sk_buff *skb, ...@@ -510,10 +509,9 @@ static void handle_std_frame(struct sk_buff *skb,
frame->is_from_san = true; frame->is_from_san = true;
} else { } else {
/* Sequence nr for the master node */ /* Sequence nr for the master node */
spin_lock_irqsave(&hsr->seqnr_lock, irqflags); lockdep_assert_held(&hsr->seqnr_lock);
frame->sequence_nr = hsr->sequence_nr; frame->sequence_nr = hsr->sequence_nr;
hsr->sequence_nr++; hsr->sequence_nr++;
spin_unlock_irqrestore(&hsr->seqnr_lock, irqflags);
} }
} }
...@@ -571,23 +569,20 @@ static int fill_frame_info(struct hsr_frame_info *frame, ...@@ -571,23 +569,20 @@ static int fill_frame_info(struct hsr_frame_info *frame,
struct ethhdr *ethhdr; struct ethhdr *ethhdr;
__be16 proto; __be16 proto;
int ret; int ret;
u32 hash;
/* Check if skb contains ethhdr */ /* Check if skb contains ethhdr */
if (skb->mac_len < sizeof(struct ethhdr)) if (skb->mac_len < sizeof(struct ethhdr))
return -EINVAL; return -EINVAL;
memset(frame, 0, sizeof(*frame)); memset(frame, 0, sizeof(*frame));
ethhdr = (struct ethhdr *)skb_mac_header(skb);
hash = hsr_mac_hash(port->hsr, ethhdr->h_source);
frame->is_supervision = is_supervision_frame(port->hsr, skb); frame->is_supervision = is_supervision_frame(port->hsr, skb);
frame->node_src = hsr_get_node(port, &hsr->node_db[hash], skb, frame->node_src = hsr_get_node(port, &hsr->node_db, skb,
frame->is_supervision, frame->is_supervision,
port->type); port->type);
if (!frame->node_src) if (!frame->node_src)
return -1; /* Unknown node and !is_supervision, or no mem */ return -1; /* Unknown node and !is_supervision, or no mem */
ethhdr = (struct ethhdr *)skb_mac_header(skb);
frame->is_vlan = false; frame->is_vlan = false;
proto = ethhdr->h_proto; proto = ethhdr->h_proto;
...@@ -617,11 +612,13 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port) ...@@ -617,11 +612,13 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)
{ {
struct hsr_frame_info frame; struct hsr_frame_info frame;
rcu_read_lock();
if (fill_frame_info(&frame, skb, port) < 0) if (fill_frame_info(&frame, skb, port) < 0)
goto out_drop; goto out_drop;
hsr_register_frame_in(frame.node_src, port, frame.sequence_nr); hsr_register_frame_in(frame.node_src, port, frame.sequence_nr);
hsr_forward_do(&frame); hsr_forward_do(&frame);
rcu_read_unlock();
/* Gets called for ingress frames as well as egress from master port. /* Gets called for ingress frames as well as egress from master port.
* So check and increment stats for master port only here. * So check and increment stats for master port only here.
*/ */
...@@ -636,6 +633,7 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port) ...@@ -636,6 +633,7 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)
return; return;
out_drop: out_drop:
rcu_read_unlock();
port->dev->stats.tx_dropped++; port->dev->stats.tx_dropped++;
kfree_skb(skb); kfree_skb(skb);
} }
...@@ -15,37 +15,10 @@ ...@@ -15,37 +15,10 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/rculist.h> #include <linux/rculist.h>
#include <linux/jhash.h>
#include "hsr_main.h" #include "hsr_main.h"
#include "hsr_framereg.h" #include "hsr_framereg.h"
#include "hsr_netlink.h" #include "hsr_netlink.h"
#ifdef CONFIG_LOCKDEP
int lockdep_hsr_is_held(spinlock_t *lock)
{
return lockdep_is_held(lock);
}
#endif
u32 hsr_mac_hash(struct hsr_priv *hsr, const unsigned char *addr)
{
u32 hash = jhash(addr, ETH_ALEN, hsr->hash_seed);
return reciprocal_scale(hash, hsr->hash_buckets);
}
struct hsr_node *hsr_node_get_first(struct hlist_head *head, spinlock_t *lock)
{
struct hlist_node *first;
first = rcu_dereference_bh_check(hlist_first_rcu(head),
lockdep_hsr_is_held(lock));
if (first)
return hlist_entry(first, struct hsr_node, mac_list);
return NULL;
}
/* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b, /* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b,
* false otherwise. * false otherwise.
*/ */
...@@ -65,30 +38,32 @@ static bool seq_nr_after(u16 a, u16 b) ...@@ -65,30 +38,32 @@ static bool seq_nr_after(u16 a, u16 b)
bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr) bool hsr_addr_is_self(struct hsr_priv *hsr, unsigned char *addr)
{ {
struct hsr_node *node; struct hsr_self_node *sn;
bool ret = false;
node = hsr_node_get_first(&hsr->self_node_db, &hsr->list_lock); rcu_read_lock();
if (!node) { sn = rcu_dereference(hsr->self_node);
if (!sn) {
WARN_ONCE(1, "HSR: No self node\n"); WARN_ONCE(1, "HSR: No self node\n");
return false; goto out;
} }
if (ether_addr_equal(addr, node->macaddress_A)) if (ether_addr_equal(addr, sn->macaddress_A) ||
return true; ether_addr_equal(addr, sn->macaddress_B))
if (ether_addr_equal(addr, node->macaddress_B)) ret = true;
return true; out:
rcu_read_unlock();
return false; return ret;
} }
/* Search for mac entry. Caller must hold rcu read lock. /* Search for mac entry. Caller must hold rcu read lock.
*/ */
static struct hsr_node *find_node_by_addr_A(struct hlist_head *node_db, static struct hsr_node *find_node_by_addr_A(struct list_head *node_db,
const unsigned char addr[ETH_ALEN]) const unsigned char addr[ETH_ALEN])
{ {
struct hsr_node *node; struct hsr_node *node;
hlist_for_each_entry_rcu(node, node_db, mac_list) { list_for_each_entry_rcu(node, node_db, mac_list) {
if (ether_addr_equal(node->macaddress_A, addr)) if (ether_addr_equal(node->macaddress_A, addr))
return node; return node;
} }
...@@ -96,58 +71,51 @@ static struct hsr_node *find_node_by_addr_A(struct hlist_head *node_db, ...@@ -96,58 +71,51 @@ static struct hsr_node *find_node_by_addr_A(struct hlist_head *node_db,
return NULL; return NULL;
} }
/* Helper for device init; the self_node_db is used in hsr_rcv() to recognize /* Helper for device init; the self_node is used in hsr_rcv() to recognize
* frames from self that's been looped over the HSR ring. * frames from self that's been looped over the HSR ring.
*/ */
int hsr_create_self_node(struct hsr_priv *hsr, int hsr_create_self_node(struct hsr_priv *hsr,
const unsigned char addr_a[ETH_ALEN], const unsigned char addr_a[ETH_ALEN],
const unsigned char addr_b[ETH_ALEN]) const unsigned char addr_b[ETH_ALEN])
{ {
struct hlist_head *self_node_db = &hsr->self_node_db; struct hsr_self_node *sn, *old;
struct hsr_node *node, *oldnode;
node = kmalloc(sizeof(*node), GFP_KERNEL); sn = kmalloc(sizeof(*sn), GFP_KERNEL);
if (!node) if (!sn)
return -ENOMEM; return -ENOMEM;
ether_addr_copy(node->macaddress_A, addr_a); ether_addr_copy(sn->macaddress_A, addr_a);
ether_addr_copy(node->macaddress_B, addr_b); ether_addr_copy(sn->macaddress_B, addr_b);
spin_lock_bh(&hsr->list_lock); spin_lock_bh(&hsr->list_lock);
oldnode = hsr_node_get_first(self_node_db, &hsr->list_lock); old = rcu_replace_pointer(hsr->self_node, sn,
if (oldnode) { lockdep_is_held(&hsr->list_lock));
hlist_replace_rcu(&oldnode->mac_list, &node->mac_list); spin_unlock_bh(&hsr->list_lock);
spin_unlock_bh(&hsr->list_lock);
kfree_rcu(oldnode, rcu_head);
} else {
hlist_add_tail_rcu(&node->mac_list, self_node_db);
spin_unlock_bh(&hsr->list_lock);
}
if (old)
kfree_rcu(old, rcu_head);
return 0; return 0;
} }
void hsr_del_self_node(struct hsr_priv *hsr) void hsr_del_self_node(struct hsr_priv *hsr)
{ {
struct hlist_head *self_node_db = &hsr->self_node_db; struct hsr_self_node *old;
struct hsr_node *node;
spin_lock_bh(&hsr->list_lock); spin_lock_bh(&hsr->list_lock);
node = hsr_node_get_first(self_node_db, &hsr->list_lock); old = rcu_replace_pointer(hsr->self_node, NULL,
if (node) { lockdep_is_held(&hsr->list_lock));
hlist_del_rcu(&node->mac_list);
kfree_rcu(node, rcu_head);
}
spin_unlock_bh(&hsr->list_lock); spin_unlock_bh(&hsr->list_lock);
if (old)
kfree_rcu(old, rcu_head);
} }
void hsr_del_nodes(struct hlist_head *node_db) void hsr_del_nodes(struct list_head *node_db)
{ {
struct hsr_node *node; struct hsr_node *node;
struct hlist_node *tmp; struct hsr_node *tmp;
hlist_for_each_entry_safe(node, tmp, node_db, mac_list) list_for_each_entry_safe(node, tmp, node_db, mac_list)
kfree_rcu(node, rcu_head); kfree(node);
} }
void prp_handle_san_frame(bool san, enum hsr_port_type port, void prp_handle_san_frame(bool san, enum hsr_port_type port,
...@@ -168,7 +136,7 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port, ...@@ -168,7 +136,7 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port,
* originating from the newly added node. * originating from the newly added node.
*/ */
static struct hsr_node *hsr_add_node(struct hsr_priv *hsr, static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
struct hlist_head *node_db, struct list_head *node_db,
unsigned char addr[], unsigned char addr[],
u16 seq_out, bool san, u16 seq_out, bool san,
enum hsr_port_type rx_port) enum hsr_port_type rx_port)
...@@ -182,6 +150,7 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr, ...@@ -182,6 +150,7 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
return NULL; return NULL;
ether_addr_copy(new_node->macaddress_A, addr); ether_addr_copy(new_node->macaddress_A, addr);
spin_lock_init(&new_node->seq_out_lock);
/* We are only interested in time diffs here, so use current jiffies /* We are only interested in time diffs here, so use current jiffies
* as initialization. (0 could trigger an spurious ring error warning). * as initialization. (0 could trigger an spurious ring error warning).
...@@ -198,14 +167,14 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr, ...@@ -198,14 +167,14 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
hsr->proto_ops->handle_san_frame(san, rx_port, new_node); hsr->proto_ops->handle_san_frame(san, rx_port, new_node);
spin_lock_bh(&hsr->list_lock); spin_lock_bh(&hsr->list_lock);
hlist_for_each_entry_rcu(node, node_db, mac_list, list_for_each_entry_rcu(node, node_db, mac_list,
lockdep_hsr_is_held(&hsr->list_lock)) { lockdep_is_held(&hsr->list_lock)) {
if (ether_addr_equal(node->macaddress_A, addr)) if (ether_addr_equal(node->macaddress_A, addr))
goto out; goto out;
if (ether_addr_equal(node->macaddress_B, addr)) if (ether_addr_equal(node->macaddress_B, addr))
goto out; goto out;
} }
hlist_add_tail_rcu(&new_node->mac_list, node_db); list_add_tail_rcu(&new_node->mac_list, node_db);
spin_unlock_bh(&hsr->list_lock); spin_unlock_bh(&hsr->list_lock);
return new_node; return new_node;
out: out:
...@@ -225,7 +194,7 @@ void prp_update_san_info(struct hsr_node *node, bool is_sup) ...@@ -225,7 +194,7 @@ void prp_update_san_info(struct hsr_node *node, bool is_sup)
/* Get the hsr_node from which 'skb' was sent. /* Get the hsr_node from which 'skb' was sent.
*/ */
struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db, struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
struct sk_buff *skb, bool is_sup, struct sk_buff *skb, bool is_sup,
enum hsr_port_type rx_port) enum hsr_port_type rx_port)
{ {
...@@ -241,7 +210,7 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db, ...@@ -241,7 +210,7 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db,
ethhdr = (struct ethhdr *)skb_mac_header(skb); ethhdr = (struct ethhdr *)skb_mac_header(skb);
hlist_for_each_entry_rcu(node, node_db, mac_list) { list_for_each_entry_rcu(node, node_db, mac_list) {
if (ether_addr_equal(node->macaddress_A, ethhdr->h_source)) { if (ether_addr_equal(node->macaddress_A, ethhdr->h_source)) {
if (hsr->proto_ops->update_san_info) if (hsr->proto_ops->update_san_info)
hsr->proto_ops->update_san_info(node, is_sup); hsr->proto_ops->update_san_info(node, is_sup);
...@@ -291,12 +260,11 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) ...@@ -291,12 +260,11 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
struct hsr_sup_tlv *hsr_sup_tlv; struct hsr_sup_tlv *hsr_sup_tlv;
struct hsr_node *node_real; struct hsr_node *node_real;
struct sk_buff *skb = NULL; struct sk_buff *skb = NULL;
struct hlist_head *node_db; struct list_head *node_db;
struct ethhdr *ethhdr; struct ethhdr *ethhdr;
int i; int i;
unsigned int pull_size = 0; unsigned int pull_size = 0;
unsigned int total_pull_size = 0; unsigned int total_pull_size = 0;
u32 hash;
/* Here either frame->skb_hsr or frame->skb_prp should be /* Here either frame->skb_hsr or frame->skb_prp should be
* valid as supervision frame always will have protocol * valid as supervision frame always will have protocol
...@@ -334,13 +302,11 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) ...@@ -334,13 +302,11 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
hsr_sp = (struct hsr_sup_payload *)skb->data; hsr_sp = (struct hsr_sup_payload *)skb->data;
/* Merge node_curr (registered on macaddress_B) into node_real */ /* Merge node_curr (registered on macaddress_B) into node_real */
node_db = port_rcv->hsr->node_db; node_db = &port_rcv->hsr->node_db;
hash = hsr_mac_hash(hsr, hsr_sp->macaddress_A); node_real = find_node_by_addr_A(node_db, hsr_sp->macaddress_A);
node_real = find_node_by_addr_A(&node_db[hash], hsr_sp->macaddress_A);
if (!node_real) if (!node_real)
/* No frame received from AddrA of this node yet */ /* No frame received from AddrA of this node yet */
node_real = hsr_add_node(hsr, &node_db[hash], node_real = hsr_add_node(hsr, node_db, hsr_sp->macaddress_A,
hsr_sp->macaddress_A,
HSR_SEQNR_START - 1, true, HSR_SEQNR_START - 1, true,
port_rcv->type); port_rcv->type);
if (!node_real) if (!node_real)
...@@ -374,14 +340,14 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) ...@@ -374,14 +340,14 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
hsr_sp = (struct hsr_sup_payload *)skb->data; hsr_sp = (struct hsr_sup_payload *)skb->data;
/* Check if redbox mac and node mac are equal. */ /* Check if redbox mac and node mac are equal. */
if (!ether_addr_equal(node_real->macaddress_A, if (!ether_addr_equal(node_real->macaddress_A, hsr_sp->macaddress_A)) {
hsr_sp->macaddress_A)) {
/* This is a redbox supervision frame for a VDAN! */ /* This is a redbox supervision frame for a VDAN! */
goto done; goto done;
} }
} }
ether_addr_copy(node_real->macaddress_B, ethhdr->h_source); ether_addr_copy(node_real->macaddress_B, ethhdr->h_source);
spin_lock_bh(&node_real->seq_out_lock);
for (i = 0; i < HSR_PT_PORTS; i++) { for (i = 0; i < HSR_PT_PORTS; i++) {
if (!node_curr->time_in_stale[i] && if (!node_curr->time_in_stale[i] &&
time_after(node_curr->time_in[i], node_real->time_in[i])) { time_after(node_curr->time_in[i], node_real->time_in[i])) {
...@@ -392,12 +358,16 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) ...@@ -392,12 +358,16 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
if (seq_nr_after(node_curr->seq_out[i], node_real->seq_out[i])) if (seq_nr_after(node_curr->seq_out[i], node_real->seq_out[i]))
node_real->seq_out[i] = node_curr->seq_out[i]; node_real->seq_out[i] = node_curr->seq_out[i];
} }
spin_unlock_bh(&node_real->seq_out_lock);
node_real->addr_B_port = port_rcv->type; node_real->addr_B_port = port_rcv->type;
spin_lock_bh(&hsr->list_lock); spin_lock_bh(&hsr->list_lock);
hlist_del_rcu(&node_curr->mac_list); if (!node_curr->removed) {
list_del_rcu(&node_curr->mac_list);
node_curr->removed = true;
kfree_rcu(node_curr, rcu_head);
}
spin_unlock_bh(&hsr->list_lock); spin_unlock_bh(&hsr->list_lock);
kfree_rcu(node_curr, rcu_head);
done: done:
/* Push back here */ /* Push back here */
...@@ -433,7 +403,6 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb, ...@@ -433,7 +403,6 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
struct hsr_port *port) struct hsr_port *port)
{ {
struct hsr_node *node_dst; struct hsr_node *node_dst;
u32 hash;
if (!skb_mac_header_was_set(skb)) { if (!skb_mac_header_was_set(skb)) {
WARN_ONCE(1, "%s: Mac header not set\n", __func__); WARN_ONCE(1, "%s: Mac header not set\n", __func__);
...@@ -443,8 +412,7 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb, ...@@ -443,8 +412,7 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
if (!is_unicast_ether_addr(eth_hdr(skb)->h_dest)) if (!is_unicast_ether_addr(eth_hdr(skb)->h_dest))
return; return;
hash = hsr_mac_hash(port->hsr, eth_hdr(skb)->h_dest); node_dst = find_node_by_addr_A(&port->hsr->node_db,
node_dst = find_node_by_addr_A(&port->hsr->node_db[hash],
eth_hdr(skb)->h_dest); eth_hdr(skb)->h_dest);
if (!node_dst) { if (!node_dst) {
if (net_ratelimit()) if (net_ratelimit())
...@@ -484,13 +452,17 @@ void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port, ...@@ -484,13 +452,17 @@ void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port,
int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node, int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node,
u16 sequence_nr) u16 sequence_nr)
{ {
spin_lock_bh(&node->seq_out_lock);
if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type]) && if (seq_nr_before_or_eq(sequence_nr, node->seq_out[port->type]) &&
time_is_after_jiffies(node->time_out[port->type] + time_is_after_jiffies(node->time_out[port->type] +
msecs_to_jiffies(HSR_ENTRY_FORGET_TIME))) msecs_to_jiffies(HSR_ENTRY_FORGET_TIME))) {
spin_unlock_bh(&node->seq_out_lock);
return 1; return 1;
}
node->time_out[port->type] = jiffies; node->time_out[port->type] = jiffies;
node->seq_out[port->type] = sequence_nr; node->seq_out[port->type] = sequence_nr;
spin_unlock_bh(&node->seq_out_lock);
return 0; return 0;
} }
...@@ -520,71 +492,60 @@ static struct hsr_port *get_late_port(struct hsr_priv *hsr, ...@@ -520,71 +492,60 @@ static struct hsr_port *get_late_port(struct hsr_priv *hsr,
void hsr_prune_nodes(struct timer_list *t) void hsr_prune_nodes(struct timer_list *t)
{ {
struct hsr_priv *hsr = from_timer(hsr, t, prune_timer); struct hsr_priv *hsr = from_timer(hsr, t, prune_timer);
struct hlist_node *tmp;
struct hsr_node *node; struct hsr_node *node;
struct hsr_node *tmp;
struct hsr_port *port; struct hsr_port *port;
unsigned long timestamp; unsigned long timestamp;
unsigned long time_a, time_b; unsigned long time_a, time_b;
int i;
spin_lock_bh(&hsr->list_lock); spin_lock_bh(&hsr->list_lock);
list_for_each_entry_safe(node, tmp, &hsr->node_db, mac_list) {
/* Don't prune own node. Neither time_in[HSR_PT_SLAVE_A]
* nor time_in[HSR_PT_SLAVE_B], will ever be updated for
* the master port. Thus the master node will be repeatedly
* pruned leading to packet loss.
*/
if (hsr_addr_is_self(hsr, node->macaddress_A))
continue;
/* Shorthand */
time_a = node->time_in[HSR_PT_SLAVE_A];
time_b = node->time_in[HSR_PT_SLAVE_B];
/* Check for timestamps old enough to risk wrap-around */
if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET / 2))
node->time_in_stale[HSR_PT_SLAVE_A] = true;
if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET / 2))
node->time_in_stale[HSR_PT_SLAVE_B] = true;
/* Get age of newest frame from node.
* At least one time_in is OK here; nodes get pruned long
* before both time_ins can get stale
*/
timestamp = time_a;
if (node->time_in_stale[HSR_PT_SLAVE_A] ||
(!node->time_in_stale[HSR_PT_SLAVE_B] &&
time_after(time_b, time_a)))
timestamp = time_b;
/* Warn of ring error only as long as we get frames at all */
if (time_is_after_jiffies(timestamp +
msecs_to_jiffies(1.5 * MAX_SLAVE_DIFF))) {
rcu_read_lock();
port = get_late_port(hsr, node);
if (port)
hsr_nl_ringerror(hsr, node->macaddress_A, port);
rcu_read_unlock();
}
for (i = 0; i < hsr->hash_buckets; i++) { /* Prune old entries */
hlist_for_each_entry_safe(node, tmp, &hsr->node_db[i], if (time_is_before_jiffies(timestamp +
mac_list) { msecs_to_jiffies(HSR_NODE_FORGET_TIME))) {
/* Don't prune own node. hsr_nl_nodedown(hsr, node->macaddress_A);
* Neither time_in[HSR_PT_SLAVE_A] if (!node->removed) {
* nor time_in[HSR_PT_SLAVE_B], will ever be updated list_del_rcu(&node->mac_list);
* for the master port. Thus the master node will be node->removed = true;
* repeatedly pruned leading to packet loss. /* Note that we need to free this entry later: */
*/
if (hsr_addr_is_self(hsr, node->macaddress_A))
continue;
/* Shorthand */
time_a = node->time_in[HSR_PT_SLAVE_A];
time_b = node->time_in[HSR_PT_SLAVE_B];
/* Check for timestamps old enough to
* risk wrap-around
*/
if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET / 2))
node->time_in_stale[HSR_PT_SLAVE_A] = true;
if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET / 2))
node->time_in_stale[HSR_PT_SLAVE_B] = true;
/* Get age of newest frame from node.
* At least one time_in is OK here; nodes get pruned
* long before both time_ins can get stale
*/
timestamp = time_a;
if (node->time_in_stale[HSR_PT_SLAVE_A] ||
(!node->time_in_stale[HSR_PT_SLAVE_B] &&
time_after(time_b, time_a)))
timestamp = time_b;
/* Warn of ring error only as long as we get
* frames at all
*/
if (time_is_after_jiffies(timestamp +
msecs_to_jiffies(1.5 * MAX_SLAVE_DIFF))) {
rcu_read_lock();
port = get_late_port(hsr, node);
if (port)
hsr_nl_ringerror(hsr,
node->macaddress_A,
port);
rcu_read_unlock();
}
/* Prune old entries */
if (time_is_before_jiffies(timestamp +
msecs_to_jiffies(HSR_NODE_FORGET_TIME))) {
hsr_nl_nodedown(hsr, node->macaddress_A);
hlist_del_rcu(&node->mac_list);
/* Note that we need to free this
* entry later:
*/
kfree_rcu(node, rcu_head); kfree_rcu(node, rcu_head);
} }
} }
...@@ -600,20 +561,17 @@ void *hsr_get_next_node(struct hsr_priv *hsr, void *_pos, ...@@ -600,20 +561,17 @@ void *hsr_get_next_node(struct hsr_priv *hsr, void *_pos,
unsigned char addr[ETH_ALEN]) unsigned char addr[ETH_ALEN])
{ {
struct hsr_node *node; struct hsr_node *node;
u32 hash;
hash = hsr_mac_hash(hsr, addr);
if (!_pos) { if (!_pos) {
node = hsr_node_get_first(&hsr->node_db[hash], node = list_first_or_null_rcu(&hsr->node_db,
&hsr->list_lock); struct hsr_node, mac_list);
if (node) if (node)
ether_addr_copy(addr, node->macaddress_A); ether_addr_copy(addr, node->macaddress_A);
return node; return node;
} }
node = _pos; node = _pos;
hlist_for_each_entry_continue_rcu(node, mac_list) { list_for_each_entry_continue_rcu(node, &hsr->node_db, mac_list) {
ether_addr_copy(addr, node->macaddress_A); ether_addr_copy(addr, node->macaddress_A);
return node; return node;
} }
...@@ -633,11 +591,8 @@ int hsr_get_node_data(struct hsr_priv *hsr, ...@@ -633,11 +591,8 @@ int hsr_get_node_data(struct hsr_priv *hsr,
struct hsr_node *node; struct hsr_node *node;
struct hsr_port *port; struct hsr_port *port;
unsigned long tdiff; unsigned long tdiff;
u32 hash;
hash = hsr_mac_hash(hsr, addr);
node = find_node_by_addr_A(&hsr->node_db[hash], addr); node = find_node_by_addr_A(&hsr->node_db, addr);
if (!node) if (!node)
return -ENOENT; return -ENOENT;
......
...@@ -28,17 +28,9 @@ struct hsr_frame_info { ...@@ -28,17 +28,9 @@ struct hsr_frame_info {
bool is_from_san; bool is_from_san;
}; };
#ifdef CONFIG_LOCKDEP
int lockdep_hsr_is_held(spinlock_t *lock);
#else
#define lockdep_hsr_is_held(lock) 1
#endif
u32 hsr_mac_hash(struct hsr_priv *hsr, const unsigned char *addr);
struct hsr_node *hsr_node_get_first(struct hlist_head *head, spinlock_t *lock);
void hsr_del_self_node(struct hsr_priv *hsr); void hsr_del_self_node(struct hsr_priv *hsr);
void hsr_del_nodes(struct hlist_head *node_db); void hsr_del_nodes(struct list_head *node_db);
struct hsr_node *hsr_get_node(struct hsr_port *port, struct hlist_head *node_db, struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db,
struct sk_buff *skb, bool is_sup, struct sk_buff *skb, bool is_sup,
enum hsr_port_type rx_port); enum hsr_port_type rx_port);
void hsr_handle_sup_frame(struct hsr_frame_info *frame); void hsr_handle_sup_frame(struct hsr_frame_info *frame);
...@@ -76,7 +68,9 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port, ...@@ -76,7 +68,9 @@ void prp_handle_san_frame(bool san, enum hsr_port_type port,
void prp_update_san_info(struct hsr_node *node, bool is_sup); void prp_update_san_info(struct hsr_node *node, bool is_sup);
struct hsr_node { struct hsr_node {
struct hlist_node mac_list; struct list_head mac_list;
/* Protect R/W access to seq_out */
spinlock_t seq_out_lock;
unsigned char macaddress_A[ETH_ALEN]; unsigned char macaddress_A[ETH_ALEN];
unsigned char macaddress_B[ETH_ALEN]; unsigned char macaddress_B[ETH_ALEN];
/* Local slave through which AddrB frames are received from this node */ /* Local slave through which AddrB frames are received from this node */
...@@ -88,6 +82,7 @@ struct hsr_node { ...@@ -88,6 +82,7 @@ struct hsr_node {
bool san_a; bool san_a;
bool san_b; bool san_b;
u16 seq_out[HSR_PT_PORTS]; u16 seq_out[HSR_PT_PORTS];
bool removed;
struct rcu_head rcu_head; struct rcu_head rcu_head;
}; };
......
...@@ -47,9 +47,6 @@ ...@@ -47,9 +47,6 @@
#define HSR_V1_SUP_LSDUSIZE 52 #define HSR_V1_SUP_LSDUSIZE 52
#define HSR_HSIZE_SHIFT 8
#define HSR_HSIZE BIT(HSR_HSIZE_SHIFT)
/* The helper functions below assumes that 'path' occupies the 4 most /* The helper functions below assumes that 'path' occupies the 4 most
* significant bits of the 16-bit field shared by 'path' and 'LSDU_size' (or * significant bits of the 16-bit field shared by 'path' and 'LSDU_size' (or
* equivalently, the 4 most significant bits of HSR tag byte 14). * equivalently, the 4 most significant bits of HSR tag byte 14).
...@@ -185,11 +182,17 @@ struct hsr_proto_ops { ...@@ -185,11 +182,17 @@ struct hsr_proto_ops {
void (*update_san_info)(struct hsr_node *node, bool is_sup); void (*update_san_info)(struct hsr_node *node, bool is_sup);
}; };
struct hsr_self_node {
unsigned char macaddress_A[ETH_ALEN];
unsigned char macaddress_B[ETH_ALEN];
struct rcu_head rcu_head;
};
struct hsr_priv { struct hsr_priv {
struct rcu_head rcu_head; struct rcu_head rcu_head;
struct list_head ports; struct list_head ports;
struct hlist_head node_db[HSR_HSIZE]; /* Known HSR nodes */ struct list_head node_db; /* Known HSR nodes */
struct hlist_head self_node_db; /* MACs of slaves */ struct hsr_self_node __rcu *self_node; /* MACs of slaves */
struct timer_list announce_timer; /* Supervision frame dispatch */ struct timer_list announce_timer; /* Supervision frame dispatch */
struct timer_list prune_timer; struct timer_list prune_timer;
int announce_count; int announce_count;
...@@ -199,8 +202,6 @@ struct hsr_priv { ...@@ -199,8 +202,6 @@ struct hsr_priv {
spinlock_t seqnr_lock; /* locking for sequence_nr */ spinlock_t seqnr_lock; /* locking for sequence_nr */
spinlock_t list_lock; /* locking for node list */ spinlock_t list_lock; /* locking for node list */
struct hsr_proto_ops *proto_ops; struct hsr_proto_ops *proto_ops;
u32 hash_buckets;
u32 hash_seed;
#define PRP_LAN_ID 0x5 /* 0x1010 for A and 0x1011 for B. Bit 0 is set #define PRP_LAN_ID 0x5 /* 0x1010 for A and 0x1011 for B. Bit 0 is set
* based on SLAVE_A or SLAVE_B * based on SLAVE_A or SLAVE_B
*/ */
......
...@@ -105,7 +105,6 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev, ...@@ -105,7 +105,6 @@ static int hsr_newlink(struct net *src_net, struct net_device *dev,
static void hsr_dellink(struct net_device *dev, struct list_head *head) static void hsr_dellink(struct net_device *dev, struct list_head *head)
{ {
struct hsr_priv *hsr = netdev_priv(dev); struct hsr_priv *hsr = netdev_priv(dev);
int i;
del_timer_sync(&hsr->prune_timer); del_timer_sync(&hsr->prune_timer);
del_timer_sync(&hsr->announce_timer); del_timer_sync(&hsr->announce_timer);
...@@ -114,8 +113,7 @@ static void hsr_dellink(struct net_device *dev, struct list_head *head) ...@@ -114,8 +113,7 @@ static void hsr_dellink(struct net_device *dev, struct list_head *head)
hsr_del_ports(hsr); hsr_del_ports(hsr);
hsr_del_self_node(hsr); hsr_del_self_node(hsr);
for (i = 0; i < hsr->hash_buckets; i++) hsr_del_nodes(&hsr->node_db);
hsr_del_nodes(&hsr->node_db[i]);
unregister_netdevice_queue(dev, head); unregister_netdevice_queue(dev, head);
} }
......
...@@ -48,6 +48,7 @@ TARGETS += nci ...@@ -48,6 +48,7 @@ TARGETS += nci
TARGETS += net TARGETS += net
TARGETS += net/af_unix TARGETS += net/af_unix
TARGETS += net/forwarding TARGETS += net/forwarding
TARGETS += net/hsr
TARGETS += net/mptcp TARGETS += net/mptcp
TARGETS += net/openvswitch TARGETS += net/openvswitch
TARGETS += netfilter TARGETS += netfilter
......
# SPDX-License-Identifier: GPL-2.0
top_srcdir = ../../../../..
TEST_PROGS := hsr_ping.sh
include ../../lib.mk
CONFIG_IPV6=y
CONFIG_NET_SCH_NETEM=m
CONFIG_HSR=y
CONFIG_VETH=y
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
ret=0
ksft_skip=4
ipv6=true
optstring="h4"
usage() {
echo "Usage: $0 [OPTION]"
echo -e "\t-4: IPv4 only: disable IPv6 tests (default: test both IPv4 and IPv6)"
}
while getopts "$optstring" option;do
case "$option" in
"h")
usage $0
exit 0
;;
"4")
ipv6=false
;;
"?")
usage $0
exit 1
;;
esac
done
sec=$(date +%s)
rndh=$(printf %x $sec)-$(mktemp -u XXXXXX)
ns1="ns1-$rndh"
ns2="ns2-$rndh"
ns3="ns3-$rndh"
cleanup()
{
local netns
for netns in "$ns1" "$ns2" "$ns3" ;do
ip netns del $netns
done
}
ip -Version > /dev/null 2>&1
if [ $? -ne 0 ];then
echo "SKIP: Could not run test without ip tool"
exit $ksft_skip
fi
trap cleanup EXIT
for i in "$ns1" "$ns2" "$ns3" ;do
ip netns add $i || exit $ksft_skip
ip -net $i link set lo up
done
echo "INFO: preparing interfaces."
# Three HSR nodes. Each node has one link to each of its neighbour, two links in total.
#
# ns1eth1 ----- ns2eth1
# hsr1 hsr2
# ns1eth2 ns2eth2
# | |
# ns3eth1 ns3eth2
# \ /
# hsr3
#
# Interfaces
ip link add ns1eth1 netns "$ns1" type veth peer name ns2eth1 netns "$ns2"
ip link add ns1eth2 netns "$ns1" type veth peer name ns3eth1 netns "$ns3"
ip link add ns3eth2 netns "$ns3" type veth peer name ns2eth2 netns "$ns2"
# HSRv0.
ip -net "$ns1" link add name hsr1 type hsr slave1 ns1eth1 slave2 ns1eth2 supervision 45 version 0 proto 0
ip -net "$ns2" link add name hsr2 type hsr slave1 ns2eth1 slave2 ns2eth2 supervision 45 version 0 proto 0
ip -net "$ns3" link add name hsr3 type hsr slave1 ns3eth1 slave2 ns3eth2 supervision 45 version 0 proto 0
# IP for HSR
ip -net "$ns1" addr add 100.64.0.1/24 dev hsr1
ip -net "$ns1" addr add dead:beef:1::1/64 dev hsr1 nodad
ip -net "$ns2" addr add 100.64.0.2/24 dev hsr2
ip -net "$ns2" addr add dead:beef:1::2/64 dev hsr2 nodad
ip -net "$ns3" addr add 100.64.0.3/24 dev hsr3
ip -net "$ns3" addr add dead:beef:1::3/64 dev hsr3 nodad
# All Links up
ip -net "$ns1" link set ns1eth1 up
ip -net "$ns1" link set ns1eth2 up
ip -net "$ns1" link set hsr1 up
ip -net "$ns2" link set ns2eth1 up
ip -net "$ns2" link set ns2eth2 up
ip -net "$ns2" link set hsr2 up
ip -net "$ns3" link set ns3eth1 up
ip -net "$ns3" link set ns3eth2 up
ip -net "$ns3" link set hsr3 up
# $1: IP address
is_v6()
{
[ -z "${1##*:*}" ]
}
do_ping()
{
local netns="$1"
local connect_addr="$2"
local ping_args="-q -c 2"
if is_v6 "${connect_addr}"; then
$ipv6 || return 0
ping_args="${ping_args} -6"
fi
ip netns exec ${netns} ping ${ping_args} $connect_addr >/dev/null
if [ $? -ne 0 ] ; then
echo "$netns -> $connect_addr connectivity [ FAIL ]" 1>&2
ret=1
return 1
fi
return 0
}
do_ping_long()
{
local netns="$1"
local connect_addr="$2"
local ping_args="-q -c 10"
if is_v6 "${connect_addr}"; then
$ipv6 || return 0
ping_args="${ping_args} -6"
fi
OUT="$(LANG=C ip netns exec ${netns} ping ${ping_args} $connect_addr | grep received)"
if [ $? -ne 0 ] ; then
echo "$netns -> $connect_addr ping [ FAIL ]" 1>&2
ret=1
return 1
fi
VAL="$(echo $OUT | cut -d' ' -f1-8)"
if [ "$VAL" != "10 packets transmitted, 10 received, 0% packet loss," ]
then
echo "$netns -> $connect_addr ping TEST [ FAIL ]"
echo "Expect to send and receive 10 packets and no duplicates."
echo "Full message: ${OUT}."
ret=1
return 1
fi
return 0
}
stop_if_error()
{
local msg="$1"
if [ ${ret} -ne 0 ]; then
echo "FAIL: ${msg}" 1>&2
exit ${ret}
fi
}
echo "INFO: Initial validation ping."
# Each node has to be able each one.
do_ping "$ns1" 100.64.0.2
do_ping "$ns2" 100.64.0.1
do_ping "$ns3" 100.64.0.1
stop_if_error "Initial validation failed."
do_ping "$ns1" 100.64.0.3
do_ping "$ns2" 100.64.0.3
do_ping "$ns3" 100.64.0.2
do_ping "$ns1" dead:beef:1::2
do_ping "$ns1" dead:beef:1::3
do_ping "$ns2" dead:beef:1::1
do_ping "$ns2" dead:beef:1::2
do_ping "$ns3" dead:beef:1::1
do_ping "$ns3" dead:beef:1::2
stop_if_error "Initial validation failed."
# Wait until supervisor all supervision frames have been processed and the node
# entries have been merged. Otherwise duplicate frames will be observed which is
# valid at this stage.
WAIT=5
while [ ${WAIT} -gt 0 ]
do
grep 00:00:00:00:00:00 /sys/kernel/debug/hsr/hsr*/node_table
if [ $? -ne 0 ]
then
break
fi
sleep 1
let WAIT = WAIT - 1
done
# Just a safety delay in case the above check didn't handle it.
sleep 1
echo "INFO: Longer ping test."
do_ping_long "$ns1" 100.64.0.2
do_ping_long "$ns1" dead:beef:1::2
do_ping_long "$ns1" 100.64.0.3
do_ping_long "$ns1" dead:beef:1::3
stop_if_error "Longer ping test failed."
do_ping_long "$ns2" 100.64.0.1
do_ping_long "$ns2" dead:beef:1::1
do_ping_long "$ns2" 100.64.0.3
do_ping_long "$ns2" dead:beef:1::2
stop_if_error "Longer ping test failed."
do_ping_long "$ns3" 100.64.0.1
do_ping_long "$ns3" dead:beef:1::1
do_ping_long "$ns3" 100.64.0.2
do_ping_long "$ns3" dead:beef:1::2
stop_if_error "Longer ping test failed."
echo "INFO: Cutting one link."
do_ping_long "$ns1" 100.64.0.3 &
sleep 3
ip -net "$ns3" link set ns3eth1 down
wait
ip -net "$ns3" link set ns3eth1 up
stop_if_error "Failed with one link down."
echo "INFO: Delay the link and drop a few packages."
tc -net "$ns3" qdisc add dev ns3eth1 root netem delay 50ms
tc -net "$ns2" qdisc add dev ns2eth1 root netem delay 5ms loss 25%
do_ping_long "$ns1" 100.64.0.2
do_ping_long "$ns1" 100.64.0.3
stop_if_error "Failed with delay and packetloss."
do_ping_long "$ns2" 100.64.0.1
do_ping_long "$ns2" 100.64.0.3
stop_if_error "Failed with delay and packetloss."
do_ping_long "$ns3" 100.64.0.1
do_ping_long "$ns3" 100.64.0.2
stop_if_error "Failed with delay and packetloss."
echo "INFO: All good."
exit $ret
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment