ipv4: Handle PMTU in all ICMP error handlers.
[deliverable/linux.git] / net / ipv4 / ipip.c
index ae1413e3f2f8bd7a16ed73bdd327f908a6012a28..715338a1b205a07195e31878f50cdbef31864c86 100644 (file)
@@ -144,33 +144,48 @@ static void ipip_dev_free(struct net_device *dev);
 
 /* often modified stats are per cpu, other are shared (netdev->stats) */
 struct pcpu_tstats {
-       unsigned long   rx_packets;
-       unsigned long   rx_bytes;
-       unsigned long   tx_packets;
-       unsigned long   tx_bytes;
-} __attribute__((aligned(4*sizeof(unsigned long))));
+       u64     rx_packets;
+       u64     rx_bytes;
+       u64     tx_packets;
+       u64     tx_bytes;
+       struct u64_stats_sync   syncp;
+};
 
-static struct net_device_stats *ipip_get_stats(struct net_device *dev)
+static struct rtnl_link_stats64 *ipip_get_stats64(struct net_device *dev,
+                                                 struct rtnl_link_stats64 *tot)
 {
-       struct pcpu_tstats sum = { 0 };
        int i;
 
        for_each_possible_cpu(i) {
                const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i);
-
-               sum.rx_packets += tstats->rx_packets;
-               sum.rx_bytes   += tstats->rx_bytes;
-               sum.tx_packets += tstats->tx_packets;
-               sum.tx_bytes   += tstats->tx_bytes;
+               u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
+               unsigned int start;
+
+               do {
+                       start = u64_stats_fetch_begin_bh(&tstats->syncp);
+                       rx_packets = tstats->rx_packets;
+                       tx_packets = tstats->tx_packets;
+                       rx_bytes = tstats->rx_bytes;
+                       tx_bytes = tstats->tx_bytes;
+               } while (u64_stats_fetch_retry_bh(&tstats->syncp, start));
+
+               tot->rx_packets += rx_packets;
+               tot->tx_packets += tx_packets;
+               tot->rx_bytes   += rx_bytes;
+               tot->tx_bytes   += tx_bytes;
        }
-       dev->stats.rx_packets = sum.rx_packets;
-       dev->stats.rx_bytes   = sum.rx_bytes;
-       dev->stats.tx_packets = sum.tx_packets;
-       dev->stats.tx_bytes   = sum.tx_bytes;
-       return &dev->stats;
+
+       tot->tx_fifo_errors = dev->stats.tx_fifo_errors;
+       tot->tx_carrier_errors = dev->stats.tx_carrier_errors;
+       tot->tx_dropped = dev->stats.tx_dropped;
+       tot->tx_aborted_errors = dev->stats.tx_aborted_errors;
+       tot->tx_errors = dev->stats.tx_errors;
+       tot->collisions = dev->stats.collisions;
+
+       return tot;
 }
 
-static struct ip_tunnel * ipip_tunnel_lookup(struct net *net,
+static struct ip_tunnel *ipip_tunnel_lookup(struct net *net,
                __be32 remote, __be32 local)
 {
        unsigned int h0 = HASH(remote);
@@ -245,7 +260,7 @@ static void ipip_tunnel_link(struct ipip_net *ipn, struct ip_tunnel *t)
        rcu_assign_pointer(*tp, t);
 }
 
-static struct ip_tunnel * ipip_tunnel_locate(struct net *net,
+static struct ip_tunnel *ipip_tunnel_locate(struct net *net,
                struct ip_tunnel_parm *parms, int create)
 {
        __be32 remote = parms->iph.daddr;
@@ -333,9 +348,6 @@ static int ipip_err(struct sk_buff *skb, u32 info)
                case ICMP_PORT_UNREACH:
                        /* Impossible event. */
                        return 0;
-               case ICMP_FRAG_NEEDED:
-                       /* Soft state for pmtu is maintained by IP core. */
-                       return 0;
                default:
                        /* All others are translated to HOST_UNREACH.
                           rfc2003 contains "deep thoughts" about NET_UNREACH,
@@ -354,7 +366,17 @@ static int ipip_err(struct sk_buff *skb, u32 info)
 
        rcu_read_lock();
        t = ipip_tunnel_lookup(dev_net(skb->dev), iph->daddr, iph->saddr);
-       if (t == NULL || t->parms.iph.daddr == 0)
+       if (t == NULL)
+               goto out;
+
+       if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
+               ipv4_update_pmtu(skb, dev_net(skb->dev), info,
+                                t->dev->ifindex, 0, IPPROTO_IPIP, 0);
+               err = 0;
+               goto out;
+       }
+
+       if (t->parms.iph.daddr == 0)
                goto out;
 
        err = 0;
@@ -404,8 +426,10 @@ static int ipip_rcv(struct sk_buff *skb)
                skb->pkt_type = PACKET_HOST;
 
                tstats = this_cpu_ptr(tunnel->dev->tstats);
+               u64_stats_update_begin(&tstats->syncp);
                tstats->rx_packets++;
                tstats->rx_bytes += skb->len;
+               u64_stats_update_end(&tstats->syncp);
 
                __skb_tunnel_rx(skb, tunnel->dev);
 
@@ -730,7 +754,7 @@ static const struct net_device_ops ipip_netdev_ops = {
        .ndo_start_xmit = ipip_tunnel_xmit,
        .ndo_do_ioctl   = ipip_tunnel_ioctl,
        .ndo_change_mtu = ipip_tunnel_change_mtu,
-       .ndo_get_stats  = ipip_get_stats,
+       .ndo_get_stats64 = ipip_get_stats64,
 };
 
 static void ipip_dev_free(struct net_device *dev)
This page took 0.024729 seconds and 5 git commands to generate.