]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
[NET]: dev: secondary unicast address support
authorPatrick McHardy <kaber@trash.net>
Wed, 27 Jun 2007 08:28:10 +0000 (01:28 -0700)
committerDavid S. Miller <davem@sunset.davemloft.net>
Wed, 11 Jul 2007 05:15:56 +0000 (22:15 -0700)
Add support for configuring secondary unicast addresses on network
devices. To support this devices capable of filtering multiple
unicast addresses need to change their set_multicast_list function
to configure unicast filters as well and assign it to dev->set_rx_mode
instead of dev->set_multicast_list. Other devices are put into promiscous
mode when secondary unicast addresses are present.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netdevice.h
net/core/dev.c
net/core/dev_mcast.c

index 9e114e77e54de5f7cd22d33918c25f8c8442bed8..2c0cc19edfb22125dfc25e23021d84dd02dd1b7a 100644 (file)
@@ -397,6 +397,9 @@ struct net_device
        unsigned char           addr_len;       /* hardware address length      */
        unsigned short          dev_id;         /* for shared network cards */
 
+       struct dev_addr_list    *uc_list;       /* Secondary unicast mac addresses */
+       int                     uc_count;       /* Number of installed ucasts   */
+       int                     uc_promisc;
        struct dev_addr_list    *mc_list;       /* Multicast mac addresses      */
        int                     mc_count;       /* Number of installed mcasts   */
        int                     promiscuity;
@@ -502,6 +505,8 @@ struct net_device
                                                void *saddr,
                                                unsigned len);
        int                     (*rebuild_header)(struct sk_buff *skb);
+#define HAVE_SET_RX_MODE
+       void                    (*set_rx_mode)(struct net_device *dev);
 #define HAVE_MULTICAST                  
        void                    (*set_multicast_list)(struct net_device *dev);
 #define HAVE_SET_MAC_ADDR               
@@ -1008,8 +1013,11 @@ extern struct net_device *alloc_netdev(int sizeof_priv, const char *name,
                                       void (*setup)(struct net_device *));
 extern int             register_netdev(struct net_device *dev);
 extern void            unregister_netdev(struct net_device *dev);
-/* Functions used for multicast support */
-extern void            dev_mc_upload(struct net_device *dev);
+/* Functions used for secondary unicast and multicast support */
+extern void            dev_set_rx_mode(struct net_device *dev);
+extern void            __dev_set_rx_mode(struct net_device *dev);
+extern int             dev_unicast_delete(struct net_device *dev, void *addr, int alen);
+extern int             dev_unicast_add(struct net_device *dev, void *addr, int alen);
 extern int             dev_mc_delete(struct net_device *dev, void *addr, int alen, int all);
 extern int             dev_mc_add(struct net_device *dev, void *addr, int alen, int newonly);
 extern void            dev_mc_discard(struct net_device *dev);
index 18759ccdf219b998d0c5ca95c6edf9e62004ebc2..36e9bf8ec4af24facfb95876f19ab55d8d1f57f3 100644 (file)
@@ -942,7 +942,7 @@ int dev_open(struct net_device *dev)
                /*
                 *      Initialize multicasting status
                 */
-               dev_mc_upload(dev);
+               dev_set_rx_mode(dev);
 
                /*
                 *      Wakeup transmit queue engine
@@ -2498,17 +2498,7 @@ int netdev_set_master(struct net_device *slave, struct net_device *master)
        return 0;
 }
 
-/**
- *     dev_set_promiscuity     - update promiscuity count on a device
- *     @dev: device
- *     @inc: modifier
- *
- *     Add or remove promiscuity from a device. While the count in the device
- *     remains above zero the interface remains promiscuous. Once it hits zero
- *     the device reverts back to normal filtering operation. A negative inc
- *     value is used to drop promiscuity on the device.
- */
-void dev_set_promiscuity(struct net_device *dev, int inc)
+static void __dev_set_promiscuity(struct net_device *dev, int inc)
 {
        unsigned short old_flags = dev->flags;
 
@@ -2517,7 +2507,6 @@ void dev_set_promiscuity(struct net_device *dev, int inc)
        else
                dev->flags |= IFF_PROMISC;
        if (dev->flags != old_flags) {
-               dev_mc_upload(dev);
                printk(KERN_INFO "device %s %s promiscuous mode\n",
                       dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
                                                               "left");
@@ -2530,6 +2519,25 @@ void dev_set_promiscuity(struct net_device *dev, int inc)
        }
 }
 
