12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2a748ee24SJiri Pirko /*
3a748ee24SJiri Pirko * net/core/dev_addr_lists.c - Functions for handling net device lists
4a748ee24SJiri Pirko * Copyright (c) 2010 Jiri Pirko <[email protected]>
5a748ee24SJiri Pirko *
6a748ee24SJiri Pirko * This file contains functions for working with unicast, multicast and device
7a748ee24SJiri Pirko * addresses lists.
8a748ee24SJiri Pirko */
9a748ee24SJiri Pirko
10a748ee24SJiri Pirko #include <linux/netdevice.h>
11a748ee24SJiri Pirko #include <linux/rtnetlink.h>
12bc3b2d7fSPaul Gortmaker #include <linux/export.h>
13a748ee24SJiri Pirko #include <linux/list.h>
14a748ee24SJiri Pirko
156264f58cSJakub Kicinski #include "dev.h"
166264f58cSJakub Kicinski
17a748ee24SJiri Pirko /*
18a748ee24SJiri Pirko * General list handling functions
19a748ee24SJiri Pirko */
20a748ee24SJiri Pirko
__hw_addr_insert(struct netdev_hw_addr_list * list,struct netdev_hw_addr * new,int addr_len)21a387ff8eSJakub Kicinski static int __hw_addr_insert(struct netdev_hw_addr_list *list,
22a387ff8eSJakub Kicinski struct netdev_hw_addr *new, int addr_len)
23a387ff8eSJakub Kicinski {
24a387ff8eSJakub Kicinski struct rb_node **ins_point = &list->tree.rb_node, *parent = NULL;
25a387ff8eSJakub Kicinski struct netdev_hw_addr *ha;
26a387ff8eSJakub Kicinski
27a387ff8eSJakub Kicinski while (*ins_point) {
28a387ff8eSJakub Kicinski int diff;
29a387ff8eSJakub Kicinski
30a387ff8eSJakub Kicinski ha = rb_entry(*ins_point, struct netdev_hw_addr, node);
31a387ff8eSJakub Kicinski diff = memcmp(new->addr, ha->addr, addr_len);
32a387ff8eSJakub Kicinski if (diff == 0)
33a387ff8eSJakub Kicinski diff = memcmp(&new->type, &ha->type, sizeof(new->type));
34a387ff8eSJakub Kicinski
35a387ff8eSJakub Kicinski parent = *ins_point;
36a387ff8eSJakub Kicinski if (diff < 0)
37a387ff8eSJakub Kicinski ins_point = &parent->rb_left;
38a387ff8eSJakub Kicinski else if (diff > 0)
39a387ff8eSJakub Kicinski ins_point = &parent->rb_right;
40a387ff8eSJakub Kicinski else
41a387ff8eSJakub Kicinski return -EEXIST;
42a387ff8eSJakub Kicinski }
43a387ff8eSJakub Kicinski
44a387ff8eSJakub Kicinski rb_link_node_rcu(&new->node, parent, ins_point);
45a387ff8eSJakub Kicinski rb_insert_color(&new->node, &list->tree);
46a387ff8eSJakub Kicinski
47a387ff8eSJakub Kicinski return 0;
48a387ff8eSJakub Kicinski }
49a387ff8eSJakub Kicinski
50406f42faSGilad Naaman static struct netdev_hw_addr*
__hw_addr_create(const unsigned char * addr,int addr_len,unsigned char addr_type,bool global,bool sync)51406f42faSGilad Naaman __hw_addr_create(const unsigned char *addr, int addr_len,
52406f42faSGilad Naaman unsigned char addr_type, bool global, bool sync)
53a748ee24SJiri Pirko {
54a748ee24SJiri Pirko struct netdev_hw_addr *ha;
55a748ee24SJiri Pirko int alloc_size;
56a748ee24SJiri Pirko
5712a94634SJohn Fastabend alloc_size = sizeof(*ha);
5812a94634SJohn Fastabend if (alloc_size < L1_CACHE_BYTES)
5912a94634SJohn Fastabend alloc_size = L1_CACHE_BYTES;
6012a94634SJohn Fastabend ha = kmalloc(alloc_size, GFP_ATOMIC);
6112a94634SJohn Fastabend if (!ha)
62406f42faSGilad Naaman return NULL;
6312a94634SJohn Fastabend memcpy(ha->addr, addr, addr_len);
6412a94634SJohn Fastabend ha->type = addr_type;
6512a94634SJohn Fastabend ha->refcount = 1;
6612a94634SJohn Fastabend ha->global_use = global;
676ef7b8a2SVlad Yasevich ha->synced = sync ? 1 : 0;
689747ba66SJay Vosburgh ha->sync_cnt = 0;
6912a94634SJohn Fastabend
70406f42faSGilad Naaman return ha;
7112a94634SJohn Fastabend }
7212a94634SJohn Fastabend
__hw_addr_add_ex(struct netdev_hw_addr_list * list,const unsigned char * addr,int addr_len,unsigned char addr_type,bool global,bool sync,int sync_count,bool exclusive)7312a94634SJohn Fastabend static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
746b6e2725Sstephen hemminger const unsigned char *addr, int addr_len,
756ef7b8a2SVlad Yasevich unsigned char addr_type, bool global, bool sync,
76406f42faSGilad Naaman int sync_count, bool exclusive)
7712a94634SJohn Fastabend {
78406f42faSGilad Naaman struct rb_node **ins_point = &list->tree.rb_node, *parent = NULL;
7912a94634SJohn Fastabend struct netdev_hw_addr *ha;
8012a94634SJohn Fastabend
81a748ee24SJiri Pirko if (addr_len > MAX_ADDR_LEN)
82a748ee24SJiri Pirko return -EINVAL;
83a748ee24SJiri Pirko
84406f42faSGilad Naaman while (*ins_point) {
85406f42faSGilad Naaman int diff;
86406f42faSGilad Naaman
87406f42faSGilad Naaman ha = rb_entry(*ins_point, struct netdev_hw_addr, node);
88406f42faSGilad Naaman diff = memcmp(addr, ha->addr, addr_len);
89406f42faSGilad Naaman if (diff == 0)
90406f42faSGilad Naaman diff = memcmp(&addr_type, &ha->type, sizeof(addr_type));
91406f42faSGilad Naaman
92406f42faSGilad Naaman parent = *ins_point;
93406f42faSGilad Naaman if (diff < 0) {
94406f42faSGilad Naaman ins_point = &parent->rb_left;
95406f42faSGilad Naaman } else if (diff > 0) {
96406f42faSGilad Naaman ins_point = &parent->rb_right;
97406f42faSGilad Naaman } else {
98406f42faSGilad Naaman if (exclusive)
99406f42faSGilad Naaman return -EEXIST;
10022bedad3SJiri Pirko if (global) {
10122bedad3SJiri Pirko /* check if addr is already used as global */
10222bedad3SJiri Pirko if (ha->global_use)
10322bedad3SJiri Pirko return 0;
10422bedad3SJiri Pirko else
10522bedad3SJiri Pirko ha->global_use = true;
10622bedad3SJiri Pirko }
1074cd729b0SVlad Yasevich if (sync) {
1086ef7b8a2SVlad Yasevich if (ha->synced && sync_count)
10929ca2f8fSJay Vosburgh return -EEXIST;
1104cd729b0SVlad Yasevich else
1116ef7b8a2SVlad Yasevich ha->synced++;
1124cd729b0SVlad Yasevich }
113a748ee24SJiri Pirko ha->refcount++;
114a748ee24SJiri Pirko return 0;
115a748ee24SJiri Pirko }
116a748ee24SJiri Pirko }
117a748ee24SJiri Pirko
118406f42faSGilad Naaman ha = __hw_addr_create(addr, addr_len, addr_type, global, sync);
119406f42faSGilad Naaman if (!ha)
120406f42faSGilad Naaman return -ENOMEM;
121406f42faSGilad Naaman
122406f42faSGilad Naaman rb_link_node(&ha->node, parent, ins_point);
123406f42faSGilad Naaman rb_insert_color(&ha->node, &list->tree);
124406f42faSGilad Naaman
125406f42faSGilad Naaman list_add_tail_rcu(&ha->list, &list->list);
126406f42faSGilad Naaman list->count++;
127406f42faSGilad Naaman
128406f42faSGilad Naaman return 0;
129a748ee24SJiri Pirko }
130a748ee24SJiri Pirko
__hw_addr_add(struct netdev_hw_addr_list * list,const unsigned char * addr,int addr_len,unsigned char addr_type)1316b6e2725Sstephen hemminger static int __hw_addr_add(struct netdev_hw_addr_list *list,
1326b6e2725Sstephen hemminger const unsigned char *addr, int addr_len,
1336b6e2725Sstephen hemminger unsigned char addr_type)
13422bedad3SJiri Pirko {
1356ef7b8a2SVlad Yasevich return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false,
136406f42faSGilad Naaman 0, false);
13722bedad3SJiri Pirko }
13822bedad3SJiri Pirko
__hw_addr_del_entry(struct netdev_hw_addr_list * list,struct netdev_hw_addr * ha,bool global,bool sync)1394cd729b0SVlad Yasevich static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
1404cd729b0SVlad Yasevich struct netdev_hw_addr *ha, bool global,
1414cd729b0SVlad Yasevich bool sync)
142a748ee24SJiri Pirko {
1434cd729b0SVlad Yasevich if (global && !ha->global_use)
1444cd729b0SVlad Yasevich return -ENOENT;
145a748ee24SJiri Pirko
1464cd729b0SVlad Yasevich if (sync && !ha->synced)
1474cd729b0SVlad Yasevich return -ENOENT;
1484cd729b0SVlad Yasevich
1494cd729b0SVlad Yasevich if (global)
15022bedad3SJiri Pirko ha->global_use = false;
1514cd729b0SVlad Yasevich
1524cd729b0SVlad Yasevich if (sync)
1536ef7b8a2SVlad Yasevich ha->synced--;
1544cd729b0SVlad Yasevich
155a748ee24SJiri Pirko if (--ha->refcount)
156a748ee24SJiri Pirko return 0;
157406f42faSGilad Naaman
158406f42faSGilad Naaman rb_erase(&ha->node, &list->tree);
159406f42faSGilad Naaman
160a748ee24SJiri Pirko list_del_rcu(&ha->list);
161217f1863SLai Jiangshan kfree_rcu(ha, rcu_head);
162a748ee24SJiri Pirko list->count--;
163a748ee24SJiri Pirko return 0;
164a748ee24SJiri Pirko }
1654cd729b0SVlad Yasevich
__hw_addr_lookup(struct netdev_hw_addr_list * list,const unsigned char * addr,int addr_len,unsigned char addr_type)166406f42faSGilad Naaman static struct netdev_hw_addr *__hw_addr_lookup(struct netdev_hw_addr_list *list,
167406f42faSGilad Naaman const unsigned char *addr, int addr_len,
168406f42faSGilad Naaman unsigned char addr_type)
169406f42faSGilad Naaman {
170406f42faSGilad Naaman struct rb_node *node;
171406f42faSGilad Naaman
172406f42faSGilad Naaman node = list->tree.rb_node;
173406f42faSGilad Naaman
174406f42faSGilad Naaman while (node) {
175406f42faSGilad Naaman struct netdev_hw_addr *ha = rb_entry(node, struct netdev_hw_addr, node);
176406f42faSGilad Naaman int diff = memcmp(addr, ha->addr, addr_len);
177406f42faSGilad Naaman
178406f42faSGilad Naaman if (diff == 0 && addr_type)
179406f42faSGilad Naaman diff = memcmp(&addr_type, &ha->type, sizeof(addr_type));
180406f42faSGilad Naaman
181406f42faSGilad Naaman if (diff < 0)
182406f42faSGilad Naaman node = node->rb_left;
183406f42faSGilad Naaman else if (diff > 0)
184406f42faSGilad Naaman node = node->rb_right;
185406f42faSGilad Naaman else
186406f42faSGilad Naaman return ha;
187406f42faSGilad Naaman }
188406f42faSGilad Naaman
189406f42faSGilad Naaman return NULL;
190406f42faSGilad Naaman }
191406f42faSGilad Naaman
__hw_addr_del_ex(struct netdev_hw_addr_list * list,const unsigned char * addr,int addr_len,unsigned char addr_type,bool global,bool sync)1924cd729b0SVlad Yasevich static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
1934cd729b0SVlad Yasevich const unsigned char *addr, int addr_len,
1944cd729b0SVlad Yasevich unsigned char addr_type, bool global, bool sync)
1954cd729b0SVlad Yasevich {
196406f42faSGilad Naaman struct netdev_hw_addr *ha = __hw_addr_lookup(list, addr, addr_len, addr_type);
1974cd729b0SVlad Yasevich
198406f42faSGilad Naaman if (!ha)
199a748ee24SJiri Pirko return -ENOENT;
200406f42faSGilad Naaman return __hw_addr_del_entry(list, ha, global, sync);
201a748ee24SJiri Pirko }
202a748ee24SJiri Pirko
__hw_addr_del(struct netdev_hw_addr_list * list,const unsigned char * addr,int addr_len,unsigned char addr_type)2036b6e2725Sstephen hemminger static int __hw_addr_del(struct netdev_hw_addr_list *list,
2046b6e2725Sstephen hemminger const unsigned char *addr, int addr_len,
2056b6e2725Sstephen hemminger unsigned char addr_type)
20622bedad3SJiri Pirko {
2074cd729b0SVlad Yasevich return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
2084cd729b0SVlad Yasevich }
2094cd729b0SVlad Yasevich
__hw_addr_sync_one(struct netdev_hw_addr_list * to_list,struct netdev_hw_addr * ha,int addr_len)2104cd729b0SVlad Yasevich static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
2114cd729b0SVlad Yasevich struct netdev_hw_addr *ha,
2124cd729b0SVlad Yasevich int addr_len)
2134cd729b0SVlad Yasevich {
2144cd729b0SVlad Yasevich int err;
2154cd729b0SVlad Yasevich
2164cd729b0SVlad Yasevich err = __hw_addr_add_ex(to_list, ha->addr, addr_len, ha->type,
217406f42faSGilad Naaman false, true, ha->sync_cnt, false);
21829ca2f8fSJay Vosburgh if (err && err != -EEXIST)
2194cd729b0SVlad Yasevich return err;
22029ca2f8fSJay Vosburgh
22129ca2f8fSJay Vosburgh if (!err) {
2224cd729b0SVlad Yasevich ha->sync_cnt++;
2234cd729b0SVlad Yasevich ha->refcount++;
22429ca2f8fSJay Vosburgh }
2254cd729b0SVlad Yasevich
2264cd729b0SVlad Yasevich return 0;
2274cd729b0SVlad Yasevich }
2284cd729b0SVlad Yasevich
__hw_addr_unsync_one(struct netdev_hw_addr_list * to_list,struct netdev_hw_addr_list * from_list,struct netdev_hw_addr * ha,int addr_len)2294cd729b0SVlad Yasevich static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
2304cd729b0SVlad Yasevich struct netdev_hw_addr_list *from_list,
2314cd729b0SVlad Yasevich struct netdev_hw_addr *ha,
2324cd729b0SVlad Yasevich int addr_len)
2334cd729b0SVlad Yasevich {
2344cd729b0SVlad Yasevich int err;
2354cd729b0SVlad Yasevich
2364cd729b0SVlad Yasevich err = __hw_addr_del_ex(to_list, ha->addr, addr_len, ha->type,
2374cd729b0SVlad Yasevich false, true);
2384cd729b0SVlad Yasevich if (err)
2394cd729b0SVlad Yasevich return;
2404cd729b0SVlad Yasevich ha->sync_cnt--;
24160ba834cSJay Vosburgh /* address on from list is not marked synced */
24260ba834cSJay Vosburgh __hw_addr_del_entry(from_list, ha, false, false);
2434cd729b0SVlad Yasevich }
2444cd729b0SVlad Yasevich
__hw_addr_sync_multiple(struct netdev_hw_addr_list * to_list,struct netdev_hw_addr_list * from_list,int addr_len)245*04508d20SMD Danish Anwar int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
2464cd729b0SVlad Yasevich struct netdev_hw_addr_list *from_list,
2474cd729b0SVlad Yasevich int addr_len)
2484cd729b0SVlad Yasevich {
2494cd729b0SVlad Yasevich int err = 0;
2504cd729b0SVlad Yasevich struct netdev_hw_addr *ha, *tmp;
2514cd729b0SVlad Yasevich
2524cd729b0SVlad Yasevich list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
2534cd729b0SVlad Yasevich if (ha->sync_cnt == ha->refcount) {
2544cd729b0SVlad Yasevich __hw_addr_unsync_one(to_list, from_list, ha, addr_len);
2554cd729b0SVlad Yasevich } else {
2564cd729b0SVlad Yasevich err = __hw_addr_sync_one(to_list, ha, addr_len);
2574cd729b0SVlad Yasevich if (err)
2584cd729b0SVlad Yasevich break;
2594cd729b0SVlad Yasevich }
2604cd729b0SVlad Yasevich }
2614cd729b0SVlad Yasevich return err;
26222bedad3SJiri Pirko }
263*04508d20SMD Danish Anwar EXPORT_SYMBOL(__hw_addr_sync_multiple);
26422bedad3SJiri Pirko
2654cd729b0SVlad Yasevich /* This function only works where there is a strict 1-1 relationship
266a8c924e9SSimon Horman * between source and destination of they synch. If you ever need to
2674cd729b0SVlad Yasevich * sync addresses to more then 1 destination, you need to use
2684cd729b0SVlad Yasevich * __hw_addr_sync_multiple().
2694cd729b0SVlad Yasevich */
__hw_addr_sync(struct netdev_hw_addr_list * to_list,struct netdev_hw_addr_list * from_list,int addr_len)27022bedad3SJiri Pirko int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
271a748ee24SJiri Pirko struct netdev_hw_addr_list *from_list,
272a748ee24SJiri Pirko int addr_len)
273a748ee24SJiri Pirko {
274a748ee24SJiri Pirko int err = 0;
275a748ee24SJiri Pirko struct netdev_hw_addr *ha, *tmp;
276a748ee24SJiri Pirko
277a748ee24SJiri Pirko list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
2784cd729b0SVlad Yasevich if (!ha->sync_cnt) {
2794cd729b0SVlad Yasevich err = __hw_addr_sync_one(to_list, ha, addr_len);
280a748ee24SJiri Pirko if (err)
281a748ee24SJiri Pirko break;
2824cd729b0SVlad Yasevich } else if (ha->refcount == 1)
2834cd729b0SVlad Yasevich __hw_addr_unsync_one(to_list, from_list, ha, addr_len);
284a748ee24SJiri Pirko }
285a748ee24SJiri Pirko return err;
286a748ee24SJiri Pirko }
28722bedad3SJiri Pirko EXPORT_SYMBOL(__hw_addr_sync);
288a748ee24SJiri Pirko
__hw_addr_unsync(struct netdev_hw_addr_list * to_list,struct netdev_hw_addr_list * from_list,int addr_len)28922bedad3SJiri Pirko void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
290a748ee24SJiri Pirko struct netdev_hw_addr_list *from_list,
291a748ee24SJiri Pirko int addr_len)
292a748ee24SJiri Pirko {
293a748ee24SJiri Pirko struct netdev_hw_addr *ha, *tmp;
294a748ee24SJiri Pirko
295a748ee24SJiri Pirko list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
2964cd729b0SVlad Yasevich if (ha->sync_cnt)
2974cd729b0SVlad Yasevich __hw_addr_unsync_one(to_list, from_list, ha, addr_len);
298a748ee24SJiri Pirko }
299a748ee24SJiri Pirko }
30022bedad3SJiri Pirko EXPORT_SYMBOL(__hw_addr_unsync);
301a748ee24SJiri Pirko
302670e5b8eSAlexander Duyck /**
303a8c924e9SSimon Horman * __hw_addr_sync_dev - Synchronize device's multicast list
304a8c924e9SSimon Horman * @list: address list to synchronize
305670e5b8eSAlexander Duyck * @dev: device to sync
306670e5b8eSAlexander Duyck * @sync: function to call if address should be added
307670e5b8eSAlexander Duyck * @unsync: function to call if address should be removed
308670e5b8eSAlexander Duyck *
309897b9faeSLu Wei * This function is intended to be called from the ndo_set_rx_mode
310670e5b8eSAlexander Duyck * function of devices that require explicit address add/remove
311670e5b8eSAlexander Duyck * notifications. The unsync function may be NULL in which case
312670e5b8eSAlexander Duyck * the addresses requiring removal will simply be removed without
313670e5b8eSAlexander Duyck * any notification to the device.
314670e5b8eSAlexander Duyck **/
__hw_addr_sync_dev(struct netdev_hw_addr_list * list,struct net_device * dev,int (* sync)(struct net_device *,const unsigned char *),int (* unsync)(struct net_device *,const unsigned char *))315670e5b8eSAlexander Duyck int __hw_addr_sync_dev(struct netdev_hw_addr_list *list,
316670e5b8eSAlexander Duyck struct net_device *dev,
317670e5b8eSAlexander Duyck int (*sync)(struct net_device *, const unsigned char *),
318670e5b8eSAlexander Duyck int (*unsync)(struct net_device *,
319670e5b8eSAlexander Duyck const unsigned char *))
320670e5b8eSAlexander Duyck {
321670e5b8eSAlexander Duyck struct netdev_hw_addr *ha, *tmp;
322670e5b8eSAlexander Duyck int err;
323670e5b8eSAlexander Duyck
324670e5b8eSAlexander Duyck /* first go through and flush out any stale entries */
325670e5b8eSAlexander Duyck list_for_each_entry_safe(ha, tmp, &list->list, list) {
326670e5b8eSAlexander Duyck if (!ha->sync_cnt || ha->refcount != 1)
327670e5b8eSAlexander Duyck continue;
328670e5b8eSAlexander Duyck
329670e5b8eSAlexander Duyck /* if unsync is defined and fails defer unsyncing address */
330670e5b8eSAlexander Duyck if (unsync && unsync(dev, ha->addr))
331670e5b8eSAlexander Duyck continue;
332670e5b8eSAlexander Duyck
333670e5b8eSAlexander Duyck ha->sync_cnt--;
334670e5b8eSAlexander Duyck __hw_addr_del_entry(list, ha, false, false);
335670e5b8eSAlexander Duyck }
336670e5b8eSAlexander Duyck
337670e5b8eSAlexander Duyck /* go through and sync new entries to the list */
338670e5b8eSAlexander Duyck list_for_each_entry_safe(ha, tmp, &list->list, list) {
339670e5b8eSAlexander Duyck if (ha->sync_cnt)
340670e5b8eSAlexander Duyck continue;
341670e5b8eSAlexander Duyck
342670e5b8eSAlexander Duyck err = sync(dev, ha->addr);
343670e5b8eSAlexander Duyck if (err)
344670e5b8eSAlexander Duyck return err;
345670e5b8eSAlexander Duyck
346670e5b8eSAlexander Duyck ha->sync_cnt++;
347670e5b8eSAlexander Duyck ha->refcount++;
348670e5b8eSAlexander Duyck }
349670e5b8eSAlexander Duyck
350670e5b8eSAlexander Duyck return 0;
351670e5b8eSAlexander Duyck }
352670e5b8eSAlexander Duyck EXPORT_SYMBOL(__hw_addr_sync_dev);
353670e5b8eSAlexander Duyck
354670e5b8eSAlexander Duyck /**
355e7946760SIvan Khoronzhuk * __hw_addr_ref_sync_dev - Synchronize device's multicast address list taking
356e7946760SIvan Khoronzhuk * into account references
357e7946760SIvan Khoronzhuk * @list: address list to synchronize
358e7946760SIvan Khoronzhuk * @dev: device to sync
359e7946760SIvan Khoronzhuk * @sync: function to call if address or reference on it should be added
360e7946760SIvan Khoronzhuk * @unsync: function to call if address or some reference on it should removed
361e7946760SIvan Khoronzhuk *
362e7946760SIvan Khoronzhuk * This function is intended to be called from the ndo_set_rx_mode
363e7946760SIvan Khoronzhuk * function of devices that require explicit address or references on it
364e7946760SIvan Khoronzhuk * add/remove notifications. The unsync function may be NULL in which case
365e7946760SIvan Khoronzhuk * the addresses or references on it requiring removal will simply be
366e7946760SIvan Khoronzhuk * removed without any notification to the device. That is responsibility of
367e7946760SIvan Khoronzhuk * the driver to identify and distribute address or references on it between
368e7946760SIvan Khoronzhuk * internal address tables.
369e7946760SIvan Khoronzhuk **/
__hw_addr_ref_sync_dev(struct netdev_hw_addr_list * list,struct net_device * dev,int (* sync)(struct net_device *,const unsigned char *,int),int (* unsync)(struct net_device *,const unsigned char *,int))370e7946760SIvan Khoronzhuk int __hw_addr_ref_sync_dev(struct netdev_hw_addr_list *list,
371e7946760SIvan Khoronzhuk struct net_device *dev,
372e7946760SIvan Khoronzhuk int (*sync)(struct net_device *,
373e7946760SIvan Khoronzhuk const unsigned char *, int),
374e7946760SIvan Khoronzhuk int (*unsync)(struct net_device *,
375e7946760SIvan Khoronzhuk const unsigned char *, int))
376e7946760SIvan Khoronzhuk {
377e7946760SIvan Khoronzhuk struct netdev_hw_addr *ha, *tmp;
378e7946760SIvan Khoronzhuk int err, ref_cnt;
379e7946760SIvan Khoronzhuk
380e7946760SIvan Khoronzhuk /* first go through and flush out any unsynced/stale entries */
381e7946760SIvan Khoronzhuk list_for_each_entry_safe(ha, tmp, &list->list, list) {
382e7946760SIvan Khoronzhuk /* sync if address is not used */
383e7946760SIvan Khoronzhuk if ((ha->sync_cnt << 1) <= ha->refcount)
384e7946760SIvan Khoronzhuk continue;
385e7946760SIvan Khoronzhuk
386e7946760SIvan Khoronzhuk /* if fails defer unsyncing address */
387e7946760SIvan Khoronzhuk ref_cnt = ha->refcount - ha->sync_cnt;
388e7946760SIvan Khoronzhuk if (unsync && unsync(dev, ha->addr, ref_cnt))
389e7946760SIvan Khoronzhuk continue;
390e7946760SIvan Khoronzhuk
391e7946760SIvan Khoronzhuk ha->refcount = (ref_cnt << 1) + 1;
392e7946760SIvan Khoronzhuk ha->sync_cnt = ref_cnt;
393e7946760SIvan Khoronzhuk __hw_addr_del_entry(list, ha, false, false);
394e7946760SIvan Khoronzhuk }
395e7946760SIvan Khoronzhuk
396e7946760SIvan Khoronzhuk /* go through and sync updated/new entries to the list */
397e7946760SIvan Khoronzhuk list_for_each_entry_safe(ha, tmp, &list->list, list) {
398e7946760SIvan Khoronzhuk /* sync if address added or reused */
399e7946760SIvan Khoronzhuk if ((ha->sync_cnt << 1) >= ha->refcount)
400e7946760SIvan Khoronzhuk continue;
401e7946760SIvan Khoronzhuk
402e7946760SIvan Khoronzhuk ref_cnt = ha->refcount - ha->sync_cnt;
403e7946760SIvan Khoronzhuk err = sync(dev, ha->addr, ref_cnt);
404e7946760SIvan Khoronzhuk if (err)
405e7946760SIvan Khoronzhuk return err;
406e7946760SIvan Khoronzhuk
407e7946760SIvan Khoronzhuk ha->refcount = ref_cnt << 1;
408e7946760SIvan Khoronzhuk ha->sync_cnt = ref_cnt;
409e7946760SIvan Khoronzhuk }
410e7946760SIvan Khoronzhuk
411e7946760SIvan Khoronzhuk return 0;
412e7946760SIvan Khoronzhuk }
413e7946760SIvan Khoronzhuk EXPORT_SYMBOL(__hw_addr_ref_sync_dev);
414e7946760SIvan Khoronzhuk
415e7946760SIvan Khoronzhuk /**
416e7946760SIvan Khoronzhuk * __hw_addr_ref_unsync_dev - Remove synchronized addresses and references on
417e7946760SIvan Khoronzhuk * it from device
418e7946760SIvan Khoronzhuk * @list: address list to remove synchronized addresses (references on it) from
419e7946760SIvan Khoronzhuk * @dev: device to sync
420e7946760SIvan Khoronzhuk * @unsync: function to call if address and references on it should be removed
421e7946760SIvan Khoronzhuk *
422e7946760SIvan Khoronzhuk * Remove all addresses that were added to the device by
423e7946760SIvan Khoronzhuk * __hw_addr_ref_sync_dev(). This function is intended to be called from the
424e7946760SIvan Khoronzhuk * ndo_stop or ndo_open functions on devices that require explicit address (or
425e7946760SIvan Khoronzhuk * references on it) add/remove notifications. If the unsync function pointer
426e7946760SIvan Khoronzhuk * is NULL then this function can be used to just reset the sync_cnt for the
427e7946760SIvan Khoronzhuk * addresses in the list.
428e7946760SIvan Khoronzhuk **/
__hw_addr_ref_unsync_dev(struct netdev_hw_addr_list * list,struct net_device * dev,int (* unsync)(struct net_device *,const unsigned char *,int))429e7946760SIvan Khoronzhuk void __hw_addr_ref_unsync_dev(struct netdev_hw_addr_list *list,
430e7946760SIvan Khoronzhuk struct net_device *dev,
431e7946760SIvan Khoronzhuk int (*unsync)(struct net_device *,
432e7946760SIvan Khoronzhuk const unsigned char *, int))
433e7946760SIvan Khoronzhuk {
434e7946760SIvan Khoronzhuk struct netdev_hw_addr *ha, *tmp;
435e7946760SIvan Khoronzhuk
436e7946760SIvan Khoronzhuk list_for_each_entry_safe(ha, tmp, &list->list, list) {
437e7946760SIvan Khoronzhuk if (!ha->sync_cnt)
438e7946760SIvan Khoronzhuk continue;
439e7946760SIvan Khoronzhuk
440e7946760SIvan Khoronzhuk /* if fails defer unsyncing address */
441e7946760SIvan Khoronzhuk if (unsync && unsync(dev, ha->addr, ha->sync_cnt))
442e7946760SIvan Khoronzhuk continue;
443e7946760SIvan Khoronzhuk
444e7946760SIvan Khoronzhuk ha->refcount -= ha->sync_cnt - 1;
445e7946760SIvan Khoronzhuk ha->sync_cnt = 0;
446e7946760SIvan Khoronzhuk __hw_addr_del_entry(list, ha, false, false);
447e7946760SIvan Khoronzhuk }
448e7946760SIvan Khoronzhuk }
449e7946760SIvan Khoronzhuk EXPORT_SYMBOL(__hw_addr_ref_unsync_dev);
450e7946760SIvan Khoronzhuk
451e7946760SIvan Khoronzhuk /**
4521d2398dcSFabian Frederick * __hw_addr_unsync_dev - Remove synchronized addresses from device
4531d2398dcSFabian Frederick * @list: address list to remove synchronized addresses from
454670e5b8eSAlexander Duyck * @dev: device to sync
455670e5b8eSAlexander Duyck * @unsync: function to call if address should be removed
456670e5b8eSAlexander Duyck *
457670e5b8eSAlexander Duyck * Remove all addresses that were added to the device by __hw_addr_sync_dev().
458670e5b8eSAlexander Duyck * This function is intended to be called from the ndo_stop or ndo_open
459670e5b8eSAlexander Duyck * functions on devices that require explicit address add/remove
460670e5b8eSAlexander Duyck * notifications. If the unsync function pointer is NULL then this function
461670e5b8eSAlexander Duyck * can be used to just reset the sync_cnt for the addresses in the list.
462670e5b8eSAlexander Duyck **/
__hw_addr_unsync_dev(struct netdev_hw_addr_list * list,struct net_device * dev,int (* unsync)(struct net_device *,const unsigned char *))463670e5b8eSAlexander Duyck void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list,
464670e5b8eSAlexander Duyck struct net_device *dev,
465670e5b8eSAlexander Duyck int (*unsync)(struct net_device *,
466670e5b8eSAlexander Duyck const unsigned char *))
467670e5b8eSAlexander Duyck {
468670e5b8eSAlexander Duyck struct netdev_hw_addr *ha, *tmp;
469670e5b8eSAlexander Duyck
470670e5b8eSAlexander Duyck list_for_each_entry_safe(ha, tmp, &list->list, list) {
471670e5b8eSAlexander Duyck if (!ha->sync_cnt)
472670e5b8eSAlexander Duyck continue;
473670e5b8eSAlexander Duyck
474670e5b8eSAlexander Duyck /* if unsync is defined and fails defer unsyncing address */
475670e5b8eSAlexander Duyck if (unsync && unsync(dev, ha->addr))
476670e5b8eSAlexander Duyck continue;
477670e5b8eSAlexander Duyck
478670e5b8eSAlexander Duyck ha->sync_cnt--;
479670e5b8eSAlexander Duyck __hw_addr_del_entry(list, ha, false, false);
480670e5b8eSAlexander Duyck }
481670e5b8eSAlexander Duyck }
482670e5b8eSAlexander Duyck EXPORT_SYMBOL(__hw_addr_unsync_dev);
483670e5b8eSAlexander Duyck
__hw_addr_flush(struct netdev_hw_addr_list * list)484477bb933Sstephen hemminger static void __hw_addr_flush(struct netdev_hw_addr_list *list)
485a748ee24SJiri Pirko {
486a748ee24SJiri Pirko struct netdev_hw_addr *ha, *tmp;
487a748ee24SJiri Pirko
488406f42faSGilad Naaman list->tree = RB_ROOT;
489a748ee24SJiri Pirko list_for_each_entry_safe(ha, tmp, &list->list, list) {
490a748ee24SJiri Pirko list_del_rcu(&ha->list);
491217f1863SLai Jiangshan kfree_rcu(ha, rcu_head);
492a748ee24SJiri Pirko }
493a748ee24SJiri Pirko list->count = 0;
494a748ee24SJiri Pirko }
495a748ee24SJiri Pirko
__hw_addr_init(struct netdev_hw_addr_list * list)49622bedad3SJiri Pirko void __hw_addr_init(struct netdev_hw_addr_list *list)
497a748ee24SJiri Pirko {
498a748ee24SJiri Pirko INIT_LIST_HEAD(&list->list);
499a748ee24SJiri Pirko list->count = 0;
500406f42faSGilad Naaman list->tree = RB_ROOT;
501a748ee24SJiri Pirko }
50222bedad3SJiri Pirko EXPORT_SYMBOL(__hw_addr_init);
503a748ee24SJiri Pirko
504a748ee24SJiri Pirko /*
505a748ee24SJiri Pirko * Device addresses handling functions
506a748ee24SJiri Pirko */
507a748ee24SJiri Pirko
508d07b26f5SJakub Kicinski /* Check that netdev->dev_addr is not written to directly as this would
509d07b26f5SJakub Kicinski * break the rbtree layout. All changes should go thru dev_addr_set() and co.
510d07b26f5SJakub Kicinski * Remove this check in mid-2024.
511d07b26f5SJakub Kicinski */
dev_addr_check(struct net_device * dev)512d07b26f5SJakub Kicinski void dev_addr_check(struct net_device *dev)
513d07b26f5SJakub Kicinski {
514d07b26f5SJakub Kicinski if (!memcmp(dev->dev_addr, dev->dev_addr_shadow, MAX_ADDR_LEN))
515d07b26f5SJakub Kicinski return;
516d07b26f5SJakub Kicinski
517d07b26f5SJakub Kicinski netdev_warn(dev, "Current addr: %*ph\n", MAX_ADDR_LEN, dev->dev_addr);
518d07b26f5SJakub Kicinski netdev_warn(dev, "Expected addr: %*ph\n",
519d07b26f5SJakub Kicinski MAX_ADDR_LEN, dev->dev_addr_shadow);
520d07b26f5SJakub Kicinski netdev_WARN(dev, "Incorrect netdev->dev_addr\n");
521d07b26f5SJakub Kicinski }
522d07b26f5SJakub Kicinski
523a748ee24SJiri Pirko /**
524a748ee24SJiri Pirko * dev_addr_flush - Flush device address list
525a748ee24SJiri Pirko * @dev: device
526a748ee24SJiri Pirko *
527a748ee24SJiri Pirko * Flush device address list and reset ->dev_addr.
528a748ee24SJiri Pirko *
529a748ee24SJiri Pirko * The caller must hold the rtnl_mutex.
530a748ee24SJiri Pirko */
dev_addr_flush(struct net_device * dev)531a748ee24SJiri Pirko void dev_addr_flush(struct net_device *dev)
532a748ee24SJiri Pirko {
533a748ee24SJiri Pirko /* rtnl_mutex must be held here */
534d07b26f5SJakub Kicinski dev_addr_check(dev);
535a748ee24SJiri Pirko
536a748ee24SJiri Pirko __hw_addr_flush(&dev->dev_addrs);
537a748ee24SJiri Pirko dev->dev_addr = NULL;
538a748ee24SJiri Pirko }
539a748ee24SJiri Pirko
540a748ee24SJiri Pirko /**
541a748ee24SJiri Pirko * dev_addr_init - Init device address list
542a748ee24SJiri Pirko * @dev: device
543a748ee24SJiri Pirko *
544a748ee24SJiri Pirko * Init device address list and create the first element,
545a748ee24SJiri Pirko * used by ->dev_addr.
546a748ee24SJiri Pirko *
547a748ee24SJiri Pirko * The caller must hold the rtnl_mutex.
548a748ee24SJiri Pirko */
dev_addr_init(struct net_device * dev)549a748ee24SJiri Pirko int dev_addr_init(struct net_device *dev)
550a748ee24SJiri Pirko {
551a748ee24SJiri Pirko unsigned char addr[MAX_ADDR_LEN];
552a748ee24SJiri Pirko struct netdev_hw_addr *ha;
553a748ee24SJiri Pirko int err;
554a748ee24SJiri Pirko
555a748ee24SJiri Pirko /* rtnl_mutex must be held here */
556a748ee24SJiri Pirko
557a748ee24SJiri Pirko __hw_addr_init(&dev->dev_addrs);
558a748ee24SJiri Pirko memset(addr, 0, sizeof(addr));
559a748ee24SJiri Pirko err = __hw_addr_add(&dev->dev_addrs, addr, sizeof(addr),
560a748ee24SJiri Pirko NETDEV_HW_ADDR_T_LAN);
561a748ee24SJiri Pirko if (!err) {
562a748ee24SJiri Pirko /*
563a748ee24SJiri Pirko * Get the first (previously created) address from the list
564a748ee24SJiri Pirko * and set dev_addr pointer to this location.
565a748ee24SJiri Pirko */
566a748ee24SJiri Pirko ha = list_first_entry(&dev->dev_addrs.list,
567a748ee24SJiri Pirko struct netdev_hw_addr, list);
568a748ee24SJiri Pirko dev->dev_addr = ha->addr;
569a748ee24SJiri Pirko }
570a748ee24SJiri Pirko return err;
571a748ee24SJiri Pirko }
572a748ee24SJiri Pirko
dev_addr_mod(struct net_device * dev,unsigned int offset,const void * addr,size_t len)573adeef3e3SJakub Kicinski void dev_addr_mod(struct net_device *dev, unsigned int offset,
574adeef3e3SJakub Kicinski const void *addr, size_t len)
575adeef3e3SJakub Kicinski {
576adeef3e3SJakub Kicinski struct netdev_hw_addr *ha;
577adeef3e3SJakub Kicinski
578d07b26f5SJakub Kicinski dev_addr_check(dev);
579d07b26f5SJakub Kicinski
580adeef3e3SJakub Kicinski ha = container_of(dev->dev_addr, struct netdev_hw_addr, addr[0]);
581a387ff8eSJakub Kicinski rb_erase(&ha->node, &dev->dev_addrs.tree);
582adeef3e3SJakub Kicinski memcpy(&ha->addr[offset], addr, len);
583d07b26f5SJakub Kicinski memcpy(&dev->dev_addr_shadow[offset], addr, len);
584a387ff8eSJakub Kicinski WARN_ON(__hw_addr_insert(&dev->dev_addrs, ha, dev->addr_len));
585adeef3e3SJakub Kicinski }
586adeef3e3SJakub Kicinski EXPORT_SYMBOL(dev_addr_mod);
587adeef3e3SJakub Kicinski
588a748ee24SJiri Pirko /**
589a748ee24SJiri Pirko * dev_addr_add - Add a device address
590a748ee24SJiri Pirko * @dev: device
591a748ee24SJiri Pirko * @addr: address to add
592a748ee24SJiri Pirko * @addr_type: address type
593a748ee24SJiri Pirko *
594a748ee24SJiri Pirko * Add a device address to the device or increase the reference count if
595a748ee24SJiri Pirko * it already exists.
596a748ee24SJiri Pirko *
597a748ee24SJiri Pirko * The caller must hold the rtnl_mutex.
598a748ee24SJiri Pirko */
dev_addr_add(struct net_device * dev,const unsigned char * addr,unsigned char addr_type)5996b6e2725Sstephen hemminger int dev_addr_add(struct net_device *dev, const unsigned char *addr,
600a748ee24SJiri Pirko unsigned char addr_type)
601a748ee24SJiri Pirko {
602a748ee24SJiri Pirko int err;
603a748ee24SJiri Pirko
604a748ee24SJiri Pirko ASSERT_RTNL();
605a748ee24SJiri Pirko
606d59cdf94SPetr Machata err = dev_pre_changeaddr_notify(dev, addr, NULL);
607d59cdf94SPetr Machata if (err)
608d59cdf94SPetr Machata return err;
609a748ee24SJiri Pirko err = __hw_addr_add(&dev->dev_addrs, addr, dev->addr_len, addr_type);
610a748ee24SJiri Pirko if (!err)
611a748ee24SJiri Pirko call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
612a748ee24SJiri Pirko return err;
613a748ee24SJiri Pirko }
614a748ee24SJiri Pirko EXPORT_SYMBOL(dev_addr_add);
615a748ee24SJiri Pirko
616a748ee24SJiri Pirko /**
617a748ee24SJiri Pirko * dev_addr_del - Release a device address.
618a748ee24SJiri Pirko * @dev: device
619a748ee24SJiri Pirko * @addr: address to delete
620a748ee24SJiri Pirko * @addr_type: address type
621a748ee24SJiri Pirko *
622a748ee24SJiri Pirko * Release reference to a device address and remove it from the device
623a748ee24SJiri Pirko * if the reference count drops to zero.
624a748ee24SJiri Pirko *
625a748ee24SJiri Pirko * The caller must hold the rtnl_mutex.
626a748ee24SJiri Pirko */
dev_addr_del(struct net_device * dev,const unsigned char * addr,unsigned char addr_type)6276b6e2725Sstephen hemminger int dev_addr_del(struct net_device *dev, const unsigned char *addr,
628a748ee24SJiri Pirko unsigned char addr_type)
629a748ee24SJiri Pirko {
630a748ee24SJiri Pirko int err;
631a748ee24SJiri Pirko struct netdev_hw_addr *ha;
632a748ee24SJiri Pirko
633a748ee24SJiri Pirko ASSERT_RTNL();
634a748ee24SJiri Pirko
635a748ee24SJiri Pirko /*
636a748ee24SJiri Pirko * We can not remove the first address from the list because
637a748ee24SJiri Pirko * dev->dev_addr points to that.
638a748ee24SJiri Pirko */
639a748ee24SJiri Pirko ha = list_first_entry(&dev->dev_addrs.list,
640a748ee24SJiri Pirko struct netdev_hw_addr, list);
641a652208eSJiri Pirko if (!memcmp(ha->addr, addr, dev->addr_len) &&
642a652208eSJiri Pirko ha->type == addr_type && ha->refcount == 1)
643a748ee24SJiri Pirko return -ENOENT;
644a748ee24SJiri Pirko
645a748ee24SJiri Pirko err = __hw_addr_del(&dev->dev_addrs, addr, dev->addr_len,
646a748ee24SJiri Pirko addr_type);
647a748ee24SJiri Pirko if (!err)
648a748ee24SJiri Pirko call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
649a748ee24SJiri Pirko return err;
650a748ee24SJiri Pirko }
651a748ee24SJiri Pirko EXPORT_SYMBOL(dev_addr_del);
652a748ee24SJiri Pirko
653a748ee24SJiri Pirko /*
654a748ee24SJiri Pirko * Unicast list handling functions
655a748ee24SJiri Pirko */
656a748ee24SJiri Pirko
657a748ee24SJiri Pirko /**
65812a94634SJohn Fastabend * dev_uc_add_excl - Add a global secondary unicast address
65912a94634SJohn Fastabend * @dev: device
66012a94634SJohn Fastabend * @addr: address to add
66112a94634SJohn Fastabend */
dev_uc_add_excl(struct net_device * dev,const unsigned char * addr)6626b6e2725Sstephen hemminger int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr)
66312a94634SJohn Fastabend {
66412a94634SJohn Fastabend int err;
66512a94634SJohn Fastabend
66612a94634SJohn Fastabend netif_addr_lock_bh(dev);
667406f42faSGilad Naaman err = __hw_addr_add_ex(&dev->uc, addr, dev->addr_len,
668406f42faSGilad Naaman NETDEV_HW_ADDR_T_UNICAST, true, false,
669406f42faSGilad Naaman 0, true);
67012a94634SJohn Fastabend if (!err)
67112a94634SJohn Fastabend __dev_set_rx_mode(dev);
67212a94634SJohn Fastabend netif_addr_unlock_bh(dev);
67312a94634SJohn Fastabend return err;
67412a94634SJohn Fastabend }
67512a94634SJohn Fastabend EXPORT_SYMBOL(dev_uc_add_excl);
67612a94634SJohn Fastabend
67712a94634SJohn Fastabend /**
678a748ee24SJiri Pirko * dev_uc_add - Add a secondary unicast address
679a748ee24SJiri Pirko * @dev: device
680a748ee24SJiri Pirko * @addr: address to add
681a748ee24SJiri Pirko *
682a748ee24SJiri Pirko * Add a secondary unicast address to the device or increase
683a748ee24SJiri Pirko * the reference count if it already exists.
684a748ee24SJiri Pirko */
dev_uc_add(struct net_device * dev,const unsigned char * addr)6856b6e2725Sstephen hemminger int dev_uc_add(struct net_device *dev, const unsigned char *addr)
686a748ee24SJiri Pirko {
687a748ee24SJiri Pirko int err;
688a748ee24SJiri Pirko
689a748ee24SJiri Pirko netif_addr_lock_bh(dev);
690a748ee24SJiri Pirko err = __hw_addr_add(&dev->uc, addr, dev->addr_len,
691a748ee24SJiri Pirko NETDEV_HW_ADDR_T_UNICAST);
692a748ee24SJiri Pirko if (!err)
693a748ee24SJiri Pirko __dev_set_rx_mode(dev);
694a748ee24SJiri Pirko netif_addr_unlock_bh(dev);
695a748ee24SJiri Pirko return err;
696a748ee24SJiri Pirko }
697a748ee24SJiri Pirko EXPORT_SYMBOL(dev_uc_add);
698a748ee24SJiri Pirko
699a748ee24SJiri Pirko /**
700a748ee24SJiri Pirko * dev_uc_del - Release secondary unicast address.
701a748ee24SJiri Pirko * @dev: device
702a748ee24SJiri Pirko * @addr: address to delete
703a748ee24SJiri Pirko *
704a748ee24SJiri Pirko * Release reference to a secondary unicast address and remove it
705a748ee24SJiri Pirko * from the device if the reference count drops to zero.
706a748ee24SJiri Pirko */
dev_uc_del(struct net_device * dev,const unsigned char * addr)7076b6e2725Sstephen hemminger int dev_uc_del(struct net_device *dev, const unsigned char *addr)
708a748ee24SJiri Pirko {
709a748ee24SJiri Pirko int err;
710a748ee24SJiri Pirko
711a748ee24SJiri Pirko netif_addr_lock_bh(dev);
712a748ee24SJiri Pirko err = __hw_addr_del(&dev->uc, addr, dev->addr_len,
713a748ee24SJiri Pirko NETDEV_HW_ADDR_T_UNICAST);
714a748ee24SJiri Pirko if (!err)
715a748ee24SJiri Pirko __dev_set_rx_mode(dev);
716a748ee24SJiri Pirko netif_addr_unlock_bh(dev);
717a748ee24SJiri Pirko return err;
718a748ee24SJiri Pirko }
719a748ee24SJiri Pirko EXPORT_SYMBOL(dev_uc_del);
720a748ee24SJiri Pirko
721a748ee24SJiri Pirko /**
722a748ee24SJiri Pirko * dev_uc_sync - Synchronize device's unicast list to another device
723a748ee24SJiri Pirko * @to: destination device
724a748ee24SJiri Pirko * @from: source device
725a748ee24SJiri Pirko *
726a748ee24SJiri Pirko * Add newly added addresses to the destination device and release
727a748ee24SJiri Pirko * addresses that have no users left. The source device must be
728ab16ebf3SJiri Pirko * locked by netif_addr_lock_bh.
729a748ee24SJiri Pirko *
730a748ee24SJiri Pirko * This function is intended to be called from the dev->set_rx_mode
7314cd729b0SVlad Yasevich * function of layered software devices. This function assumes that
7324cd729b0SVlad Yasevich * addresses will only ever be synced to the @to devices and no other.
733a748ee24SJiri Pirko */
dev_uc_sync(struct net_device * to,struct net_device * from)734a748ee24SJiri Pirko int dev_uc_sync(struct net_device *to, struct net_device *from)
735a748ee24SJiri Pirko {
736a748ee24SJiri Pirko int err = 0;
737a748ee24SJiri Pirko
738a748ee24SJiri Pirko if (to->addr_len != from->addr_len)
739a748ee24SJiri Pirko return -EINVAL;
740a748ee24SJiri Pirko
7411fc70edbSTaehee Yoo netif_addr_lock(to);
742a748ee24SJiri Pirko err = __hw_addr_sync(&to->uc, &from->uc, to->addr_len);
743a748ee24SJiri Pirko if (!err)
744a748ee24SJiri Pirko __dev_set_rx_mode(to);
7452429f7acSJiri Pirko netif_addr_unlock(to);
746a748ee24SJiri Pirko return err;
747a748ee24SJiri Pirko }
748a748ee24SJiri Pirko EXPORT_SYMBOL(dev_uc_sync);
749a748ee24SJiri Pirko
750a748ee24SJiri Pirko /**
7514cd729b0SVlad Yasevich * dev_uc_sync_multiple - Synchronize device's unicast list to another
7524cd729b0SVlad Yasevich * device, but allow for multiple calls to sync to multiple devices.
7534cd729b0SVlad Yasevich * @to: destination device
7544cd729b0SVlad Yasevich * @from: source device
7554cd729b0SVlad Yasevich *
7564cd729b0SVlad Yasevich * Add newly added addresses to the destination device and release
7574cd729b0SVlad Yasevich * addresses that have been deleted from the source. The source device
7584cd729b0SVlad Yasevich * must be locked by netif_addr_lock_bh.
7594cd729b0SVlad Yasevich *
7604cd729b0SVlad Yasevich * This function is intended to be called from the dev->set_rx_mode
7614cd729b0SVlad Yasevich * function of layered software devices. It allows for a single source
7624cd729b0SVlad Yasevich * device to be synced to multiple destination devices.
7634cd729b0SVlad Yasevich */
dev_uc_sync_multiple(struct net_device * to,struct net_device * from)7644cd729b0SVlad Yasevich int dev_uc_sync_multiple(struct net_device *to, struct net_device *from)
7654cd729b0SVlad Yasevich {
7664cd729b0SVlad Yasevich int err = 0;
7674cd729b0SVlad Yasevich
7684cd729b0SVlad Yasevich if (to->addr_len != from->addr_len)
7694cd729b0SVlad Yasevich return -EINVAL;
7704cd729b0SVlad Yasevich
7711fc70edbSTaehee Yoo netif_addr_lock(to);
7724cd729b0SVlad Yasevich err = __hw_addr_sync_multiple(&to->uc, &from->uc, to->addr_len);
7734cd729b0SVlad Yasevich if (!err)
7744cd729b0SVlad Yasevich __dev_set_rx_mode(to);
7754cd729b0SVlad Yasevich netif_addr_unlock(to);
7764cd729b0SVlad Yasevich return err;
7774cd729b0SVlad Yasevich }
7784cd729b0SVlad Yasevich EXPORT_SYMBOL(dev_uc_sync_multiple);
7794cd729b0SVlad Yasevich
7804cd729b0SVlad Yasevich /**
781a748ee24SJiri Pirko * dev_uc_unsync - Remove synchronized addresses from the destination device
782a748ee24SJiri Pirko * @to: destination device
783a748ee24SJiri Pirko * @from: source device
784a748ee24SJiri Pirko *
785a748ee24SJiri Pirko * Remove all addresses that were added to the destination device by
786a748ee24SJiri Pirko * dev_uc_sync(). This function is intended to be called from the
787a748ee24SJiri Pirko * dev->stop function of layered software devices.
788a748ee24SJiri Pirko */
dev_uc_unsync(struct net_device * to,struct net_device * from)789a748ee24SJiri Pirko void dev_uc_unsync(struct net_device *to, struct net_device *from)
790a748ee24SJiri Pirko {
791a748ee24SJiri Pirko if (to->addr_len != from->addr_len)
792a748ee24SJiri Pirko return;
793a748ee24SJiri Pirko
794e8280338SCong Wang /* netif_addr_lock_bh() uses lockdep subclass 0, this is okay for two
795e8280338SCong Wang * reasons:
796e8280338SCong Wang * 1) This is always called without any addr_list_lock, so as the
797e8280338SCong Wang * outermost one here, it must be 0.
798e8280338SCong Wang * 2) This is called by some callers after unlinking the upper device,
799e8280338SCong Wang * so the dev->lower_level becomes 1 again.
800e8280338SCong Wang * Therefore, the subclass for 'from' is 0, for 'to' is either 1 or
801e8280338SCong Wang * larger.
802e8280338SCong Wang */
803a748ee24SJiri Pirko netif_addr_lock_bh(from);
8041fc70edbSTaehee Yoo netif_addr_lock(to);
805a748ee24SJiri Pirko __hw_addr_unsync(&to->uc, &from->uc, to->addr_len);
806a748ee24SJiri Pirko __dev_set_rx_mode(to);
807a748ee24SJiri Pirko netif_addr_unlock(to);
808a748ee24SJiri Pirko netif_addr_unlock_bh(from);
809a748ee24SJiri Pirko }
810a748ee24SJiri Pirko EXPORT_SYMBOL(dev_uc_unsync);
811a748ee24SJiri Pirko
812a748ee24SJiri Pirko /**
813a748ee24SJiri Pirko * dev_uc_flush - Flush unicast addresses
814a748ee24SJiri Pirko * @dev: device
815a748ee24SJiri Pirko *
816a748ee24SJiri Pirko * Flush unicast addresses.
817a748ee24SJiri Pirko */
dev_uc_flush(struct net_device * dev)818a748ee24SJiri Pirko void dev_uc_flush(struct net_device *dev)
819a748ee24SJiri Pirko {
820a748ee24SJiri Pirko netif_addr_lock_bh(dev);
821a748ee24SJiri Pirko __hw_addr_flush(&dev->uc);
822a748ee24SJiri Pirko netif_addr_unlock_bh(dev);
823a748ee24SJiri Pirko }
824a748ee24SJiri Pirko EXPORT_SYMBOL(dev_uc_flush);
825a748ee24SJiri Pirko
826a748ee24SJiri Pirko /**
827af825087SXiongfeng Wang * dev_uc_init - Init unicast address list
828a748ee24SJiri Pirko * @dev: device
829a748ee24SJiri Pirko *
830a748ee24SJiri Pirko * Init unicast address list.
831a748ee24SJiri Pirko */
dev_uc_init(struct net_device * dev)832a748ee24SJiri Pirko void dev_uc_init(struct net_device *dev)
833a748ee24SJiri Pirko {
834a748ee24SJiri Pirko __hw_addr_init(&dev->uc);
835a748ee24SJiri Pirko }
836a748ee24SJiri Pirko EXPORT_SYMBOL(dev_uc_init);
837a748ee24SJiri Pirko
838a748ee24SJiri Pirko /*
839a748ee24SJiri Pirko * Multicast list handling functions
840a748ee24SJiri Pirko */
841a748ee24SJiri Pirko
84212a94634SJohn Fastabend /**
84312a94634SJohn Fastabend * dev_mc_add_excl - Add a global secondary multicast address
84412a94634SJohn Fastabend * @dev: device
84512a94634SJohn Fastabend * @addr: address to add
84612a94634SJohn Fastabend */
dev_mc_add_excl(struct net_device * dev,const unsigned char * addr)8476b6e2725Sstephen hemminger int dev_mc_add_excl(struct net_device *dev, const unsigned char *addr)
84812a94634SJohn Fastabend {
84912a94634SJohn Fastabend int err;
85012a94634SJohn Fastabend
85112a94634SJohn Fastabend netif_addr_lock_bh(dev);
852406f42faSGilad Naaman err = __hw_addr_add_ex(&dev->mc, addr, dev->addr_len,
853406f42faSGilad Naaman NETDEV_HW_ADDR_T_MULTICAST, true, false,
854406f42faSGilad Naaman 0, true);
85512a94634SJohn Fastabend if (!err)
85612a94634SJohn Fastabend __dev_set_rx_mode(dev);
85712a94634SJohn Fastabend netif_addr_unlock_bh(dev);
85812a94634SJohn Fastabend return err;
85912a94634SJohn Fastabend }
86012a94634SJohn Fastabend EXPORT_SYMBOL(dev_mc_add_excl);
86112a94634SJohn Fastabend
__dev_mc_add(struct net_device * dev,const unsigned char * addr,bool global)8626b6e2725Sstephen hemminger static int __dev_mc_add(struct net_device *dev, const unsigned char *addr,
86322bedad3SJiri Pirko bool global)
86422bedad3SJiri Pirko {
86522bedad3SJiri Pirko int err;
86622bedad3SJiri Pirko
86722bedad3SJiri Pirko netif_addr_lock_bh(dev);
86822bedad3SJiri Pirko err = __hw_addr_add_ex(&dev->mc, addr, dev->addr_len,
869406f42faSGilad Naaman NETDEV_HW_ADDR_T_MULTICAST, global, false,
870406f42faSGilad Naaman 0, false);
87122bedad3SJiri Pirko if (!err)
87222bedad3SJiri Pirko __dev_set_rx_mode(dev);
87322bedad3SJiri Pirko netif_addr_unlock_bh(dev);
87422bedad3SJiri Pirko return err;
87522bedad3SJiri Pirko }
87622bedad3SJiri Pirko /**
87722bedad3SJiri Pirko * dev_mc_add - Add a multicast address
87822bedad3SJiri Pirko * @dev: device
87922bedad3SJiri Pirko * @addr: address to add
88022bedad3SJiri Pirko *
88122bedad3SJiri Pirko * Add a multicast address to the device or increase
88222bedad3SJiri Pirko * the reference count if it already exists.
88322bedad3SJiri Pirko */
dev_mc_add(struct net_device * dev,const unsigned char * addr)8846b6e2725Sstephen hemminger int dev_mc_add(struct net_device *dev, const unsigned char *addr)
88522bedad3SJiri Pirko {
88622bedad3SJiri Pirko return __dev_mc_add(dev, addr, false);
88722bedad3SJiri Pirko }
88822bedad3SJiri Pirko EXPORT_SYMBOL(dev_mc_add);
88922bedad3SJiri Pirko
89022bedad3SJiri Pirko /**
89122bedad3SJiri Pirko * dev_mc_add_global - Add a global multicast address
89222bedad3SJiri Pirko * @dev: device
89322bedad3SJiri Pirko * @addr: address to add
89422bedad3SJiri Pirko *
89522bedad3SJiri Pirko * Add a global multicast address to the device.
89622bedad3SJiri Pirko */
dev_mc_add_global(struct net_device * dev,const unsigned char * addr)8976b6e2725Sstephen hemminger int dev_mc_add_global(struct net_device *dev, const unsigned char *addr)
89822bedad3SJiri Pirko {
89922bedad3SJiri Pirko return __dev_mc_add(dev, addr, true);
90022bedad3SJiri Pirko }
90122bedad3SJiri Pirko EXPORT_SYMBOL(dev_mc_add_global);
90222bedad3SJiri Pirko
__dev_mc_del(struct net_device * dev,const unsigned char * addr,bool global)9036b6e2725Sstephen hemminger static int __dev_mc_del(struct net_device *dev, const unsigned char *addr,
90422bedad3SJiri Pirko bool global)
90522bedad3SJiri Pirko {
90622bedad3SJiri Pirko int err;
90722bedad3SJiri Pirko
90822bedad3SJiri Pirko netif_addr_lock_bh(dev);
90922bedad3SJiri Pirko err = __hw_addr_del_ex(&dev->mc, addr, dev->addr_len,
9104cd729b0SVlad Yasevich NETDEV_HW_ADDR_T_MULTICAST, global, false);
91122bedad3SJiri Pirko if (!err)
91222bedad3SJiri Pirko __dev_set_rx_mode(dev);
91322bedad3SJiri Pirko netif_addr_unlock_bh(dev);
91422bedad3SJiri Pirko return err;
91522bedad3SJiri Pirko }
91622bedad3SJiri Pirko
91722bedad3SJiri Pirko /**
91822bedad3SJiri Pirko * dev_mc_del - Delete a multicast address.
91922bedad3SJiri Pirko * @dev: device
92022bedad3SJiri Pirko * @addr: address to delete
92122bedad3SJiri Pirko *
92222bedad3SJiri Pirko * Release reference to a multicast address and remove it
92322bedad3SJiri Pirko * from the device if the reference count drops to zero.
92422bedad3SJiri Pirko */
dev_mc_del(struct net_device * dev,const unsigned char * addr)9256b6e2725Sstephen hemminger int dev_mc_del(struct net_device *dev, const unsigned char *addr)
92622bedad3SJiri Pirko {
92722bedad3SJiri Pirko return __dev_mc_del(dev, addr, false);
92822bedad3SJiri Pirko }
92922bedad3SJiri Pirko EXPORT_SYMBOL(dev_mc_del);
93022bedad3SJiri Pirko
93122bedad3SJiri Pirko /**
93222bedad3SJiri Pirko * dev_mc_del_global - Delete a global multicast address.
93322bedad3SJiri Pirko * @dev: device
93422bedad3SJiri Pirko * @addr: address to delete
93522bedad3SJiri Pirko *
93622bedad3SJiri Pirko * Release reference to a multicast address and remove it
93722bedad3SJiri Pirko * from the device if the reference count drops to zero.
93822bedad3SJiri Pirko */
dev_mc_del_global(struct net_device * dev,const unsigned char * addr)9396b6e2725Sstephen hemminger int dev_mc_del_global(struct net_device *dev, const unsigned char *addr)
94022bedad3SJiri Pirko {
94122bedad3SJiri Pirko return __dev_mc_del(dev, addr, true);
94222bedad3SJiri Pirko }
94322bedad3SJiri Pirko EXPORT_SYMBOL(dev_mc_del_global);
94422bedad3SJiri Pirko
94522bedad3SJiri Pirko /**
946cdfb97bcSZhi Yong Wu * dev_mc_sync - Synchronize device's multicast list to another device
94722bedad3SJiri Pirko * @to: destination device
94822bedad3SJiri Pirko * @from: source device
94922bedad3SJiri Pirko *
95022bedad3SJiri Pirko * Add newly added addresses to the destination device and release
95122bedad3SJiri Pirko * addresses that have no users left. The source device must be
952ab16ebf3SJiri Pirko * locked by netif_addr_lock_bh.
95322bedad3SJiri Pirko *
954b81693d9SJiri Pirko * This function is intended to be called from the ndo_set_rx_mode
955b81693d9SJiri Pirko * function of layered software devices.
95622bedad3SJiri Pirko */
dev_mc_sync(struct net_device * to,struct net_device * from)95722bedad3SJiri Pirko int dev_mc_sync(struct net_device *to, struct net_device *from)
95822bedad3SJiri Pirko {
95922bedad3SJiri Pirko int err = 0;
96022bedad3SJiri Pirko
96122bedad3SJiri Pirko if (to->addr_len != from->addr_len)
96222bedad3SJiri Pirko return -EINVAL;
96322bedad3SJiri Pirko
9641fc70edbSTaehee Yoo netif_addr_lock(to);
96522bedad3SJiri Pirko err = __hw_addr_sync(&to->mc, &from->mc, to->addr_len);
96622bedad3SJiri Pirko if (!err)
96722bedad3SJiri Pirko __dev_set_rx_mode(to);
9682429f7acSJiri Pirko netif_addr_unlock(to);
96922bedad3SJiri Pirko return err;
97022bedad3SJiri Pirko }
97122bedad3SJiri Pirko EXPORT_SYMBOL(dev_mc_sync);
97222bedad3SJiri Pirko
97322bedad3SJiri Pirko /**
974cdfb97bcSZhi Yong Wu * dev_mc_sync_multiple - Synchronize device's multicast list to another
9754cd729b0SVlad Yasevich * device, but allow for multiple calls to sync to multiple devices.
9764cd729b0SVlad Yasevich * @to: destination device
9774cd729b0SVlad Yasevich * @from: source device
9784cd729b0SVlad Yasevich *
9794cd729b0SVlad Yasevich * Add newly added addresses to the destination device and release
9804cd729b0SVlad Yasevich * addresses that have no users left. The source device must be
9814cd729b0SVlad Yasevich * locked by netif_addr_lock_bh.
9824cd729b0SVlad Yasevich *
9834cd729b0SVlad Yasevich * This function is intended to be called from the ndo_set_rx_mode
9844cd729b0SVlad Yasevich * function of layered software devices. It allows for a single
9854cd729b0SVlad Yasevich * source device to be synced to multiple destination devices.
9864cd729b0SVlad Yasevich */
dev_mc_sync_multiple(struct net_device * to,struct net_device * from)9874cd729b0SVlad Yasevich int dev_mc_sync_multiple(struct net_device *to, struct net_device *from)
9884cd729b0SVlad Yasevich {
9894cd729b0SVlad Yasevich int err = 0;
9904cd729b0SVlad Yasevich
9914cd729b0SVlad Yasevich if (to->addr_len != from->addr_len)
9924cd729b0SVlad Yasevich return -EINVAL;
9934cd729b0SVlad Yasevich
9941fc70edbSTaehee Yoo netif_addr_lock(to);
995b190a508SJay Vosburgh err = __hw_addr_sync_multiple(&to->mc, &from->mc, to->addr_len);
9964cd729b0SVlad Yasevich if (!err)
9974cd729b0SVlad Yasevich __dev_set_rx_mode(to);
9984cd729b0SVlad Yasevich netif_addr_unlock(to);
9994cd729b0SVlad Yasevich return err;
10004cd729b0SVlad Yasevich }
10014cd729b0SVlad Yasevich EXPORT_SYMBOL(dev_mc_sync_multiple);
10024cd729b0SVlad Yasevich
10034cd729b0SVlad Yasevich /**
100422bedad3SJiri Pirko * dev_mc_unsync - Remove synchronized addresses from the destination device
100522bedad3SJiri Pirko * @to: destination device
100622bedad3SJiri Pirko * @from: source device
100722bedad3SJiri Pirko *
100822bedad3SJiri Pirko * Remove all addresses that were added to the destination device by
100922bedad3SJiri Pirko * dev_mc_sync(). This function is intended to be called from the
101022bedad3SJiri Pirko * dev->stop function of layered software devices.
101122bedad3SJiri Pirko */
dev_mc_unsync(struct net_device * to,struct net_device * from)101222bedad3SJiri Pirko void dev_mc_unsync(struct net_device *to, struct net_device *from)
101322bedad3SJiri Pirko {
101422bedad3SJiri Pirko if (to->addr_len != from->addr_len)
101522bedad3SJiri Pirko return;
101622bedad3SJiri Pirko
1017e8280338SCong Wang /* See the above comments inside dev_uc_unsync(). */
101822bedad3SJiri Pirko netif_addr_lock_bh(from);
10191fc70edbSTaehee Yoo netif_addr_lock(to);
102022bedad3SJiri Pirko __hw_addr_unsync(&to->mc, &from->mc, to->addr_len);
102122bedad3SJiri Pirko __dev_set_rx_mode(to);
102222bedad3SJiri Pirko netif_addr_unlock(to);
102322bedad3SJiri Pirko netif_addr_unlock_bh(from);
102422bedad3SJiri Pirko }
102522bedad3SJiri Pirko EXPORT_SYMBOL(dev_mc_unsync);
102622bedad3SJiri Pirko
102722bedad3SJiri Pirko /**
102822bedad3SJiri Pirko * dev_mc_flush - Flush multicast addresses
102922bedad3SJiri Pirko * @dev: device
103022bedad3SJiri Pirko *
103122bedad3SJiri Pirko * Flush multicast addresses.
103222bedad3SJiri Pirko */
dev_mc_flush(struct net_device * dev)103322bedad3SJiri Pirko void dev_mc_flush(struct net_device *dev)
103422bedad3SJiri Pirko {
103522bedad3SJiri Pirko netif_addr_lock_bh(dev);
103622bedad3SJiri Pirko __hw_addr_flush(&dev->mc);
103722bedad3SJiri Pirko netif_addr_unlock_bh(dev);
103822bedad3SJiri Pirko }
103922bedad3SJiri Pirko EXPORT_SYMBOL(dev_mc_flush);
104022bedad3SJiri Pirko
104122bedad3SJiri Pirko /**
1042bb9aaaa1Ssunlianwen * dev_mc_init - Init multicast address list
104322bedad3SJiri Pirko * @dev: device
104422bedad3SJiri Pirko *
104522bedad3SJiri Pirko * Init multicast address list.
104622bedad3SJiri Pirko */
dev_mc_init(struct net_device * dev)104722bedad3SJiri Pirko void dev_mc_init(struct net_device *dev)
104822bedad3SJiri Pirko {
104922bedad3SJiri Pirko __hw_addr_init(&dev->mc);
105022bedad3SJiri Pirko }
105122bedad3SJiri Pirko EXPORT_SYMBOL(dev_mc_init);
1052