]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/core/dev.c
[NET]: dev_mcast: switch to generic net_device address lists
[net-next-2.6.git] / net / core / dev.c
index 5a7f20f7857468aeab6ba56ba391c228a59efc91..18759ccdf219b998d0c5ca95c6edf9e62004ebc2 100644 (file)
@@ -1509,9 +1509,11 @@ int dev_queue_xmit(struct sk_buff *skb)
                skb_set_transport_header(skb, skb->csum_start -
                                              skb_headroom(skb));
 
-               if (!(dev->features & NETIF_F_GEN_CSUM) &&
-                   (!(dev->features & NETIF_F_IP_CSUM) ||
-                    skb->protocol != htons(ETH_P_IP)))
+               if (!(dev->features & NETIF_F_GEN_CSUM)
+                   || ((dev->features & NETIF_F_IP_CSUM)
+                       && skb->protocol == htons(ETH_P_IP))
+                   || ((dev->features & NETIF_F_IPV6_CSUM)
+                       && skb->protocol == htons(ETH_P_IPV6)))
                        if (skb_checksum_help(skb))
                                goto out_kfree_skb;
        }
@@ -2009,6 +2011,7 @@ static void net_rx_action(struct softirq_action *h)
                }
        }
 out:
+       local_irq_enable();
 #ifdef CONFIG_NET_DMA
        /*
         * There may not be any more sk_buffs coming right now, so push
@@ -2022,7 +2025,6 @@ out:
                rcu_read_unlock();
        }
 #endif
-       local_irq_enable();
        return;
 
 softnet_break:
@@ -2551,6 +2553,75 @@ void dev_set_allmulti(struct net_device *dev, int inc)
                dev_mc_upload(dev);
 }
 
+int __dev_addr_delete(struct dev_addr_list **list, void *addr, int alen,
+                     int glbl)
+{
+       struct dev_addr_list *da;
+
+       for (; (da = *list) != NULL; list = &da->next) {
+               if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
+                   alen == da->da_addrlen) {
+                       if (glbl) {
+                               int old_glbl = da->da_gusers;
+                               da->da_gusers = 0;
+                               if (old_glbl == 0)
+                                       break;
+                       }
+                       if (--da->da_users)
+                               return 0;
+
+                       *list = da->next;
+                       kfree(da);
+                       return 0;
+               }
+       }
+       return -ENOENT;
+}
+
+int __dev_addr_add(struct dev_addr_list **list, void *addr, int alen, int glbl)
+{
+       struct dev_addr_list *da;
+
+       for (da = *list; da != NULL; da = da->next) {
+               if (memcmp(da->da_addr, addr, da->da_addrlen) == 0 &&
+                   da->da_addrlen == alen) {
+                       if (glbl) {
+                               int old_glbl = da->da_gusers;
+                               da->da_gusers = 1;
+                               if (old_glbl)
+                                       return 0;
+                       }
+                       da->da_users++;
+                       return 0;
+               }
+       }
+
+       da = kmalloc(sizeof(*da), GFP_ATOMIC);
+       if (da == NULL)
+               return -ENOMEM;
+       memcpy(da->da_addr, addr, alen);
+       da->da_addrlen = alen;
+       da->da_users = 1;
+       da->da_gusers = glbl ? 1 : 0;
+       da->next = *list;
+       *list = da;
+       return 0;
+}
+
+void __dev_addr_discard(struct dev_addr_list **list)
+{
+       struct dev_addr_list *tmp;
+
+       while (*list != NULL) {
+               tmp = *list;
+               *list = tmp->next;
+               if (tmp->da_users > tmp->da_gusers)
+                       printk("__dev_addr_discard: address leakage! "
+                              "da_users=%d\n", tmp->da_users);
+               kfree(tmp);
+       }
+}
+
 unsigned dev_get_flags(const struct net_device *dev)
 {
        unsigned flags;
@@ -2577,7 +2648,7 @@ unsigned dev_get_flags(const struct net_device *dev)
 
 int dev_change_flags(struct net_device *dev, unsigned flags)
 {
-       int ret;
+       int ret, changes;
        int old_flags = dev->flags;
 
        /*
@@ -2632,8 +2703,10 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
                dev_set_allmulti(dev, inc);
        }
 
-       if (old_flags ^ dev->flags)
-               rtmsg_ifinfo(RTM_NEWLINK, dev, old_flags ^ dev->flags);
+       /* Exclude state transition flags, already notified */
+       changes = (old_flags ^ dev->flags) & ~(IFF_UP | IFF_RUNNING);
+       if (changes)
+               rtmsg_ifinfo(RTM_NEWLINK, dev, changes);
 
        return ret;
 }
@@ -3105,6 +3178,22 @@ int register_netdevice(struct net_device *dev)
                }
        }
 
+       /* Fix illegal checksum combinations */
+       if ((dev->features & NETIF_F_HW_CSUM) &&
+           (dev->features & (NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
+               printk(KERN_NOTICE "%s: mixed HW and IP checksum settings.\n",
+                      dev->name);
+               dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM);
+       }
+
+       if ((dev->features & NETIF_F_NO_CSUM) &&
+           (dev->features & (NETIF_F_HW_CSUM|NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM))) {
+               printk(KERN_NOTICE "%s: mixed no checksumming and other settings.\n",
+                      dev->name);
+               dev->features &= ~(NETIF_F_IP_CSUM|NETIF_F_IPV6_CSUM|NETIF_F_HW_CSUM);
+       }
+
+
        /* Fix illegal SG+CSUM combinations. */
        if ((dev->features & NETIF_F_SG) &&
            !(dev->features & NETIF_F_ALL_CSUM)) {