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

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
Loading
Loading
Loading
Loading
+16 −24
Original line number Diff line number Diff line
@@ -9,7 +9,6 @@
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/debugfs.h>
#include <linux/jhash.h>
#include "hsr_main.h"
#include "hsr_framereg.h"

@@ -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_node *node;
	int i;

	seq_printf(sfp, "Node Table entries for (%s) device\n",
		   (priv->prot_version == PRP_V1 ? "PRP" : "HSR"));
@@ -33,29 +31,23 @@ hsr_node_table_show(struct seq_file *sfp, void *data)
		seq_puts(sfp, "DAN-H\n");

	rcu_read_lock();

	for (i = 0 ; i < priv->hash_buckets; i++) {
		hlist_for_each_entry_rcu(node, &priv->node_db[i], mac_list) {
	list_for_each_entry_rcu(node, &priv->node_db, mac_list) {
		/* skip self node */
		if (hsr_addr_is_self(priv, node->macaddress_A))
			continue;
		seq_printf(sfp, "%pM ", &node->macaddress_A[0]);
		seq_printf(sfp, "%pM ", &node->macaddress_B[0]);
			seq_printf(sfp, "%10lx, ",
				   node->time_in[HSR_PT_SLAVE_A]);
			seq_printf(sfp, "%10lx, ",
				   node->time_in[HSR_PT_SLAVE_B]);
		seq_printf(sfp, "%10lx, ", node->time_in[HSR_PT_SLAVE_A]);
		seq_printf(sfp, "%10lx, ", node->time_in[HSR_PT_SLAVE_B]);
		seq_printf(sfp, "%14x, ", node->addr_B_port);

		if (priv->prot_version == PRP_V1)
			seq_printf(sfp, "%5x, %5x, %5x\n",
				   node->san_a, node->san_b,
					   (node->san_a == 0 &&
					    node->san_b == 0));
				   (node->san_a == 0 && node->san_b == 0));
		else
			seq_printf(sfp, "%5x\n", 1);
	}
	}
	rcu_read_unlock();
	return 0;
}
+13 −18
Original line number Diff line number Diff line
@@ -219,7 +219,9 @@ static netdev_tx_t hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev)
		skb->dev = master->dev;
		skb_reset_mac_header(skb);
		skb_reset_mac_len(skb);
		spin_lock_bh(&hsr->seqnr_lock);
		hsr_forward_skb(skb, master);
		spin_unlock_bh(&hsr->seqnr_lock);
	} else {
		dev_core_stats_tx_dropped_inc(dev);
		dev_kfree_skb_any(skb);
@@ -278,7 +280,6 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
	__u8 type = HSR_TLV_LIFE_CHECK;
	struct hsr_sup_payload *hsr_sp;
	struct hsr_sup_tag *hsr_stag;
	unsigned long irqflags;
	struct sk_buff *skb;

	*interval = msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL);
@@ -299,7 +300,7 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
	set_hsr_stag_HSR_ver(hsr_stag, hsr->prot_version);

	/* 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) {
		hsr_stag->sequence_nr = htons(hsr->sup_sequence_nr);
		hsr->sup_sequence_nr++;
@@ -307,7 +308,6 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
		hsr_stag->sequence_nr = htons(hsr->sequence_nr);
		hsr->sequence_nr++;
	}
	spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);

	hsr_stag->tlv.HSR_TLV_type = type;
	/* TODO: Why 12 in HSRv0? */
@@ -318,11 +318,13 @@ static void send_hsr_supervision_frame(struct hsr_port *master,
	hsr_sp = skb_put(skb, sizeof(struct hsr_sup_payload));
	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;
	}

	hsr_forward_skb(skb, master);

	spin_unlock_bh(&hsr->seqnr_lock);
	return;
}

