]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net: count hw_addr syncs so that unsync works properly.
authorVlad Yasevich <vyasevic@redhat.com>
Tue, 2 Apr 2013 21:10:07 +0000 (17:10 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 1 May 2013 15:56:36 +0000 (08:56 -0700)
[ Upstream commit 4543fbefe6e06a9e40d9f2b28d688393a299f079 ]

A few drivers use dev_uc_sync/unsync to synchronize the
address lists from master down to slave/lower devices.  In
some cases (bond/team) a single address list is synched down
to multiple devices.  At the time of unsync, we have a leak
in these lower devices, because "synced" is treated as a
boolean and the address will not be unsynced for anything after
the first device/call.

Treat "synced" as a count (same as refcount) and allow all
unsync calls to work.

Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/netdevice.h
net/core/dev_addr_lists.c

index c0372151ce22041f5503e3451459db63a87170f7..02f887a6fb4f9fc8df145a2ba440f6eb595a82e5 100644 (file)
@@ -231,9 +231,9 @@ struct netdev_hw_addr {
 #define NETDEV_HW_ADDR_T_SLAVE         3
 #define NETDEV_HW_ADDR_T_UNICAST       4
 #define NETDEV_HW_ADDR_T_MULTICAST     5
-       bool                    synced;
        bool                    global_use;
        int                     refcount;
+       int                     synced;
        struct rcu_head         rcu_head;
 };
 
index c776af5369351e219d98c560586d9a064ede8be0..41775e4bd42e2a21ff93723d58ce66d4325799e8 100644 (file)
@@ -56,7 +56,7 @@ static int __hw_addr_add_ex(struct netdev_hw_addr_list *list,
        ha->type = addr_type;
        ha->refcount = 1;
        ha->global_use = global;
-       ha->synced = false;
+       ha->synced = 0;
        list_add_tail_rcu(&ha->list, &list->list);
        list->count++;
        return 0;
@@ -154,7 +154,7 @@ int __hw_addr_sync(struct netdev_hw_addr_list *to_list,
                                            addr_len, ha->type);
                        if (err)
                                break;
-                       ha->synced = true;
+                       ha->synced++;
                        ha->refcount++;
                } else if (ha->refcount == 1) {
                        __hw_addr_del(to_list, ha->addr, addr_len, ha->type);
@@ -175,7 +175,7 @@ void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
                if (ha->synced) {
                        __hw_addr_del(to_list, ha->addr,
                                      addr_len, ha->type);
-                       ha->synced = false;
+                       ha->synced--;
                        __hw_addr_del(from_list, ha->addr,
                                      addr_len, ha->type);
                }