packet: tx timestamping on tpacket ring
authorWillem de Bruijn <willemb@google.com>
Tue, 23 Apr 2013 00:39:28 +0000 (00:39 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 25 Apr 2013 05:22:22 +0000 (01:22 -0400)
When transmit timestamping is enabled at the socket level, record a
timestamp on packets written to a PACKET_TX_RING. Tx timestamps are
always looped to the application over the socket error queue. Software
timestamps are also written back into the packet frame header in the
packet ring.

Reported-by: Paul Chavent <paul.chavent@onera.fr>
Signed-off-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/core/skbuff.c
net/packet/af_packet.c

index 898cf5c566f921c93fbbaf1a5ee7d0fc0bb72619..af9185d0be6a9e3b3b81c10d061b82992c40fc1d 100644 (file)
@@ -3327,12 +3327,8 @@ void skb_tstamp_tx(struct sk_buff *orig_skb,
        if (!sk)
                return;
 
-       skb = skb_clone(orig_skb, GFP_ATOMIC);
-       if (!skb)
-               return;
-
        if (hwtstamps) {
-               *skb_hwtstamps(skb) =
+               *skb_hwtstamps(orig_skb) =
                        *hwtstamps;
        } else {
                /*
@@ -3340,9 +3336,13 @@ void skb_tstamp_tx(struct sk_buff *orig_skb,
                 * so keep the shared tx_flags and only
                 * store software time stamp
                 */
-               skb->tstamp = ktime_get_real();
+               orig_skb->tstamp = ktime_get_real();
        }
 
+       skb = skb_clone(orig_skb, GFP_ATOMIC);
+       if (!skb)
+               return;
+
        serr = SKB_EXT_ERR(skb);
        memset(serr, 0, sizeof(*serr));
        serr->ee.ee_errno = ENOMSG;
index 7e387ff64465c934075993f77c4def101d2160de..ec8ea27733acafe6b360a83e8e63e79ed92d7f67 100644 (file)
@@ -339,6 +339,37 @@ static int __packet_get_status(struct packet_sock *po, void *frame)
        }
 }
 
+static void __packet_set_timestamp(struct packet_sock *po, void *frame,
+                                  ktime_t tstamp)
+{
+       union tpacket_uhdr h;
+       struct timespec ts;
+
+       if (!ktime_to_timespec_cond(tstamp, &ts) ||
+           !sock_flag(&po->sk, SOCK_TIMESTAMPING_SOFTWARE))
+               return;
+
+       h.raw = frame;
+       switch (po->tp_version) {
+       case TPACKET_V1:
+               h.h1->tp_sec = ts.tv_sec;
+               h.h1->tp_usec = ts.tv_nsec / NSEC_PER_USEC;
+               break;
+       case TPACKET_V2:
+               h.h2->tp_sec = ts.tv_sec;
+               h.h2->tp_nsec = ts.tv_nsec;
+               break;
+       case TPACKET_V3:
+       default:
+               WARN(1, "TPACKET version not supported.\n");
+               BUG();
+       }
+
+       /* one flush is safe, as both fields always lie on the same cacheline */
+       flush_dcache_page(pgv_to_page(&h.h1->tp_sec));
+       smp_wmb();
+}
+
 static void *packet_lookup_frame(struct packet_sock *po,
                struct packet_ring_buffer *rb,
                unsigned int position,
@@ -1877,6 +1908,7 @@ static void tpacket_destruct_skb(struct sk_buff *skb)
                ph = skb_shinfo(skb)->destructor_arg;
                BUG_ON(atomic_read(&po->tx_ring.pending) == 0);
                atomic_dec(&po->tx_ring.pending);
+               __packet_set_timestamp(po, ph, skb->tstamp);
                __packet_set_status(po, ph, TP_STATUS_AVAILABLE);
        }
 
@@ -1900,6 +1932,7 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
        skb->dev = dev;
        skb->priority = po->sk.sk_priority;
        skb->mark = po->sk.sk_mark;
+       sock_tx_timestamp(&po->sk, &skb_shinfo(skb)->tx_flags);
        skb_shinfo(skb)->destructor_arg = ph.raw;
 
        switch (po->tp_version) {