tcp: make sure xmit goal size never becomes zero
[safe/jmp/linux-2.6] / net / ipv4 / devinet.c
index f8c0b0a..126bb91 100644 (file)
@@ -112,13 +112,7 @@ static inline void devinet_sysctl_unregister(struct in_device *idev)
 
 static struct in_ifaddr *inet_alloc_ifa(void)
 {
-       struct in_ifaddr *ifa = kzalloc(sizeof(*ifa), GFP_KERNEL);
-
-       if (ifa) {
-               INIT_RCU_HEAD(&ifa->rcu_head);
-       }
-
-       return ifa;
+       return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
 }
 
 static void inet_rcu_free_ifa(struct rcu_head *head)
@@ -138,8 +132,8 @@ void in_dev_finish_destroy(struct in_device *idev)
 {
        struct net_device *dev = idev->dev;
 
-       BUG_TRAP(!idev->ifa_list);
-       BUG_TRAP(!idev->mc_list);
+       WARN_ON(idev->ifa_list);
+       WARN_ON(idev->mc_list);
 #ifdef NET_REFCNT_DEBUG
        printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n",
               idev, dev ? dev->name : "NIL");
@@ -161,13 +155,14 @@ static struct in_device *inetdev_init(struct net_device *dev)
        in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
        if (!in_dev)
                goto out;
-       INIT_RCU_HEAD(&in_dev->rcu_head);
        memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
                        sizeof(in_dev->cnf));
        in_dev->cnf.sysctl = NULL;
        in_dev->dev = dev;
        if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL)
                goto out_kfree;
+       if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
+               dev_disable_lro(dev);
        /* Reference in_dev->dev */
        dev_hold(dev);
        /* Account for reference dev->ip_ptr (below) */
@@ -397,7 +392,7 @@ static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
        }
        ipv4_devconf_setall(in_dev);
        if (ifa->ifa_dev != in_dev) {
-               BUG_TRAP(!ifa->ifa_dev);
+               WARN_ON(ifa->ifa_dev);
                in_dev_hold(in_dev);
                ifa->ifa_dev = in_dev;
        }
@@ -611,9 +606,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
        if (colon)
                *colon = 0;
 
-#ifdef CONFIG_KMOD
        dev_load(net, ifr.ifr_name);
-#endif
 
        switch (cmd) {
        case SIOCGIFADDR:       /* Get interface address */
@@ -1027,6 +1020,11 @@ skip:
        }
 }
 
+static inline bool inetdev_valid_mtu(unsigned mtu)
+{
+       return mtu >= 68;
+}
+
 /* Called only under RTNL semaphore */
 
 static int inetdev_event(struct notifier_block *this, unsigned long event,
@@ -1046,6 +1044,10 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
                                IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
                                IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
                        }
+               } else if (event == NETDEV_CHANGEMTU) {
+                       /* Re-enabling IP */
+                       if (inetdev_valid_mtu(dev->mtu))
+                               in_dev = inetdev_init(dev);
                }
                goto out;
        }
@@ -1056,7 +1058,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
                dev->ip_ptr = NULL;
                break;
        case NETDEV_UP:
-               if (dev->mtu < 68)
+               if (!inetdev_valid_mtu(dev->mtu))
                        break;
                if (dev->flags & IFF_LOOPBACK) {
                        struct in_ifaddr *ifa;
@@ -1073,14 +1075,22 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
                        }
                }
                ip_mc_up(in_dev);
+               /* fall through */
+       case NETDEV_CHANGEADDR:
+               if (IN_DEV_ARP_NOTIFY(in_dev))
+                       arp_send(ARPOP_REQUEST, ETH_P_ARP,
+                                in_dev->ifa_list->ifa_address,
+                                dev,
+                                in_dev->ifa_list->ifa_address,
+                                NULL, dev->dev_addr, NULL);
                break;
        case NETDEV_DOWN:
                ip_mc_down(in_dev);
                break;
        case NETDEV_CHANGEMTU:
-               if (dev->mtu >= 68)
+               if (inetdev_valid_mtu(dev->mtu))
                        break;
-               /* MTU falled under 68, disable IP */
+               /* disable IP when MTU is not enough */
        case NETDEV_UNREGISTER:
                inetdev_destroy(in_dev);
                break;
