diff options
| author | Vlad Yasevich <vyasevic@redhat.com> | 2013-04-15 05:54:25 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2013-04-15 16:10:47 -0400 |
| commit | 4cd729b04285b7330edaf5a7080aa795d6d15ff3 (patch) | |
| tree | 274beba6b944edcd71879b779f2e674dede3cd91 | |
| parent | 0022d2dd4d76e0e7d5c241c343a5016fdfa2ad4f (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.h | 3 | ||||
| -rw-r--r-- | net/core/dev_addr_lists.c | 210 |
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); | |||
| 2627 | extern int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr); | 2628 | extern int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr); |
| 2628 | extern int dev_uc_del(struct net_device *dev, const unsigned char *addr); | 2629 | extern int dev_uc_del(struct net_device *dev, const unsigned char *addr); |
| 2629 | extern int dev_uc_sync(struct net_device *to, struct net_device *from); | 2630 | extern int dev_uc_sync(struct net_device *to, struct net_device *from); |
| 2631 | extern int dev_uc_sync_multiple(struct net_device *to, struct net_device *from); | ||
| 2630 | extern void dev_uc_unsync(struct net_device *to, struct net_device *from); | 2632 | extern void dev_uc_unsync(struct net_device *to, struct net_device *from); |
| 2631 | extern void dev_uc_flush(struct net_device *dev); | 2633 | extern void dev_uc_flush(struct net_device *dev); |
| 2632 | extern void dev_uc_init(struct net_device *dev); | 2634 | extern 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); | |||
| 2638 | extern int dev_mc_del(struct net_device *dev, const unsigned char *addr); | 2640 | extern int dev_mc_del(struct net_device *dev, const unsigned char *addr); |
| 2639 | extern int dev_mc_del_global(struct net_device *dev, const unsigned char *addr); | 2641 | extern int dev_mc_del_global(struct net_device *dev, const unsigned char *addr); |
| 2640 | extern int dev_mc_sync(struct net_device *to, struct net_device *from); | 2642 | extern int dev_mc_sync(struct net_device *to, struct net_device *from); |
| 2643 | extern int dev_mc_sync_multiple(struct net_device *to, struct net_device *from); | ||
| 2641 | extern void dev_mc_unsync(struct net_device *to, struct net_device *from); | 2644 | extern void dev_mc_unsync(struct net_device *to, struct net_device *from); |
| 2642 | extern void dev_mc_flush(struct net_device *dev); | 2645 | extern void dev_mc_flush(struct net_device *dev); |
| 2643 | extern void dev_mc_init(struct net_device *dev); | 2646 | extern 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 | ||
| 23 | static int __hw_addr_create_ex(struct netdev_hw_addr_list *list, | 23 | static 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 | ||
| 47 | static int __hw_addr_add_ex(struct netdev_hw_addr_list *list, | 48 | static 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 | ||
| 74 | static int __hw_addr_add(struct netdev_hw_addr_list *list, | 82 | static 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 | |||
| 89 | static 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 | ||
| 81 | static int __hw_addr_del_ex(struct netdev_hw_addr_list *list, | 113 | static 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 | |||
| 134 | static 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 | |||
| 150 | static void __hw_addr_unsync_one(struct netdev_hw_addr_list *to_list, | ||
