Merge branch 'net-hsr-fixes-for-prp-duplication-and-vlan-unwind'
Luka Gejak says: ==================== net: hsr: fixes for PRP duplication and VLAN unwind This series addresses two logic bugs in the HSR/PRP implementation identified during a protocol audit. These are targeted for the 'net' tree as they fix potential memory corruption and state inconsistency. The primary change resolves a race condition in the node merging path by implementing address-based lock ordering. This ensures that concurrent mutations of sequence blocks do not lead to state corruption or deadlocks. An additional fix corrects asymmetric VLAN error unwinding by implementing a centralized unwind path on slave errors. ==================== Link: https://patch.msgid.link/20260401092243.52121-1-luka.gejak@linux.dev Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
be193568be
|
|
@ -532,8 +532,8 @@ static void hsr_change_rx_flags(struct net_device *dev, int change)
|
||||||
static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev,
|
static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev,
|
||||||
__be16 proto, u16 vid)
|
__be16 proto, u16 vid)
|
||||||
{
|
{
|
||||||
bool is_slave_a_added = false;
|
struct net_device *slave_a_dev = NULL;
|
||||||
bool is_slave_b_added = false;
|
struct net_device *slave_b_dev = NULL;
|
||||||
struct hsr_port *port;
|
struct hsr_port *port;
|
||||||
struct hsr_priv *hsr;
|
struct hsr_priv *hsr;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
@ -549,33 +549,35 @@ static int hsr_ndo_vlan_rx_add_vid(struct net_device *dev,
|
||||||
switch (port->type) {
|
switch (port->type) {
|
||||||
case HSR_PT_SLAVE_A:
|
case HSR_PT_SLAVE_A:
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/* clean up Slave-B */
|
|
||||||
netdev_err(dev, "add vid failed for Slave-A\n");
|
netdev_err(dev, "add vid failed for Slave-A\n");
|
||||||
if (is_slave_b_added)
|
goto unwind;
|
||||||
vlan_vid_del(port->dev, proto, vid);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
slave_a_dev = port->dev;
|
||||||
is_slave_a_added = true;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HSR_PT_SLAVE_B:
|
case HSR_PT_SLAVE_B:
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/* clean up Slave-A */
|
|
||||||
netdev_err(dev, "add vid failed for Slave-B\n");
|
netdev_err(dev, "add vid failed for Slave-B\n");
|
||||||
if (is_slave_a_added)
|
goto unwind;
|
||||||
vlan_vid_del(port->dev, proto, vid);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
slave_b_dev = port->dev;
|
||||||
is_slave_b_added = true;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (ret)
|
||||||
|
goto unwind;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
unwind:
|
||||||
|
if (slave_a_dev)
|
||||||
|
vlan_vid_del(slave_a_dev, proto, vid);
|
||||||
|
|
||||||
|
if (slave_b_dev)
|
||||||
|
vlan_vid_del(slave_b_dev, proto, vid);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hsr_ndo_vlan_rx_kill_vid(struct net_device *dev,
|
static int hsr_ndo_vlan_rx_kill_vid(struct net_device *dev,
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,40 @@ static void hsr_free_node_rcu(struct rcu_head *rn)
|
||||||
hsr_free_node(node);
|
hsr_free_node(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hsr_lock_seq_out_pair(struct hsr_node *node_a,
|
||||||
|
struct hsr_node *node_b)
|
||||||
|
{
|
||||||
|
if (node_a == node_b) {
|
||||||
|
spin_lock_bh(&node_a->seq_out_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node_a < node_b) {
|
||||||
|
spin_lock_bh(&node_a->seq_out_lock);
|
||||||
|
spin_lock_nested(&node_b->seq_out_lock, SINGLE_DEPTH_NESTING);
|
||||||
|
} else {
|
||||||
|
spin_lock_bh(&node_b->seq_out_lock);
|
||||||
|
spin_lock_nested(&node_a->seq_out_lock, SINGLE_DEPTH_NESTING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hsr_unlock_seq_out_pair(struct hsr_node *node_a,
|
||||||
|
struct hsr_node *node_b)
|
||||||
|
{
|
||||||
|
if (node_a == node_b) {
|
||||||
|
spin_unlock_bh(&node_a->seq_out_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node_a < node_b) {
|
||||||
|
spin_unlock(&node_b->seq_out_lock);
|
||||||
|
spin_unlock_bh(&node_a->seq_out_lock);
|
||||||
|
} else {
|
||||||
|
spin_unlock(&node_a->seq_out_lock);
|
||||||
|
spin_unlock_bh(&node_b->seq_out_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void hsr_del_nodes(struct list_head *node_db)
|
void hsr_del_nodes(struct list_head *node_db)
|
||||||
{
|
{
|
||||||
struct hsr_node *node;
|
struct hsr_node *node;
|
||||||
|
|
@ -432,7 +466,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);
|
hsr_lock_seq_out_pair(node_real, node_curr);
|
||||||
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])) {
|
||||||
|
|
@ -455,7 +489,7 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame)
|
||||||
src_blk->seq_nrs[i], HSR_SEQ_BLOCK_SIZE);
|
src_blk->seq_nrs[i], HSR_SEQ_BLOCK_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spin_unlock_bh(&node_real->seq_out_lock);
|
hsr_unlock_seq_out_pair(node_real, node_curr);
|
||||||
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);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue