ipv4: use a 64bit load/store in output path
Eric Dumazet [Wed, 30 Nov 2011 19:00:53 +0000 (19:00 +0000)]
gcc compiler is smart enough to use a single load/store if we
memcpy(dptr, sptr, 8) on x86_64, regardless of
CONFIG_CC_OPTIMIZE_FOR_SIZE

In IP header, daddr immediately follows saddr, this wont change in the
future. We only need to make sure our flowi4 (saddr,daddr) fields wont
break the rule.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

include/net/flow.h
net/ipv4/ip_output.c

index a094477..9192d69 100644 (file)
@@ -59,8 +59,11 @@ struct flowi4 {
 #define flowi4_proto           __fl_common.flowic_proto
 #define flowi4_flags           __fl_common.flowic_flags
 #define flowi4_secid           __fl_common.flowic_secid
-       __be32                  daddr;
+
+       /* (saddr,daddr) must be grouped, same order as in IP header */
        __be32                  saddr;
+       __be32                  daddr;
+
        union flowi_uli         uli;
 #define fl4_sport              uli.ports.sport
 #define fl4_dport              uli.ports.dport
index 0bc95f3..0d5e567 100644 (file)
@@ -319,6 +319,20 @@ int ip_output(struct sk_buff *skb)
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
 }
 
+/*
+ * copy saddr and daddr, possibly using 64bit load/stores
+ * Equivalent to :
+ *   iph->saddr = fl4->saddr;
+ *   iph->daddr = fl4->daddr;
+ */
+static void ip_copy_addrs(struct iphdr *iph, const struct flowi4 *fl4)
+{
+       BUILD_BUG_ON(offsetof(typeof(*fl4), daddr) !=
+                    offsetof(typeof(*fl4), saddr) + sizeof(fl4->saddr));
+       memcpy(&iph->saddr, &fl4->saddr,
+              sizeof(fl4->saddr) + sizeof(fl4->daddr));
+}
+
 int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl)
 {
        struct sock *sk = skb->sk;
@@ -381,8 +395,8 @@ packet_routed:
                iph->frag_off = 0;
        iph->ttl      = ip_select_ttl(inet, &rt->dst);
        iph->protocol = sk->sk_protocol;
-       iph->saddr    = fl4->saddr;
-       iph->daddr    = fl4->daddr;
+       ip_copy_addrs(iph, fl4);
+
        /* Transport layer set skb->h.foo itself. */
 
        if (inet_opt && inet_opt->opt.optlen) {
@@ -1337,8 +1351,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
        ip_select_ident(iph, &rt->dst, sk);
        iph->ttl = ttl;
        iph->protocol = sk->sk_protocol;
-       iph->saddr = fl4->saddr;
-       iph->daddr = fl4->daddr;
+       ip_copy_addrs(iph, fl4);
 
        if (opt) {
                iph->ihl += opt->optlen>>2;