aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netdevice.h3
-rw-r--r--net/core/dev_addr_lists.c210
2 files changed, 171 insertions, 42 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 53d3939358a7..623b57b52195 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -209,6 +209,7 @@ struct netdev_hw_addr {
209#define NETDEV_HW_ADDR_T_UNICAST 4 209#define NETDEV_HW_ADDR_T_UNICAST 4
210#define NETDEV_HW_ADDR_T_MULTICAST 5 210#define NETDEV_HW_ADDR_T_MULTICAST 5
211 bool global_use; 211 bool global_use;
212 int sync_cnt;
212 int refcount; 213 int refcount;
213 int synced; 214 int synced;
214 struct rcu_head rcu_head; 215 struct rcu_head rcu_head;
@@ -2627,6 +2628,7 @@ extern int dev_uc_add(struct net_device *dev, const unsigned char *addr);
2627extern int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr); 2628extern int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr);
2628extern int dev_uc_del(struct net_device *dev, const unsigned char *addr); 2629extern int dev_uc_del(struct net_device *dev, const unsigned char *addr);
2629extern int dev_uc_sync(struct net_device *to, struct net_device *from); 2630extern int dev_uc_sync(struct net_device *to, struct net_device *from);
2631extern int dev_uc_sync_multiple(struct net_device *to, struct net_device *from);
2630extern void dev_uc_unsync(struct net_device *to, struct net_device *from); 2632extern void dev_uc_unsync(struct net_device *to, struct net_device *from);
2631extern void dev_uc_flush(struct net_device *dev); 2633extern void dev_uc_flush(struct net_device *dev);
2632extern void dev_uc_init(struct net_device *dev); 2634extern void dev_uc_init(struct net_device *dev);
@@ -2638,6 +2640,7 @@ extern int dev_mc_add_excl(struct net_device *dev, const unsigned char *addr);
2638extern int dev_mc_del(struct net_device *dev, const unsigned char *addr); 2640extern int dev_mc_del(struct net_device *dev, const unsigned char *addr);
2639extern int dev_mc_del_global(struct net_device *dev, const unsigned char *addr); 2641extern int dev_mc_del_global(struct net_device *dev, const unsigned char *addr);
2640extern int dev_mc_sync(struct net_device *to, struct net_device *from); 2642extern int dev_mc_sync(struct net_device *to, struct net_device *from);
2643extern int dev_mc_sync_multiple(struct net_device *to, struct net_device *from);
2641extern void dev_mc_unsync(struct net_device *to, struct net_device *from); 2644extern void dev_mc_unsync(struct net_device *to, struct net_device *from);
2642extern void dev_mc_flush(struct net_device *dev); 2645extern void dev_mc_flush(struct net_device *dev);
2643extern void dev_mc_init(struct net_device *dev); 2646extern void dev_mc_init(struct net_device *dev);
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index abdc9e6ef33e..c013f38482a1 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -22,7 +22,8 @@
22 22
23static int __hw_addr_create_ex(struct netdev_hw_addr_list *list, 23static int __hw_addr_create_ex(struct netdev_hw_addr_list *list,
24 const unsigned char *addr, int addr_len, 24 const unsigned char *addr, int addr_len,
25 unsigned char addr_type, bool global) 25 unsigned char addr_type, bool global,
26 bool sync)
26{ 27{
27 struct netdev_hw_addr *ha; 28 struct netdev_hw_addr *ha;
28 int alloc_size; 29 int alloc_size;
@@ -37,7 +38,7 @@ static int __hw_addr_create_ex(struct netdev_hw_addr_list *list,
37 ha->type = addr_type; 38 ha->type = addr_type;
38 ha->refcount = 1; 39 ha->refcount = 1;
39 ha->global_use = global; 40 ha->global_use = global;
40 ha->synced = 0; 41 ha->synced = sync;
41 list_add_tail_rcu(&ha->list, &list->list); 42 list_add_tail_rcu(&ha->list, &list->list);
42 list->count++; 43 list->count++;
43 44
@@ -46,7 +47,7 @@ static int __hw_addr_create_ex(struct netdev_hw_addr_list *list,
46 47
47static int __hw_addr_add_ex(struct netdev_hw_addr_list *list, 48static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
48 const unsigned char *addr, int addr_len, 49 const unsigned char *addr, int addr_len,
49 unsigned char addr_type, bool global) 50 unsigned char addr_type, bool global, bool sync)
50{ 51{
51 struct netdev_hw_addr *ha; 52 struct netdev_hw_addr *ha;
52 53
@@ -63,43 +64,62 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
63 else 64 else
64 ha->global_use = true; 65 ha->global_use = true;
65 } 66 }
67 if (sync) {
68 if (ha->synced)
69 return 0;
70 else
71 ha->synced = true;
72 }
66 ha->refcount++; 73 ha->refcount++;
67 return 0; 74 return 0;
68 } 75 }
69 } 76 }
70 77
71 return __hw_addr_create_ex(list, addr, addr_len, addr_type, global); 78 return __hw_addr_create_ex(list, addr, addr_len, addr_type, global,
79 sync);
72} 80}
73 81
74static int __hw_addr_add(struct netdev_hw_addr_list *list, 82static int __hw_addr_add(struct netdev_hw_addr_list *list,
75 const unsigned char *addr, int addr_len, 83 const unsigned char *addr, int addr_len,
76 unsigned char addr_type) 84 unsigned char addr_type)
77{ 85{
78 return __hw_addr_add_ex(list, addr, addr_len, addr_type, false); 86 return __hw_addr_add_ex(list, addr, addr_len, addr_type, false, false);
87}
88
89static int __hw_addr_del_entry(struct netdev_hw_addr_list *list,
90 struct netdev_hw_addr *ha, bool global,
91 bool sync)
92{
93 if (global && !ha->global_use)
94 return -ENOENT;
95
96 if (sync && !ha->synced)
97 return -ENOENT;
98
99 if (global)
100 ha->global_use = false;
101
102 if (sync)
103 ha->synced = false;
104
105 if (--ha->refcount)
106 return 0;
107 list_del_rcu(&ha->list);
108 kfree_rcu(ha, rcu_head);
109 list->count--;
110 return 0;
79} 111}
80 112
81static int __hw_addr_del_ex(struct netdev_hw_addr_list *list, 113static int __hw_addr_del_ex(struct netdev_hw_addr_list *list,
82 const unsigned char *addr, int addr_len, 114 const unsigned char *addr, int addr_len,
83 unsigned char addr_type, bool global) 115 unsigned char addr_type, bool global, bool sync)
84{ 116{
85 struct netdev_hw_addr *ha; 117 struct netdev_hw_addr *ha;
86 118
87 list_for_each_entry(ha, &list->list, list) { 119 list_for_each_entry(ha, &list->list, list) {
88 if (!memcmp(ha->addr, addr, addr_len) && 120 if (!memcmp(ha->addr, addr, addr_len) &&
89 (ha->type == addr_type || !addr_type)) { 121 (ha->type == addr_type || !addr_type))
90 if (global) { 122 return __hw_addr_del_entry(list, ha, global, sync);
91 if (!ha->global_use)
92 break;
93 else
94 ha->global_use = false;
95 }
96 if (--ha->refcount)
97 return 0;
98 list_del_rcu(&ha->list);
99 kfree_rcu(ha, rcu_head);
100 list->count--;
101 return 0;
102 }
103 } 123 }
104 return -ENOENT; 124 return -ENOENT;
105} 125}
@@ -108,7 +128,57 @@ static int __hw_addr_del(struct netdev_hw_addr_list *list,
108 const unsigned char *addr, int addr_len, 128 const unsigned char *addr, int addr_len,
109 unsigned char addr_type) 129 unsigned char addr_type)
110{ 130{
111 return __hw_addr_del_ex(list, addr, addr_len, addr_type, false); 131 return __hw_addr_del_ex(list, addr, addr_len, addr_type, false, false);
132}
133
134static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list,
135 struct netdev_hw_addr *ha,
136 int addr_len)
137{
138 int err;
139
140 err = __hw_addr_add_ex(to_list, ha->addr, addr_len, ha->type,
141 false, true);
142 if (err)
143 return err;
144 ha->sync_cnt++;
145 ha->refcount++;
146
147 return 0;
148}
149
150static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list,
151 struct netdev_hw_addr_list *from_list,
152 struct netdev_hw_addr *ha,
153 int addr_len)
154{
155 int err;
156
157 err = __hw_addr_del_ex(to_list, ha->addr, addr_len, ha->type,
158 false, true);
159 if (err)
160 return;
161 ha->sync_cnt--;
162 __hw_addr_del_entry(from_list, ha, false, true);
163}
164
165static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
166 struct netdev_hw_addr_list *from_list,
167 int addr_len)
168{
169 int err = 0;
170 struct netdev_hw_addr *ha, *tmp;
171
172 list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
173 if (ha->sync_cnt == ha->refcount) {
174 __hw_addr_unsync_one(to_list, from_list, ha, addr_len);
175 } else {
176 err = __hw_addr_sync_one(to_list, ha, addr_len);
177 if (err)
178 break;
179 }
180 }
181 return err;
112} 182}
113 183
114int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list, 184int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list,
@@ -152,6 +222,11 @@ void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list,
152} 222}
153EXPORT_SYMBOL(__hw_addr_del_multiple); 223EXPORT_SYMBOL(__hw_addr_del_multiple);
154 224
225/* This function only works where there is a strict 1-1 relationship
226 * between source and destionation of they synch. If you ever need to
227 * sync addresses to more then 1 destination, you need to use
228 * __hw_addr_sync_multiple().
229 */
155int __hw_addr_sync(struct netdev_hw_addr_list *to_list, 230int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
156 struct netdev_hw_addr_list *from_list, 231 struct netdev_hw_addr_list *from_list,
157 int addr_len) 232 int addr_len)
@@ -160,17 +235,12 @@ int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
160 struct netdev_hw_addr *ha, *tmp; 235 struct netdev_hw_addr *ha, *tmp;
161 236
162 list_for_each_entry_safe(ha, tmp, &from_list->list, list) { 237 list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
163 if (!ha->synced) { 238 if (!ha->sync_cnt) {
164 err = __hw_addr_add(to_list, ha->addr, 239 err = __hw_addr_sync_one(to_list, ha, addr_len);
165 addr_len, ha->type);
166 if (err) 240 if (err)
167 break; 241 break;
168 ha->synced++; 242 } else if (ha->refcount == 1)
169 ha->refcount++; 243 __hw_addr_unsync_one(to_list, from_list, ha, addr_len);
170 } else if (ha->refcount == 1) {
171 __hw_addr_del(to_list, ha->addr, addr_len, ha->type);
172 __hw_addr_del(from_list, ha->addr, addr_len, ha->type);
173 }
174 } 244 }
175 return err; 245 return err;
176} 246}
@@ -183,13 +253,8 @@ void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
183 struct netdev_hw_addr *ha, *tmp; 253 struct netdev_hw_addr *ha, *tmp;
184 254
185 list_for_each_entry_safe(ha, tmp, &from_list->list, list) { 255 list_for_each_entry_safe(ha, tmp, &from_list->list, list) {
186 if (ha->synced) { 256 if (ha->sync_cnt)
187 __hw_addr_del(to_list, ha->addr, 257 __hw_addr_unsync_one(to_list, from_list, ha, addr_len);
188 addr_len, ha->type);
189 ha->synced--;
190 __hw_addr_del(from_list, ha->addr,
191 addr_len, ha->type);
192 }
193 } 258 }
194} 259}
195EXPORT_SYMBOL(__hw_addr_unsync); 260EXPORT_SYMBOL(__hw_addr_unsync);
@@ -406,7 +471,7 @@ int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr)
406 } 471 }
407 } 472 }
408 err = __hw_addr_create_ex(&dev->uc, addr, dev->addr_len, 473 err = __hw_addr_create_ex(&dev->uc, addr, dev->addr_len,
409 NETDEV_HW_ADDR_T_UNICAST, true); 474 NETDEV_HW_ADDR_T_UNICAST, true, false);
410 if (!err) 475 if (!err)
411 __dev_set_rx_mode(dev); 476 __dev_set_rx_mode(dev);
412out: 477out:
@@ -469,7 +534,8 @@ EXPORT_SYMBOL(dev_uc_del);
469 * locked by netif_addr_lock_bh. 534 * locked by netif_addr_lock_bh.
470 * 535 *
471 * This function is intended to be called from the dev->set_rx_mode 536 * This function is intended to be called from the dev->set_rx_mode
472 * function of layered software devices. 537 * function of layered software devices. This function assumes that
538 * addresses will only ever be synced to the @to devices and no other.
473 */ 539 */
474int dev_uc_sync(struct net_device *to, struct net_device *from) 540int dev_uc_sync(struct net_device *to, struct net_device *from)
475{ 541{
@@ -488,6 +554,36 @@ int dev_uc_sync(struct net_device *to, struct net_device *from)
488EXPORT_SYMBOL(dev_uc_sync); 554EXPORT_SYMBOL(dev_uc_sync);
489 555
490/** 556/**
557 * dev_uc_sync_multiple - Synchronize device's unicast list to another
558 * device, but allow for multiple calls to sync to multiple devices.
559 * @to: destination device
560 * @from: source device
561 *
562 * Add newly added addresses to the destination device and release
563 * addresses that have been deleted from the source. The source device
564 * must be locked by netif_addr_lock_bh.
565 *
566 * This function is intended to be called from the dev->set_rx_mode
567 * function of layered software devices. It allows for a single source
568 * device to be synced to multiple destination devices.
569 */
570int dev_uc_sync_multiple(struct net_device *to, struct net_device *from)
571{
572 int err = 0;
573
574 if (to->addr_len != from->addr_len)
575 return -EINVAL;
576
577 netif_addr_lock_nested(to);
578 err = __hw_addr_sync_multiple(&to->uc, &from->uc, to->addr_len);
579 if (!err)
580 __dev_set_rx_mode(to);
581 netif_addr_unlock(to);
582 return err;
583}
584EXPORT_SYMBOL(dev_uc_sync_multiple);
585
586/**
491 * dev_uc_unsync - Remove synchronized addresses from the destination device 587 * dev_uc_unsync - Remove synchronized addresses from the destination device
492 * @to: destination device 588 * @to: destination device
493 * @from: source device 589 * @from: source device
@@ -559,7 +655,7 @@ int dev_mc_add_excl(struct net_device *dev, const unsigned char *addr)
559 } 655 }
560 } 656 }
561 err = __hw_addr_create_ex(&dev->mc, addr, dev->addr_len, 657 err = __hw_addr_create_ex(&dev->mc, addr, dev->addr_len,
562 NETDEV_HW_ADDR_T_MULTICAST, true); 658 NETDEV_HW_ADDR_T_MULTICAST, true, false);
563 if (!err) 659 if (!err)
564 __dev_set_rx_mode(dev); 660 __dev_set_rx_mode(dev);
565out: 661out:
@@ -575,7 +671,7 @@ static int __dev_mc_add(struct net_device *dev, const unsigned char *addr,
575 671
576 netif_addr_lock_bh(dev); 672 netif_addr_lock_bh(dev);
577 err = __hw_addr_add_ex(&dev->mc, addr, dev->addr_len, 673 err = __hw_addr_add_ex(&dev->mc, addr, dev->addr_len,
578 NETDEV_HW_ADDR_T_MULTICAST, global); 674 NETDEV_HW_ADDR_T_MULTICAST, global, false);
579 if (!err) 675 if (!err)
580 __dev_set_rx_mode(dev); 676 __dev_set_rx_mode(dev);
581 netif_addr_unlock_bh(dev); 677 netif_addr_unlock_bh(dev);
@@ -615,7 +711,7 @@ static int __dev_mc_del(struct net_device *dev, const unsigned char *addr,
615 711
616 netif_addr_lock_bh(dev); 712 netif_addr_lock_bh(dev);
617 err = __hw_addr_del_ex(&dev->mc, addr, dev->addr_len, 713 err = __hw_addr_del_ex(&dev->mc, addr, dev->addr_len,
618 NETDEV_HW_ADDR_T_MULTICAST, global); 714 NETDEV_HW_ADDR_T_MULTICAST, global, false);
619 if (!err) 715 if (!err)
620 __dev_set_rx_mode(dev); 716 __dev_set_rx_mode(dev);
621 netif_addr_unlock_bh(dev); 717 netif_addr_unlock_bh(dev);
@@ -679,6 +775,36 @@ int dev_mc_sync(struct net_device *to, struct net_device *from)
679EXPORT_SYMBOL(dev_mc_sync); 775EXPORT_SYMBOL(dev_mc_sync);
680 776
681/** 777/**
778 * dev_mc_sync_multiple - Synchronize device's unicast list to another
779 * device, but allow for multiple calls to sync to multiple devices.
780 * @to: destination device
781 * @from: source device
782 *
783 * Add newly added addresses to the destination device and release
784 * addresses that have no users left. The source device must be
785 * locked by netif_addr_lock_bh.
786 *
787 * This function is intended to be called from the ndo_set_rx_mode
788 * function of layered software devices. It allows for a single
789 * source device to be synced to multiple destination devices.
790 */
791int dev_mc_sync_multiple(struct net_device *to, struct net_device *from)
792{
793 int err = 0;
794
795 if (to->addr_len != from->addr_len)
796 return -EINVAL;
797
798 netif_addr_lock_nested(to);
799 err = __hw_addr_sync(&to->mc, &from->mc, to->addr_len);
800 if (!err)
801 __dev_set_rx_mode(to);
802 netif_addr_unlock(to);
803 return err;
804}
805EXPORT_SYMBOL(dev_mc_sync_multiple);
806
807/**
682 * dev_mc_unsync - Remove synchronized addresses from the destination device 808 * dev_mc_unsync - Remove synchronized addresses from the destination device
683 * @to: destination device 809 * @to: destination device
684 * @from: source device 810 * @from: source device