diff --git a/include/uapi/linux/if_packet.h b/include/uapi/linux/if_packet.h index 4dfc234d80e5..b950c02030c0 100644 --- a/include/uapi/linux/if_packet.h +++ b/include/uapi/linux/if_packet.h @@ -100,6 +100,11 @@ struct tpacket_auxdata { #define TP_STATUS_SENDING (1 << 1) #define TP_STATUS_WRONG_FORMAT (1 << 2) +/* Rx and Tx ring - header status */ +#define TP_STATUS_TS_SOFTWARE (1 << 29) +#define TP_STATUS_TS_SYS_HARDWARE (1 << 30) +#define TP_STATUS_TS_RAW_HARDWARE (1 << 31) + /* Rx ring - feature request bits */ #define TP_FT_REQ_FILL_RXHASH 0x1 diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 9d46a07a15c0..ba8309a3e01b 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -339,34 +339,35 @@ static int __packet_get_status(struct packet_sock *po, void *frame) } } -static bool tpacket_get_timestamp(struct sk_buff *skb, struct timespec *ts, - unsigned int flags) +static __u32 tpacket_get_timestamp(struct sk_buff *skb, struct timespec *ts, + unsigned int flags) { struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); if (shhwtstamps) { if ((flags & SOF_TIMESTAMPING_SYS_HARDWARE) && ktime_to_timespec_cond(shhwtstamps->syststamp, ts)) - return true; + return TP_STATUS_TS_SYS_HARDWARE; if ((flags & SOF_TIMESTAMPING_RAW_HARDWARE) && ktime_to_timespec_cond(shhwtstamps->hwtstamp, ts)) - return true; + return TP_STATUS_TS_RAW_HARDWARE; } if (ktime_to_timespec_cond(skb->tstamp, ts)) - return true; + return TP_STATUS_TS_SOFTWARE; - return false; + return 0; } -static void __packet_set_timestamp(struct packet_sock *po, void *frame, - struct sk_buff *skb) +static __u32 __packet_set_timestamp(struct packet_sock *po, void *frame, + struct sk_buff *skb) { union tpacket_uhdr h; struct timespec ts; + __u32 ts_status; - if (!tpacket_get_timestamp(skb, &ts, po->tp_tstamp)) - return; + if (!(ts_status = tpacket_get_timestamp(skb, &ts, po->tp_tstamp))) + return 0; h.raw = frame; switch (po->tp_version) { @@ -387,6 +388,8 @@ static void __packet_set_timestamp(struct packet_sock *po, void *frame, /* 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(); + + return ts_status; } static void *packet_lookup_frame(struct packet_sock *po, @@ -1721,6 +1724,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, unsigned short macoff, netoff, hdrlen; struct sk_buff *copy_skb = NULL; struct timespec ts; + __u32 ts_status; if (skb->pkt_type == PACKET_LOOPBACK) goto drop; @@ -1803,9 +1807,12 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, spin_unlock(&sk->sk_receive_queue.lock); skb_copy_bits(skb, 0, h.raw + macoff, snaplen); - if (!tpacket_get_timestamp(skb, &ts, po->tp_tstamp)) + + if (!(ts_status = tpacket_get_timestamp(skb, &ts, po->tp_tstamp))) getnstimeofday(&ts); + status |= ts_status; + switch (po->tp_version) { case TPACKET_V1: h.h1->tp_len = skb->len; @@ -1905,11 +1912,14 @@ static void tpacket_destruct_skb(struct sk_buff *skb) void *ph; if (likely(po->tx_ring.pg_vec)) { + __u32 ts; + 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); - __packet_set_status(po, ph, TP_STATUS_AVAILABLE); + + ts = __packet_set_timestamp(po, ph, skb); + __packet_set_status(po, ph, TP_STATUS_AVAILABLE | ts); } sock_wfree(skb);