@@ -332,7 +334,6 @@ static void send_prp_supervision_frame(struct hsr_port *master,
	struct hsr_priv *hsr = master->hsr;
	struct hsr_sup_payload *hsr_sp;
	struct hsr_sup_tag *hsr_stag;
	unsigned long irqflags;
	struct sk_buff *skb;

	skb = hsr_init_skb(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));

	/* 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->sup_sequence_nr++;
	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,
	ether_addr_copy(hsr_sp->macaddress_A, master->dev->dev_addr);

	if (skb_put_padto(skb, ETH_ZLEN)) {
		spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);
		spin_unlock_bh(&hsr->seqnr_lock);
		return;
	}

	spin_unlock_irqrestore(&master->hsr->seqnr_lock, irqflags);

	hsr_forward_skb(skb, master);
	spin_unlock_bh(&hsr->seqnr_lock);
}

/* Announce (supervision frame) timer function
@@ -444,7 +444,7 @@ void hsr_dev_setup(struct net_device *dev)
	dev->header_ops = &hsr_header_ops;
	dev->netdev_ops = &hsr_device_ops;
	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;

@@ -485,16 +485,11 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
{
	bool unregister = false;
	struct hsr_priv *hsr;
	int res, i;
	int res;

	hsr = netdev_priv(hsr_dev);
	INIT_LIST_HEAD(&hsr->ports);
	INIT_HLIST_HEAD(&hsr->self_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]);

	INIT_LIST_HEAD(&hsr->node_db);
	spin_lock_init(&hsr->list_lock);

	eth_hw_addr_set(hsr_dev, slave[0]->dev_addr);
+6 −8
Original line number Diff line number Diff line
@@ -500,7 +500,6 @@ static void handle_std_frame(struct sk_buff *skb,
{
	struct hsr_port *port = frame->port_rcv;
	struct hsr_priv *hsr = port->hsr;
	unsigned long irqflags;

	frame->skb_hsr = NULL;
	frame->skb_prp = NULL;
@@ -510,10 +509,9 @@ static void handle_std_frame(struct sk_buff *skb,
		frame->is_from_san = true;
	} else {
		/* 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;
		hsr->sequence_nr++;
		spin_unlock_irqrestore(&hsr->seqnr_lock, irqflags);
	}
}

@@ -571,23 +569,20 @@ static int fill_frame_info(struct hsr_frame_info *frame,
	struct ethhdr *ethhdr;
	__be16 proto;
	int ret;
	u32 hash;

	/* Check if skb contains ethhdr */
	if (skb->mac_len < sizeof(struct ethhdr))
		return -EINVAL;

	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->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,
				       port->type);
	if (!frame->node_src)
		return -1; /* Unknown node and !is_supervision, or no mem */

	ethhdr = (struct ethhdr *)skb_mac_header(skb);
	frame->is_vlan = false;
	proto = ethhdr->h_proto;

