Commit a387ff8e authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller

dev_addr_list: put the first addr on the tree

Since all netdev->dev_addr modifications go via dev_addr_mod()
we can put it on the list. When address is change remove it
and add it back.
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d07b26f5
...@@ -16,6 +16,35 @@ ...@@ -16,6 +16,35 @@
* General list handling functions * General list handling functions
*/ */
static int __hw_addr_insert(struct netdev_hw_addr_list *list,
struct netdev_hw_addr *new, int addr_len)
{
struct rb_node **ins_point = &list->tree.rb_node, *parent = NULL;
struct netdev_hw_addr *ha;
while (*ins_point) {
int diff;
ha = rb_entry(*ins_point, struct netdev_hw_addr, node);
diff = memcmp(new->addr, ha->addr, addr_len);
if (diff == 0)
diff = memcmp(&new->type, &ha->type, sizeof(new->type));
parent = *ins_point;
if (diff < 0)
ins_point = &parent->rb_left;
else if (diff > 0)
ins_point = &parent->rb_right;
else
return -EEXIST;
}
rb_link_node_rcu(&new->node, parent, ins_point);
rb_insert_color(&new->node, &list->tree);
return 0;
}
static struct netdev_hw_addr* static struct netdev_hw_addr*
__hw_addr_create(const unsigned char *addr, int addr_len, __hw_addr_create(const unsigned char *addr, int addr_len,
unsigned char addr_type, bool global, bool sync) unsigned char addr_type, bool global, bool sync)
...@@ -50,11 +79,6 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list, ...@@ -50,11 +79,6 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
if (addr_len > MAX_ADDR_LEN) if (addr_len > MAX_ADDR_LEN)
return -EINVAL; return -EINVAL;
ha = list_first_entry(&list->list, struct netdev_hw_addr, list);
if (ha && !memcmp(addr, ha->addr, addr_len) &&
(!addr_type || addr_type == ha->type))
goto found_it;
while (*ins_point) { while (*ins_point) {
int diff; int diff;
...@@ -69,7 +93,6 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list, ...@@ -69,7 +93,6 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
} else if (diff > 0) { } else if (diff > 0) {
ins_point = &parent->rb_right; ins_point = &parent->rb_right;
} else { } else {
found_it:
if (exclusive) if (exclusive)
return -EEXIST; return -EEXIST;
if (global) { if (global) {
...@@ -94,16 +117,8 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list, ...@@ -94,16 +117,8 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
if (!ha) if (!ha)
return -ENOMEM; return -ENOMEM;
/* The first address in dev->dev_addrs is pointed to by dev->dev_addr rb_link_node(&ha->node, parent, ins_point);
* and mutated freely by device drivers and netdev ops, so if we insert rb_insert_color(&ha->node, &list->tree);
* it into the tree we'll end up with an invalid rbtree.
*/
if (list->count > 0) {
rb_link_node(&ha->node, parent, ins_point);
rb_insert_color(&ha->node, &list->tree);
} else {
RB_CLEAR_NODE(&ha->node);
}
list_add_tail_rcu(&ha->list, &list->list); list_add_tail_rcu(&ha->list, &list->list);
list->count++; list->count++;
...@@ -138,8 +153,7 @@ static int __hw_addr_del_entry(struct netdev_hw_addr_list *list, ...@@ -138,8 +153,7 @@ static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
if (--ha->refcount) if (--ha->refcount)
return 0; return 0;
if (!RB_EMPTY_NODE(&ha->node)) rb_erase(&ha->node, &list->tree);
rb_erase(&ha->node, &list->tree);
list_del_rcu(&ha->list); list_del_rcu(&ha->list);
kfree_rcu(ha, rcu_head); kfree_rcu(ha, rcu_head);
...@@ -151,18 +165,8 @@ static struct netdev_hw_addr *__hw_addr_lookup(struct netdev_hw_addr_list *list, ...@@ -151,18 +165,8 @@ static struct netdev_hw_addr *__hw_addr_lookup(struct netdev_hw_addr_list *list,
const unsigned char *addr, int addr_len, const unsigned char *addr, int addr_len,
unsigned char addr_type) unsigned char addr_type)
{ {
struct netdev_hw_addr *ha;
struct rb_node *node; struct rb_node *node;
/* The first address isn't inserted into the tree because in the dev->dev_addrs
* list it's the address pointed to by dev->dev_addr which is freely mutated
* in place, so we need to check it separately.
*/
ha = list_first_entry(&list->list, struct netdev_hw_addr, list);
if (ha && !memcmp(addr, ha->addr, addr_len) &&
(!addr_type || addr_type == ha->type))
return ha;
node = list->tree.rb_node; node = list->tree.rb_node;
while (node) { while (node) {
...@@ -571,8 +575,10 @@ void dev_addr_mod(struct net_device *dev, unsigned int offset, ...@@ -571,8 +575,10 @@ void dev_addr_mod(struct net_device *dev, unsigned int offset,
dev_addr_check(dev); dev_addr_check(dev);
ha = container_of(dev->dev_addr, struct netdev_hw_addr, addr[0]); ha = container_of(dev->dev_addr, struct netdev_hw_addr, addr[0]);
rb_erase(&ha->node, &dev->dev_addrs.tree);
memcpy(&ha->addr[offset], addr, len); memcpy(&ha->addr[offset], addr, len);
memcpy(&dev->dev_addr_shadow[offset], addr, len); memcpy(&dev->dev_addr_shadow[offset], addr, len);
WARN_ON(__hw_addr_insert(&dev->dev_addrs, ha, dev->addr_len));
} }
EXPORT_SYMBOL(dev_addr_mod); EXPORT_SYMBOL(dev_addr_mod);
......
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