hsr: Use a single struct for self_node.
self_node_db is a list_head with one entry of struct hsr_node. The purpose is to hold the two MAC addresses of the node itself. It is convenient to recycle the structure. However having a list_head and fetching always the first entry is not really optimal. Created a new data strucure contaning the two MAC addresses named hsr_self_node. Access that structure like an RCU protected pointer so it can be replaced on the fly without blocking the reader. Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Reviewed-by: Kurt Kanzenbach <kurt@linutronix.de> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
5c7aa13210
commit
20d3c1e9b8
@ -490,7 +490,6 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
|
||||
hsr = netdev_priv(hsr_dev);
|
||||
INIT_LIST_HEAD(&hsr->ports);
|
||||
INIT_LIST_HEAD(&hsr->node_db);
|
||||
INIT_LIST_HEAD(&hsr->self_node_db);
|
||||
spin_lock_init(&hsr->list_lock);
|
||||
|
||||
eth_hw_addr_set(hsr_dev, slave[0]->dev_addr);
|
||||
|
@ -38,21 +38,22 @@ 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 = list_first_or_null_rcu(&hsr->self_node_db, struct hsr_node,
|
||||
mac_list);
|
||||
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.
|
||||
@ -70,50 +71,42 @@ static struct hsr_node *find_node_by_addr_A(struct list_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 list_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 = list_first_or_null_rcu(self_node_db,
|
||||
struct hsr_node, mac_list);
|
||||
if (oldnode) {
|
||||
list_replace_rcu(&oldnode->mac_list, &node->mac_list);
|
||||
spin_unlock_bh(&hsr->list_lock);
|
||||
kfree_rcu(oldnode, rcu_head);
|
||||
} else {
|
||||
list_add_tail_rcu(&node->mac_list, self_node_db);
|
||||
spin_unlock_bh(&hsr->list_lock);
|
||||
}
|
||||
old = rcu_replace_pointer(hsr->self_node, sn,
|
||||
lockdep_is_held(&hsr->list_lock));
|
||||
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 list_head *self_node_db = &hsr->self_node_db;
|
||||
struct hsr_node *node;
|
||||
struct hsr_self_node *old;
|
||||
|
||||
spin_lock_bh(&hsr->list_lock);
|
||||
node = list_first_or_null_rcu(self_node_db, struct hsr_node, mac_list);
|
||||
if (node) {
|
||||
list_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 list_head *node_db)
|
||||
|
@ -182,11 +182,17 @@ struct hsr_proto_ops {
|
||||
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 rcu_head rcu_head;
|
||||
struct list_head ports;
|
||||
struct list_head node_db; /* Known HSR nodes */
|
||||
struct list_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 prune_timer;
|
||||
int announce_count;
|
||||
|
Loading…
Reference in New Issue
Block a user