Commit 20d3c1e9 authored by Sebastian Andrzej Siewior's avatar Sebastian Andrzej Siewior Committed by Jakub Kicinski

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: default avatarSebastian Andrzej Siewior <bigeasy@linutronix.de>
Reviewed-by: Kurt Kanzenbach's avatarKurt Kanzenbach <kurt@linutronix.de>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 5c7aa132
...@@ -490,7 +490,6 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], ...@@ -490,7 +490,6 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
hsr = netdev_priv(hsr_dev); hsr = netdev_priv(hsr_dev);
INIT_LIST_HEAD(&hsr->ports); INIT_LIST_HEAD(&hsr->ports);
INIT_LIST_HEAD(&hsr->node_db); INIT_LIST_HEAD(&hsr->node_db);
INIT_LIST_HEAD(&hsr->self_node_db);
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);
......
...@@ -38,21 +38,22 @@ static bool seq_nr_after(u16 a, u16 b) ...@@ -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) 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, rcu_read_lock();
mac_list); sn = rcu_dereference(hsr->self_node);
if (!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.
...@@ -70,50 +71,42 @@ static struct hsr_node *find_node_by_addr_A(struct list_head *node_db, ...@@ -70,50 +71,42 @@ static struct hsr_node *find_node_by_addr_A(struct list_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 list_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 = list_first_or_null_rcu(self_node_db, old = rcu_replace_pointer(hsr->self_node, sn,
struct hsr_node, mac_list); lockdep_is_held(&hsr->list_lock));
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); 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 list_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 = list_first_or_null_rcu(self_node_db, struct hsr_node, mac_list); old = rcu_replace_pointer(hsr->self_node, NULL,
if (node) { lockdep_is_held(&hsr->list_lock));
list_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 list_head *node_db) void hsr_del_nodes(struct list_head *node_db)
......
...@@ -182,11 +182,17 @@ struct hsr_proto_ops { ...@@ -182,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 list_head node_db; /* Known HSR nodes */ 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 announce_timer; /* Supervision frame dispatch */
struct timer_list prune_timer; struct timer_list prune_timer;
int announce_count; int announce_count;
......
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