aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVlad Yasevich <vyasevic@redhat.com>2013-04-15 05:54:25 -0400
committerDavid S. Miller <davem@davemloft.net>2013-04-15 16:10:47 -0400
commit4cd729b04285b7330edaf5a7080aa795d6d15ff3 (patch)
tree274beba6b944edcd71879b779f2e674dede3cd91
parent0022d2dd4d76e0e7d5c241c343a5016fdfa2ad4f (diff)
net: add dev_uc_sync_multiple() and dev_mc_sync_multiple() api
The current implementation of dev_uc_sync/unsync() assumes that there is a strict 1-to-1 relationship between the source and destination of the sync. In other words, once an address has been synced to a destination device, it will not be synced to any other device through the sync API. However, there are some virtual devices that aggreate a number of lower devices and need to sync addresses to all of them. The current API falls short there. This patch introduces a new dev_uc_sync_multiple() api that can be called in the above circumstances and allows sync to work for every invocation. CC: Jiri Pirko <jiri@resnulli.us> Signed-off-by: Vlad Yasevich <vyasevic@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-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,