@@ -617,11 +612,13 @@ void hsr_forward_skb(struct sk_buff *skb, struct hsr_port *port)
{
	struct hsr_frame_info frame;

	rcu_read_lock();
	if (fill_frame_info(&frame, skb, port) < 0)
		goto out_drop;

	hsr_register_frame_in(frame.node_src, port, frame.sequence_nr);
	hsr_forward_do(&frame);
	rcu_read_unlock();
	/* Gets called for ingress frames as well as egress from master port.
	 * 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)
	return;

out_drop:
	rcu_read_unlock();
	port->dev->stats.tx_dropped++;
	kfree_skb(skb);
}
+110 −155
Original line number Diff line number Diff line
@@ -15,37 +15,10 @@
#include <linux/etherdevice.h>
#include <linux/slab.h>
#include <linux/rculist.h>
#include <linux/jhash.h>
#include "hsr_main.h"
#include "hsr_framereg.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,
 * false otherwise.
 */
@@ -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)
{
	struct hsr_node *node;
	struct hsr_self_node *sn;
	bool ret = false;

	node = hsr_node_get_first(&hsr->self_node_db, &hsr->list_lock);
	if (!node) {
	rcu_read_lock();
	sn = rcu_dereference(hsr->self_node);
	if (!sn) {
		WARN_ONCE(1, "HSR: No self node\n");
		return false;
		goto out;
	}

	if (ether_addr_equal(addr, node->macaddress_A))
		return true;
	if (ether_addr_equal(addr, node->macaddress_B))
		return true;

	return false;
	if (ether_addr_equal(addr, sn->macaddress_A) ||
	    ether_addr_equal(addr, sn->macaddress_B))
		ret = true;
out:
	rcu_read_unlock();
	return ret;
}

/* 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])
{
	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))
			return node;
	}
@@ -96,58 +71,51 @@ static struct hsr_node *find_node_by_addr_A(struct hlist_head *node_db,
	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.
 */
int hsr_create_self_node(struct hsr_priv *hsr,
			 const unsigned char addr_a[ETH_ALEN],
			 const unsigned char addr_b[ETH_ALEN])
{
	struct hlist_head *self_node_db = &hsr->self_node_db;
	struct hsr_node *node, *oldnode;
	struct hsr_self_node *sn, *old;

	node = kmalloc(sizeof(*node), GFP_KERNEL);
	if (!node)
	sn = kmalloc(sizeof(*sn), GFP_KERNEL);
	if (!sn)
		return -ENOMEM;

	ether_addr_copy(node->macaddress_A, addr_a);
	ether_addr_copy(node->macaddress_B, addr_b);
	ether_addr_copy(sn->macaddress_A, addr_a);
	ether_addr_copy(sn->macaddress_B, addr_b);

	spin_lock_bh(&hsr->list_lock);
	oldnode = hsr_node_get_first(self_node_db, &hsr->list_lock);
	if (oldnode) {
		hlist_replace_rcu(&oldnode->mac_list, &node->mac_list);
	old = rcu_replace_pointer(hsr->self_node, sn,
				  lockdep_is_held(&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;
}

void hsr_del_self_node(struct hsr_priv *hsr)
{
	struct hlist_head *self_node_db = &hsr->self_node_db;
	struct hsr_node *node;
	struct hsr_self_node *old;

	spin_lock_bh(&hsr->list_lock);
	node = hsr_node_get_first(self_node_db, &hsr->list_lock);
	if (node) {
		hlist_del_rcu(&node->mac_list);
		kfree_rcu(node, rcu_head);
	}
	old = rcu_replace_pointer(hsr->self_node, NULL,
				  lockdep_is_held(&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 hlist_node *tmp;
	struct hsr_node *tmp;

	hlist_for_each_entry_safe(node, tmp, node_db, mac_list)
		kfree_rcu(node, rcu_head);
	list_for_each_entry_safe(node, tmp, node_db, mac_list)
		kfree(node);
}

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.
 */
static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
				     struct hlist_head *node_db,
				     struct list_head *node_db,
				     unsigned char addr[],
				     u16 seq_out, bool san,
				     enum hsr_port_type rx_port)
@@ -182,6 +150,7 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
		return NULL;

	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
	 * 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,
		hsr->proto_ops->handle_san_frame(san, rx_port, new_node);

	spin_lock_bh(&hsr->list_lock);
	hlist_for_each_entry_rcu(node, node_db, mac_list,
				 lockdep_hsr_is_held(&hsr->list_lock)) {
	list_for_each_entry_rcu(node, node_db, mac_list,
				lockdep_is_held(&hsr->list_lock)) {
		if (ether_addr_equal(node->macaddress_A, addr))
			goto out;
		if (ether_addr_equal(node->macaddress_B, addr))
			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);
	return new_node;
out:
@@ -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.
 */
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,
			      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,

	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 (hsr->proto_ops->update_san_info)
				hsr->proto_ops->update_san_info(node, is_sup);
@@ -291,12 +260,11 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
	struct hsr_sup_tlv *hsr_sup_tlv;
	struct hsr_node *node_real;
	struct sk_buff *skb = NULL;
	struct hlist_head *node_db;
	struct list_head *node_db;
	struct ethhdr *ethhdr;
	int i;
	unsigned int pull_size = 0;
	unsigned int total_pull_size = 0;
	u32 hash;

	/* Here either frame->skb_hsr or frame->skb_prp should be
	 * valid as supervision frame always will have protocol
@@ -334,13 +302,11 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
	hsr_sp = (struct hsr_sup_payload *)skb->data;

	/* Merge node_curr (registered on macaddress_B) into node_real */
	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[hash], hsr_sp->macaddress_A);
	node_db = &port_rcv->hsr->node_db;
	node_real = find_node_by_addr_A(node_db, hsr_sp->macaddress_A);
	if (!node_real)
		/* No frame received from AddrA of this node yet */
		node_real = hsr_add_node(hsr, &node_db[hash],
					 hsr_sp->macaddress_A,
		node_real = hsr_add_node(hsr, node_db, hsr_sp->macaddress_A,
					 HSR_SEQNR_START - 1, true,
					 port_rcv->type);
	if (!node_real)
@@ -374,14 +340,14 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
		hsr_sp = (struct hsr_sup_payload *)skb->data;

		/* Check if redbox mac and node mac are equal. */
		if (!ether_addr_equal(node_real->macaddress_A,
				      hsr_sp->macaddress_A)) {
		if (!ether_addr_equal(node_real->macaddress_A, hsr_sp->macaddress_A)) {
			/* This is a redbox supervision frame for a VDAN! */
			goto done;
		}
	}

	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++) {
		if (!node_curr->time_in_stale[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)
		if (seq_nr_after(node_curr->seq_out[i], node_real->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;

	spin_lock_bh(&hsr->list_lock);
	hlist_del_rcu(&node_curr->mac_list);
	spin_unlock_bh(&hsr->list_lock);
	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);

done:
	/* Push back here */
@@ -433,7 +403,6 @@ void hsr_addr_subst_dest(struct hsr_node *node_src, struct sk_buff *skb,
			 struct hsr_port *port)
{
	struct hsr_node *node_dst;
	u32 hash;

	if (!skb_mac_header_was_set(skb)) {
		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,
	if (!is_unicast_ether_addr(eth_hdr(skb)->h_dest))
		return;

	hash = hsr_mac_hash(port->hsr, eth_hdr(skb)->h_dest);
	node_dst = find_node_by_addr_A(&port->hsr->node_db[hash],
	node_dst = find_node_by_addr_A(&port->hsr->node_db,
				       eth_hdr(skb)->h_dest);
	if (!node_dst) {
		if (net_ratelimit())
@@ -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,
			   u16 sequence_nr)
{
	spin_lock_bh(&node->seq_out_lock);
	if (seq_nr_before_or_eq(sequence_nr, node->seq_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;
	}

	node->time_out[port->type] = jiffies;
	node->seq_out[port->type] = sequence_nr;
	spin_unlock_bh(&node->seq_out_lock);
	return 0;
}

@@ -520,23 +492,18 @@ static struct hsr_port *get_late_port(struct hsr_priv *hsr,
void hsr_prune_nodes(struct timer_list *t)
{
	struct hsr_priv *hsr = from_timer(hsr, t, prune_timer);
	struct hlist_node *tmp;
	struct hsr_node *node;
	struct hsr_node *tmp;
	struct hsr_port *port;
	unsigned long timestamp;
	unsigned long time_a, time_b;
	int i;

	spin_lock_bh(&hsr->list_lock);

	for (i = 0; i < hsr->hash_buckets; i++) {
		hlist_for_each_entry_safe(node, tmp, &hsr->node_db[i],
					  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.
	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;
@@ -545,17 +512,15 @@ void hsr_prune_nodes(struct timer_list *t)
		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
			 */
		/* 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
		 * 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] ||
@@ -563,17 +528,13 @@ void hsr_prune_nodes(struct timer_list *t)
		    time_after(time_b, time_a)))
			timestamp = time_b;

			/* Warn of ring error only as long as we get
			 * frames at all
			 */
		/* 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);
				hsr_nl_ringerror(hsr, node->macaddress_A, port);
			rcu_read_unlock();
		}

@@ -581,10 +542,10 @@ void hsr_prune_nodes(struct timer_list *t)
		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:
				 */
			if (!node->removed) {
				list_del_rcu(&node->mac_list);
				node->removed = true;
				/* Note that we need to free this entry later: */
				kfree_rcu(node, rcu_head);
			}
		}
@@ -600,20 +561,17 @@ void *hsr_get_next_node(struct hsr_priv *hsr, void *_pos,
			unsigned char addr[ETH_ALEN])
{
	struct hsr_node *node;
	u32 hash;

	hash = hsr_mac_hash(hsr, addr);

	if (!_pos) {
		node = hsr_node_get_first(&hsr->node_db[hash],
					  &hsr->list_lock);
		node = list_first_or_null_rcu(&hsr->node_db,
					      struct hsr_node, mac_list);
		if (node)
			ether_addr_copy(addr, node->macaddress_A);
		return node;
	}

	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);
		return node;
	}
@@ -633,11 +591,8 @@ int hsr_get_node_data(struct hsr_priv *hsr,
	struct hsr_node *node;
	struct hsr_port *port;
	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)
		return -ENOENT;

+6 −11
Original line number Diff line number Diff line
@@ -28,17 +28,9 @@ struct hsr_frame_info {
	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_nodes(struct hlist_head *node_db);
struct hsr_node *hsr_get_node(struct hsr_port *port, 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 list_head *node_db,
			      struct sk_buff *skb, bool is_sup,
			      enum hsr_port_type rx_port);
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,
void prp_update_san_info(struct hsr_node *node, bool is_sup);

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_B[ETH_ALEN];
	/* Local slave through which AddrB frames are received from this node */
@@ -88,6 +82,7 @@ struct hsr_node {
	bool			san_a;
	bool			san_b;
	u16			seq_out[HSR_PT_PORTS];
	bool			removed;
	struct rcu_head		rcu_head;
};

Loading