@@ -1099,7 +1109,7 @@ out:
 }
 
 static struct notifier_block ip_netdev_notifier = {
-       .notifier_call =inetdev_event,
+       .notifier_call = inetdev_event,
 };
 
 static inline size_t inet_nlmsg_size(void)
@@ -1186,7 +1196,7 @@ done:
        return skb->len;
 }
 
-static void rtmsg_ifa(int event, struct in_ifaddrifa, struct nlmsghdr *nlh,
+static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
                      u32 pid)
 {
        struct sk_buff *skb;
@@ -1206,7 +1216,8 @@ static void rtmsg_ifa(int event, struct in_ifaddr* ifa, struct nlmsghdr *nlh,
                kfree_skb(skb);
                goto errout;
        }
-       err = rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
+       rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
+       return;
 errout:
        if (err < 0)
                rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
@@ -1241,6 +1252,8 @@ static void inet_forward_change(struct net *net)
        read_lock(&dev_base_lock);
        for_each_netdev(net, dev) {
                struct in_device *in_dev;
+               if (on)
+                       dev_disable_lro(dev);
                rcu_read_lock();
                in_dev = __in_dev_get_rcu(dev);
                if (in_dev)
@@ -1248,12 +1261,10 @@ static void inet_forward_change(struct net *net)
                rcu_read_unlock();
        }
        read_unlock(&dev_base_lock);
-
-       rt_cache_flush(0);
 }
 
 static int devinet_conf_proc(ctl_table *ctl, int write,
-                            struct filefilp, void __user *buffer,
+                            struct file *filp, void __user *buffer,
                             size_t *lenp, loff_t *ppos)
 {
        int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
@@ -1272,7 +1283,7 @@ static int devinet_conf_proc(ctl_table *ctl, int write,
        return ret;
 }
 
-static int devinet_conf_sysctl(ctl_table *table, int __user *name, int nlen,
+static int devinet_conf_sysctl(ctl_table *table,
                               void __user *oldval, size_t __user *oldlenp,
                               void __user *newval, size_t newlen)
 {
@@ -1325,7 +1336,7 @@ static int devinet_conf_sysctl(ctl_table *table, int __user *name, int nlen,
 }
 
 static int devinet_sysctl_forward(ctl_table *ctl, int write,
-                                 struct filefilp, void __user *buffer,
+                                 struct file *filp, void __user *buffer,
                                  size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
@@ -1335,38 +1346,48 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write,
        if (write && *valp != val) {
                struct net *net = ctl->extra2;
 
-               if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING))
-                       inet_forward_change(net);
-               else if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING))
-                       rt_cache_flush(0);
+               if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
+                       rtnl_lock();
+                       if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
+                               inet_forward_change(net);
+                       } else if (*valp) {
+                               struct ipv4_devconf *cnf = ctl->extra1;
+                               struct in_device *idev =
+                                       container_of(cnf, struct in_device, cnf);
+                               dev_disable_lro(idev->dev);
+                       }
+                       rtnl_unlock();
+                       rt_cache_flush(net, 0);
+               }
        }
 
        return ret;
 }
 
 int ipv4_doint_and_flush(ctl_table *ctl, int write,
-                        struct filefilp, void __user *buffer,
+                        struct file *filp, void __user *buffer,
                         size_t *lenp, loff_t *ppos)
 {
        int *valp = ctl->data;
        int val = *valp;
        int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
+       struct net *net = ctl->extra2;
 
        if (write && *valp != val)
-               rt_cache_flush(0);
+               rt_cache_flush(net, 0);
 
        return ret;
 }
 
-int ipv4_doint_and_flush_strategy(ctl_table *table, int __user *name, int nlen,
+int ipv4_doint_and_flush_strategy(ctl_table *table,
                                  void __user *oldval, size_t __user *oldlenp,
                                  void __user *newval, size_t newlen)
 {
-       int ret = devinet_conf_sysctl(table, name, nlen, oldval, oldlenp,
-                                     newval, newlen);
+       int ret = devinet_conf_sysctl(table, oldval, oldlenp, newval, newlen);
+       struct net *net = table->extra2;
 
        if (ret == 1)
-               rt_cache_flush(0);
+               rt_cache_flush(net, 0);
 
        return ret;
 }
@@ -1427,6 +1448,7 @@ static struct devinet_sysctl_table {
                DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
                DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
                DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
+               DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
 
                DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
                DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),