Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=24023451c8df726692e2f52288a20870d13b501f
Commit:     24023451c8df726692e2f52288a20870d13b501f
Parent:     e6c9116d1dc984cb7ecf1b0fe26ca4a8ab36bb57
Author:     Patrick McHardy <[EMAIL PROTECTED]>
AuthorDate: Sat Jul 14 18:51:31 2007 -0700
Committer:  David S. Miller <[EMAIL PROTECTED]>
CommitDate: Sat Jul 14 18:51:31 2007 -0700

    [NET]: Add net_device change_rx_mode callback
    
    Currently the set_multicast_list (and set_rx_mode) callbacks are
    responsible for configuring the device according to the IFF_PROMISC,
    IFF_MULTICAST and IFF_ALLMULTI flags and the mc_list (and uc_list in
    case of set_rx_mode).
    
    These callbacks can be invoked from BH context without the rtnl_mutex
    by dev_mc_add/dev_mc_delete, which makes reading the device flags and
    promiscous/allmulti count racy. For real hardware drivers that just
    commit all changes to the hardware this is not a real problem since
    the stack guarantees to call them for every change, so at least the
    final call will not race and commit the correct configuration to the
    hardware.
    
    For software devices that want to synchronize promiscous and multicast
    state to an underlying device however this can cause corruption of the
    underlying device's flags or promisc/allmulti counts.
    
    When the software device is concurrently put in promiscous or allmulti
    mode while set_multicast_list is invoked from bottem half context, the
    device might synchronize the change to the underlying device without
    holding the rtnl_mutex, which races with concurrent changes to the
    underlying device.
    
    Add a dev->change_rx_flags hook that is invoked when any of the flags
    that affect rx filtering change (under the rtnl_mutex), which allows
    drivers to perform synchronization immediately and only synchronize
    the address lists in set_multicast_list/set_rx_mode.
    
    Signed-off-by: Patrick McHardy <[EMAIL PROTECTED]>
    Signed-off-by: David S. Miller <[EMAIL PROTECTED]>
---
 include/linux/netdevice.h |    3 +++
 net/core/dev.c            |   17 ++++++++++++++++-
 2 files changed, 19 insertions(+), 1 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 79cc3da..f193aba 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -516,6 +516,9 @@ struct net_device
                                                void *saddr,
                                                unsigned len);
        int                     (*rebuild_header)(struct sk_buff *skb);
+#define HAVE_CHANGE_RX_FLAGS
+       void                    (*change_rx_flags)(struct net_device *dev,
+                                                  int flags);
 #define HAVE_SET_RX_MODE
        void                    (*set_rx_mode)(struct net_device *dev);
 #define HAVE_MULTICAST                  
diff --git a/net/core/dev.c b/net/core/dev.c
index 9644305..59ec811 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2521,6 +2521,8 @@ static void __dev_set_promiscuity(struct net_device *dev, 
int inc)
 {
        unsigned short old_flags = dev->flags;
 
+       ASSERT_RTNL();
+
        if ((dev->promiscuity += inc) == 0)
                dev->flags &= ~IFF_PROMISC;
        else
@@ -2535,6 +2537,9 @@ static void __dev_set_promiscuity(struct net_device *dev, 
int inc)
                        dev->name, (dev->flags & IFF_PROMISC),
                        (old_flags & IFF_PROMISC),
                        audit_get_loginuid(current->audit_context));
+
+               if (dev->change_rx_flags)
+                       dev->change_rx_flags(dev, IFF_PROMISC);
        }
 }
 
@@ -2573,11 +2578,16 @@ void dev_set_allmulti(struct net_device *dev, int inc)
 {
        unsigned short old_flags = dev->flags;
 
+       ASSERT_RTNL();
+
        dev->flags |= IFF_ALLMULTI;
        if ((dev->allmulti += inc) == 0)
                dev->flags &= ~IFF_ALLMULTI;
-       if (dev->flags ^ old_flags)
+       if (dev->flags ^ old_flags) {
+               if (dev->change_rx_flags)
+                       dev->change_rx_flags(dev, IFF_ALLMULTI);
                dev_set_rx_mode(dev);
+       }
 }
 
 /*
@@ -2778,6 +2788,8 @@ int dev_change_flags(struct net_device *dev, unsigned 
flags)
        int ret, changes;
        int old_flags = dev->flags;
 
+       ASSERT_RTNL();
+
        /*
         *      Set the flags on our device.
         */
@@ -2792,6 +2804,9 @@ int dev_change_flags(struct net_device *dev, unsigned 
flags)
         *      Load in the correct multicast list now the flags have changed.
         */
 
+       if (dev->change_rx_flags && (dev->flags ^ flags) & IFF_MULTICAST)
+               dev->change_rx_flags(dev, IFF_MULTICAST);
+
        dev_set_rx_mode(dev);
 
        /*
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to