+/**
+ *     dev_set_promiscuity     - update promiscuity count on a device
+ *     @dev: device
+ *     @inc: modifier
+ *
+ *     Add or remove promiscuity from a device. While the count in the device
+ *     remains above zero the interface remains promiscuous. Once it hits zero
+ *     the device reverts back to normal filtering operation. A negative inc
+ *     value is used to drop promiscuity on the device.
+ */
+void dev_set_promiscuity(struct net_device *dev, int inc)
+{
+       unsigned short old_flags = dev->flags;
+
+       __dev_set_promiscuity(dev, inc);
+       if (dev->flags != old_flags)
+               dev_set_rx_mode(dev);
+}
+
 /**
  *     dev_set_allmulti        - update allmulti count on a device
  *     @dev: device
@@ -2550,7 +2558,48 @@ void dev_set_allmulti(struct net_device *dev, int inc)
        if ((dev->allmulti += inc) == 0)
                dev->flags &= ~IFF_ALLMULTI;
        if (dev->flags ^ old_flags)
-               dev_mc_upload(dev);
+               dev_set_rx_mode(dev);
+}
+
+/*
+ *     Upload unicast and multicast address lists to device and
+ *     configure RX filtering. When the device doesn't support unicast
+ *     filtering it is put in promiscous mode while unicast addresses
+ *     are present.
+ */
+void __dev_set_rx_mode(struct net_device *dev)
+{
+       /* dev_open will call this function so the list will stay sane. */
+       if (!(dev->flags&IFF_UP))
+               return;
+
+       if (!netif_device_present(dev))
+               return;
+
+       if (dev->set_rx_mode)
+               dev->set_rx_mode(dev);
+       else {
+               /* Unicast addresses changes may only happen under the rtnl,
+                * therefore calling __dev_set_promiscuity here is safe.
+                */
+               if (dev->uc_count > 0 && !dev->uc_promisc) {
+                       __dev_set_promiscuity(dev, 1);
+                       dev->uc_promisc = 1;
+               } else if (dev->uc_count == 0 && dev->uc_promisc) {
+                       __dev_set_promiscuity(dev, -1);
+                       dev->uc_promisc = 0;
+               }
+
+               if (dev->set_multicast_list)
+                       dev->set_multicast_list(dev);
+       }
+}
+
+void dev_set_rx_mode(struct net_device *dev)
+{
+       netif_tx_lock_bh(dev);
+       __dev_set_rx_mode(dev);
+       netif_tx_unlock_bh(dev);
 }
 
 int __dev_addr_delete(struct dev_addr_list **list, void *addr, int alen,
@@ -2622,6 +2671,66 @@ void __dev_addr_discard(struct dev_addr_list **list)
        }
 }
 
