net-timestamp: extend SCM_TIMESTAMPING ancillary data struct

Applications that request kernel tx timestamps with SO_TIMESTAMPING
read timestamps as recvmsg() ancillary data. The response is defined
implicitly as timespec[3].

1) define struct scm_timestamping explicitly and

2) add support for new tstamp types. On tx, scm_timestamping always
   accompanies a sock_extended_err. Define previously unused field
   ee_info to signal the type of ts[0]. Introduce SCM_TSTAMP_SND to
   define the existing behavior.

The reception path is not modified. On rx, no struct similar to
sock_extended_err is passed along with SCM_TIMESTAMPING.

Signed-off-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Willem de Bruijn 2014-08-04 22:11:45 -04:00 committed by David S. Miller
parent a2b81b35f9
commit f24b9be595
5 changed files with 36 additions and 10 deletions

View File

@ -249,6 +249,9 @@ enum {
SKBTX_SHARED_FRAG = 1 << 5, SKBTX_SHARED_FRAG = 1 << 5,
}; };
#define SKBTX_ANY_SW_TSTAMP SKBTX_SW_TSTAMP
#define SKBTX_ANY_TSTAMP (SKBTX_HW_TSTAMP | SKBTX_ANY_SW_TSTAMP)
/* /*
* The callback notifies userspace to release buffers when skb DMA is done in * The callback notifies userspace to release buffers when skb DMA is done in
* lower device, the skb last reference should be 0 when calling this. * lower device, the skb last reference should be 0 when calling this.

View File

@ -2169,7 +2169,9 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
*/ */
if (sock_flag(sk, SOCK_RCVTSTAMP) || if (sock_flag(sk, SOCK_RCVTSTAMP) ||
sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE) || sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE) ||
(kt.tv64 && sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE)) || (kt.tv64 &&
(sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE) ||
skb_shinfo(skb)->tx_flags & SKBTX_ANY_SW_TSTAMP)) ||
(hwtstamps->hwtstamp.tv64 && (hwtstamps->hwtstamp.tv64 &&
sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE))) sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE)))
__sock_recv_timestamp(msg, sk, skb); __sock_recv_timestamp(msg, sk, skb);

View File

@ -22,5 +22,23 @@ struct sock_extended_err {
#define SO_EE_OFFENDER(ee) ((struct sockaddr*)((ee)+1)) #define SO_EE_OFFENDER(ee) ((struct sockaddr*)((ee)+1))
/**
* struct scm_timestamping - timestamps exposed through cmsg
*
* The timestamping interfaces SO_TIMESTAMPING, MSG_TSTAMP_*
* communicate network timestamps by passing this struct in a cmsg with
* recvmsg(). See Documentation/networking/timestamping.txt for details.
*/
struct scm_timestamping {
struct timespec ts[3];
};
/* The type of scm_timestamping, passed in sock_extended_err ee_info.
* This defines the type of ts[0]. For SCM_TSTAMP_SND only, if ts[0]
* is zero, then this is a hardware timestamp and recorded in ts[2].
*/
enum {
SCM_TSTAMP_SND, /* driver passed skb to NIC, or HW */
};
#endif /* _UAPI_LINUX_ERRQUEUE_H */ #endif /* _UAPI_LINUX_ERRQUEUE_H */

View File

@ -3521,6 +3521,7 @@ void skb_tstamp_tx(struct sk_buff *orig_skb,
memset(serr, 0, sizeof(*serr)); memset(serr, 0, sizeof(*serr));
serr->ee.ee_errno = ENOMSG; serr->ee.ee_errno = ENOMSG;
serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING; serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING;
serr->ee.ee_info = SCM_TSTAMP_SND;
err = sock_queue_err_skb(sk, skb); err = sock_queue_err_skb(sk, skb);

View File

@ -106,6 +106,7 @@
#include <linux/sockios.h> #include <linux/sockios.h>
#include <linux/atalk.h> #include <linux/atalk.h>
#include <net/busy_poll.h> #include <net/busy_poll.h>
#include <linux/errqueue.h>
#ifdef CONFIG_NET_RX_BUSY_POLL #ifdef CONFIG_NET_RX_BUSY_POLL
unsigned int sysctl_net_busy_read __read_mostly; unsigned int sysctl_net_busy_read __read_mostly;
@ -697,7 +698,7 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
struct sk_buff *skb) struct sk_buff *skb)
{ {
int need_software_tstamp = sock_flag(sk, SOCK_RCVTSTAMP); int need_software_tstamp = sock_flag(sk, SOCK_RCVTSTAMP);
struct timespec ts[3]; struct scm_timestamping tss;
int empty = 1; int empty = 1;
struct skb_shared_hwtstamps *shhwtstamps = struct skb_shared_hwtstamps *shhwtstamps =
skb_hwtstamps(skb); skb_hwtstamps(skb);
@ -714,24 +715,25 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP, put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP,
sizeof(tv), &tv); sizeof(tv), &tv);
} else { } else {
skb_get_timestampns(skb, &ts[0]); struct timespec ts;
skb_get_timestampns(skb, &ts);
put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS, put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS,
sizeof(ts[0]), &ts[0]); sizeof(ts), &ts);
} }
} }
memset(&tss, 0, sizeof(tss));
memset(ts, 0, sizeof(ts)); if ((sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE) ||
if (sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE) && skb_shinfo(skb)->tx_flags & SKBTX_ANY_SW_TSTAMP) &&
ktime_to_timespec_cond(skb->tstamp, ts + 0)) ktime_to_timespec_cond(skb->tstamp, tss.ts + 0))
empty = 0; empty = 0;
if (shhwtstamps && if (shhwtstamps &&
sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE) && sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE) &&
ktime_to_timespec_cond(shhwtstamps->hwtstamp, ts + 2)) ktime_to_timespec_cond(shhwtstamps->hwtstamp, tss.ts + 2))
empty = 0; empty = 0;
if (!empty) if (!empty)
put_cmsg(msg, SOL_SOCKET, put_cmsg(msg, SOL_SOCKET,
SCM_TIMESTAMPING, sizeof(ts), &ts); SCM_TIMESTAMPING, sizeof(tss), &tss);
} }
EXPORT_SYMBOL_GPL(__sock_recv_timestamp); EXPORT_SYMBOL_GPL(__sock_recv_timestamp);