Commit 5c7aa132 authored by Sebastian Andrzej Siewior's avatar Sebastian Andrzej Siewior Committed by Jakub Kicinski

hsr: Synchronize sequence number updates.

hsr_register_frame_out() compares new sequence_nr vs the old one
recorded in hsr_node::seq_out and if the new sequence_nr is higher then
it will be written to hsr_node::seq_out as the new value.

This operation isn't locked so it is possible that two frames with the
same sequence number arrive (via the two slave devices) and are fed to
hsr_register_frame_out() at the same time. Both will pass the check and
update the sequence counter later to the same value. As a result the
content of the same packet is fed into the stack twice.

This was noticed by running ping and observing DUP being reported from
time to time.

Instead of using the hsr_priv::seqnr_lock for the whole receive path (as
it is for sending in the master node) add an additional lock that is only
used for sequence number checks and updates.

Add a per-node lock that is used during sequence number reads and
updates.

Fixes: f421436a ("net/hsr: Add support for the High-availability Seamless Redundancy protocol (HSRv0)")
Signed-off-by: default avatarSebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 06afd2c3
...@@ -157,6 +157,7 @@ static struct hsr_node *hsr_add_node(struct hsr_priv *hsr, ...@@ -157,6 +157,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).
...@@ -353,6 +354,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) ...@@ -353,6 +354,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
} }
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])) {
...@@ -363,6 +365,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) ...@@ -363,6 +365,7 @@ 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);
...@@ -456,13 +459,17 @@ void hsr_register_frame_in(struct hsr_node *node, struct hsr_port *port, ...@@ -456,13 +459,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;
} }
......
...@@ -69,6 +69,8 @@ void prp_update_san_info(struct hsr_node *node, bool is_sup); ...@@ -69,6 +69,8 @@ void prp_update_san_info(struct hsr_node *node, bool is_sup);
struct hsr_node { struct hsr_node {
struct list_head 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 */
......
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