+/**
+ *     dev_unicast_delete      - Release secondary unicast address.
+ *     @dev: device
+ *
+ *     Release reference to a secondary unicast address and remove it
+ *     from the device if the reference count drop to zero.
+ *
+ *     The caller must hold the rtnl_mutex.
+ */
+int dev_unicast_delete(struct net_device *dev, void *addr, int alen)
+{
+       int err;
+
+       ASSERT_RTNL();
+
+       netif_tx_lock_bh(dev);
+       err = __dev_addr_delete(&dev->uc_list, addr, alen, 0);
+       if (!err) {
+               dev->uc_count--;
+               __dev_set_rx_mode(dev);
+       }
+       netif_tx_unlock_bh(dev);
+       return err;
+}
+EXPORT_SYMBOL(dev_unicast_delete);
+
+/**
+ *     dev_unicast_add         - add a secondary unicast address
+ *     @dev: device
+ *
+ *     Add a secondary unicast address to the device or increase
+ *     the reference count if it already exists.
+ *
+ *     The caller must hold the rtnl_mutex.
+ */
+int dev_unicast_add(struct net_device *dev, void *addr, int alen)
+{
+       int err;
+
+       ASSERT_RTNL();
+
+       netif_tx_lock_bh(dev);
+       err = __dev_addr_add(&dev->uc_list, addr, alen, 0);
+       if (!err) {
+               dev->uc_count++;
+               __dev_set_rx_mode(dev);
+       }
+       netif_tx_unlock_bh(dev);
+       return err;
+}
+EXPORT_SYMBOL(dev_unicast_add);
+
+static void dev_unicast_discard(struct net_device *dev)
+{
+       netif_tx_lock_bh(dev);
+       __dev_addr_discard(&dev->uc_list);
+       dev->uc_count = 0;
+       netif_tx_unlock_bh(dev);
+}
+
 unsigned dev_get_flags(const struct net_device *dev)
 {
        unsigned flags;
@@ -2665,7 +2774,7 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
         *      Load in the correct multicast list now the flags have changed.
         */
 
-       dev_mc_upload(dev);
+       dev_set_rx_mode(dev);
 
        /*
         *      Have we downed the interface. We handle IFF_UP ourselves
@@ -2678,7 +2787,7 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
                ret = ((old_flags & IFF_UP) ? dev_close : dev_open)(dev);
 
                if (!ret)
-                       dev_mc_upload(dev);
+                       dev_set_rx_mode(dev);
        }
 
        if (dev->flags & IFF_UP &&
@@ -3558,8 +3667,9 @@ void unregister_netdevice(struct net_device *dev)
        raw_notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);
 
        /*
-        *      Flush the multicast chain
+        *      Flush the unicast and multicast chains
         */
+       dev_unicast_discard(dev);
        dev_mc_discard(dev);
 
        if (dev->uninit)
index 702907434a47efe3493eba2a373c9d5cdc8fc55e..5cc9b448c44300bc3b77629039884cb846815230 100644 (file)
  *     We block accesses to device mc filters with netif_tx_lock.
  */
 
-/*
- *     Update the multicast list into the physical NIC controller.
- */
-
-static void __dev_mc_upload(struct net_device *dev)
-{
-       /* Don't do anything till we up the interface
-        * [dev_open will call this function so the list will
-        * stay sane]
-        */
-
-       if (!(dev->flags&IFF_UP))
-               return;
-
-       /*
-        *      Devices with no set multicast or which have been
-        *      detached don't get set.
-        */
-
-       if (dev->set_multicast_list == NULL ||
-           !netif_device_present(dev))
-               return;
-
-       dev->set_multicast_list(dev);
-}
-
-void dev_mc_upload(struct net_device *dev)
-{
-       netif_tx_lock_bh(dev);
-       __dev_mc_upload(dev);
-       netif_tx_unlock_bh(dev);
-}
-
 /*
  *     Delete a device level multicast
  */
@@ -114,7 +81,7 @@ int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl)
                 *      loaded filter is now wrong. Fix it
                 */
 
-               __dev_mc_upload(dev);
+               __dev_set_rx_mode(dev);
        }
        netif_tx_unlock_bh(dev);
        return err;
@@ -132,7 +99,7 @@ int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl)
        err = __dev_addr_add(&dev->mc_list, addr, alen, glbl);
        if (!err) {
                dev->mc_count++;
-               __dev_mc_upload(dev);
+               __dev_set_rx_mode(dev);
        }
        netif_tx_unlock_bh(dev);
        return err;