aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2013-04-15 16:10:53 -0400
committerDavid S. Miller <davem@davemloft.net>2013-04-15 16:10:53 -0400
commit61f47132dcba1eacf8fd8e64488d0b6d4bcbe4a0 (patch)
tree97014ff57652d41b366fb41a08c93e7aa3a798e8
parent0022d2dd4d76e0e7d5c241c343a5016fdfa2ad4f (diff)
parent72b270323dee45ccf4aafff7118dcc46caaed22e (diff)
Merge branch 'sync_multiple'
Vlad Yasevich says: ==================== Current dev_[uc|mc]_addr_sync() API currently correctly syncs the addresses to the first device. Any subsequent calls to sync will not do anything since the synched variable will be set. This variable is used as an optimization to skip over addresses that have been synched. There are some devices (ex: team) that attempt to do the above. There is other work in progress that needs to above to work corretly. The short series introduces dev_[uc|mc]_addr_synch_multiple() that allows multiple calls to sync to multiple different devices. Original API is left alone and still has the limitation. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/team/team.c4
-rw-r--r--include/linux/netdevice.h3
-rw-r--r--net/core/dev_addr_lists.c210
3 files changed, 173 insertions, 44 deletions
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 621c1bddeee9..9a31e8e50fac 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -1504,8 +1504,8 @@ static void team_set_rx_mode(struct net_device *dev)
1504 1504
1505 rcu_read_lock(); 1505 rcu_read_lock();
1506 list_for_each_entry_rcu(port, &team->port_list, list) { 1506 list_for_each_entry_rcu(port, &team->port_list, list) {
1507 dev_uc_sync(port->dev, dev); 1507 dev_uc_sync_multiple(port->dev, dev);
1508 dev_mc_sync(port->dev, dev); 1508 dev_mc_sync_multiple(port->dev, dev);
1509 } 1509 }
1510 rcu_read_unlock(); 1510 rcu_read_unlock();
1511} 1511}
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