diff options
author | Jay Vosburgh <fubar@us.ibm.com> | 2013-05-31 07:57:28 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-05-31 19:56:56 -0400 |
commit | 29ca2f8fcc721517b83d0a560c47cee2dde827a6 (patch) | |
tree | bbd07f503392c4d59b61f99faaf886118a2558fd /net/core | |
parent | 60ba834c2fb65f2fafee47e03c258fac579d0591 (diff) |
net/core: __hw_addr_sync_one / _multiple broken
Currently, __hw_addr_sync_one is called in a loop by
__hw_addr_sync_multiple to sync each of a "from" device's hw addresses
to a "to" device. __hw_addr_sync_one calls __hw_addr_add_ex to attempt
to add each address. __hw_addr_add_ex is called with global=false, and
sync=true.
__hw_addr_add_ex checks to see if the new address matches an
address already on the list. If so, it tests global and sync. In this
case, sync=true, and it then checks if the address is already synced,
and if so, returns 0.
This 0 return causes __hw_addr_sync_one to increment the sync_cnt
and refcount for the "from" list's address entry, even though the address
is already synced and has a reference and sync_cnt. This will cause
the sync_cnt and refcount to increment without bound every time an
addresses is added to the "from" device and synced to the "to" device.
The fix here has two parts:
First, when __hw_addr_add_ex finds the address already exists
and is synced, return -EEXIST instead of 0.
Second, __hw_addr_sync_one checks the error return for -EEXIST,
and if so, it (a) does not add a refcount/sync_cnt, and (b) returns 0
itself so that __hw_addr_sync_multiple will not return an error.
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Reviewed-by: Vlad Yasevich <vyasevic@redhat.com>
Tested-by: Shawn Bohrer <sbohrer@rgmadvisors.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r-- | net/core/dev_addr_lists.c | 11 |
1 files changed, 7 insertions, 4 deletions
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index c858e81e6423..8e2c2ef8053d 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c | |||
@@ -67,7 +67,7 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list, | |||
67 | } | 67 | } |
68 | if (sync) { | 68 | if (sync) { |
69 | if (ha->synced) | 69 | if (ha->synced) |
70 | return 0; | 70 | return -EEXIST; |
71 | else | 71 | else |
72 | ha->synced = true; | 72 | ha->synced = true; |
73 | } | 73 | } |
@@ -140,10 +140,13 @@ static int __hw_addr_sync_one(struct netdev_hw_addr_list *to_list, | |||
140 | 140 | ||
141 | err = __hw_addr_add_ex(to_list, ha->addr, addr_len, ha->type, | 141 | err = __hw_addr_add_ex(to_list, ha->addr, addr_len, ha->type, |
142 | false, true); | 142 | false, true); |
143 | if (err) | 143 | if (err && err != -EEXIST) |
144 | return err; | 144 | return err; |
145 | ha->sync_cnt++; | 145 | |
146 | ha->refcount++; | 146 | if (!err) { |
147 | ha->sync_cnt++; | ||
148 | ha->refcount++; | ||
149 | } | ||
147 | 150 | ||
148 | return 0; | 151 | return 0; |
149 | } | 152 | } |