Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Jakub Kicinski and committed by
David S. Miller
a387ff8e d07b26f5

+34 -28
+34 -28
net/core/dev_addr_lists.c
··· 16 16 * General list handling functions 17 17 */ 18 18 19 + static int __hw_addr_insert(struct netdev_hw_addr_list *list, 20 + struct netdev_hw_addr *new, int addr_len) 21 + { 22 + struct rb_node **ins_point = &list->tree.rb_node, *parent = NULL; 23 + struct netdev_hw_addr *ha; 24 + 25 + while (*ins_point) { 26 + int diff; 27 + 28 + ha = rb_entry(*ins_point, struct netdev_hw_addr, node); 29 + diff = memcmp(new->addr, ha->addr, addr_len); 30 + if (diff == 0) 31 + diff = memcmp(&new->type, &ha->type, sizeof(new->type)); 32 + 33 + parent = *ins_point; 34 + if (diff < 0) 35 + ins_point = &parent->rb_left; 36 + else if (diff > 0) 37 + ins_point = &parent->rb_right; 38 + else 39 + return -EEXIST; 40 + } 41 + 42 + rb_link_node_rcu(&new->node, parent, ins_point); 43 + rb_insert_color(&new->node, &list->tree); 44 + 45 + return 0; 46 + } 47 + 19 48 static struct netdev_hw_addr* 20 49 __hw_addr_create(const unsigned char *addr, int addr_len, 21 50 unsigned char addr_type, bool global, bool sync) ··· 79 50 if (addr_len > MAX_ADDR_LEN) 80 51 return -EINVAL; 81 52 82 - ha = list_first_entry(&list->list, struct netdev_hw_addr, list); 83 - if (ha && !memcmp(addr, ha->addr, addr_len) && 84 - (!addr_type || addr_type == ha->type)) 85 - goto found_it; 86 - 87 53 while (*ins_point) { 88 54 int diff; 89 55 ··· 93 69 } else if (diff > 0) { 94 70 ins_point = &parent->rb_right; 95 71 } else { 96 - found_it: 97 72 if (exclusive) 98 73 return -EEXIST; 99 74 if (global) { ··· 117 94 if (!ha) 118 95 return -ENOMEM; 119 96 120 - /* The first address in dev->dev_addrs is pointed to by dev->dev_addr 121 - * and mutated freely by device drivers and netdev ops, so if we insert 122 - * it into the tree we'll end up with an invalid rbtree. 123 - */ 124 - if (list->count > 0) { 125 - rb_link_node(&ha->node, parent, ins_point); 126 - rb_insert_color(&ha->node, &list->tree); 127 - } else { 128 - RB_CLEAR_NODE(&ha->node); 129 - } 97 + rb_link_node(&ha->node, parent, ins_point); 98 + rb_insert_color(&ha->node, &list->tree); 130 99 131 100 list_add_tail_rcu(&ha->list, &list->list); 132 101 list->count++; ··· 153 138 if (--ha->refcount) 154 139 return 0; 155 140 156 - if (!RB_EMPTY_NODE(&ha->node)) 157 - rb_erase(&ha->node, &list->tree); 141 + rb_erase(&ha->node, &list->tree); 158 142 159 143 list_del_rcu(&ha->list); 160 144 kfree_rcu(ha, rcu_head); ··· 165 151 const unsigned char *addr, int addr_len, 166 152 unsigned char addr_type) 167 153 { 168 - struct netdev_hw_addr *ha; 169 154 struct rb_node *node; 170 - 171 - /* The first address isn't inserted into the tree because in the dev->dev_addrs 172 - * list it's the address pointed to by dev->dev_addr which is freely mutated 173 - * in place, so we need to check it separately. 174 - */ 175 - ha = list_first_entry(&list->list, struct netdev_hw_addr, list); 176 - if (ha && !memcmp(addr, ha->addr, addr_len) && 177 - (!addr_type || addr_type == ha->type)) 178 - return ha; 179 155 180 156 node = list->tree.rb_node; 181 157 ··· 575 571 dev_addr_check(dev); 576 572 577 573 ha = container_of(dev->dev_addr, struct netdev_hw_addr, addr[0]); 574 + rb_erase(&ha->node, &dev->dev_addrs.tree); 578 575 memcpy(&ha->addr[offset], addr, len); 579 576 memcpy(&dev->dev_addr_shadow[offset], addr, len); 577 + WARN_ON(__hw_addr_insert(&dev->dev_addrs, ha, dev->addr_len)); 580 578 } 581 579 EXPORT_SYMBOL(dev_addr_mod); 582 580