forked from mirrors/linux
		
	net: socket infrastructure for SO_TIMESTAMPING
The overlap with the old SO_TIMESTAMP[NS] options is handled so that time stamping in software (net_enable_timestamp()) is enabled when SO_TIMESTAMP[NS] and/or SO_TIMESTAMPING_RX_SOFTWARE is set. It's disabled if all of these are off. Signed-off-by: Patrick Ohly <patrick.ohly@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									ac45f602ee
								
							
						
					
					
						commit
						20d4947353
					
				
					 4 changed files with 185 additions and 41 deletions
				
			
		| 
						 | 
				
			
			@ -158,7 +158,7 @@ struct sock_common {
 | 
			
		|||
  *	@sk_allocation: allocation mode
 | 
			
		||||
  *	@sk_sndbuf: size of send buffer in bytes
 | 
			
		||||
  *	@sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE,
 | 
			
		||||
  *		   %SO_OOBINLINE settings
 | 
			
		||||
  *		   %SO_OOBINLINE settings, %SO_TIMESTAMPING settings
 | 
			
		||||
  *	@sk_no_check: %SO_NO_CHECK setting, wether or not checkup packets
 | 
			
		||||
  *	@sk_route_caps: route capabilities (e.g. %NETIF_F_TSO)
 | 
			
		||||
  *	@sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4)
 | 
			
		||||
| 
						 | 
				
			
			@ -488,6 +488,13 @@ enum sock_flags {
 | 
			
		|||
	SOCK_RCVTSTAMPNS, /* %SO_TIMESTAMPNS setting */
 | 
			
		||||
	SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */
 | 
			
		||||
	SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */
 | 
			
		||||
	SOCK_TIMESTAMPING_TX_HARDWARE,  /* %SOF_TIMESTAMPING_TX_HARDWARE */
 | 
			
		||||
	SOCK_TIMESTAMPING_TX_SOFTWARE,  /* %SOF_TIMESTAMPING_TX_SOFTWARE */
 | 
			
		||||
	SOCK_TIMESTAMPING_RX_HARDWARE,  /* %SOF_TIMESTAMPING_RX_HARDWARE */
 | 
			
		||||
	SOCK_TIMESTAMPING_RX_SOFTWARE,  /* %SOF_TIMESTAMPING_RX_SOFTWARE */
 | 
			
		||||
	SOCK_TIMESTAMPING_SOFTWARE,     /* %SOF_TIMESTAMPING_SOFTWARE */
 | 
			
		||||
	SOCK_TIMESTAMPING_RAW_HARDWARE, /* %SOF_TIMESTAMPING_RAW_HARDWARE */
 | 
			
		||||
	SOCK_TIMESTAMPING_SYS_HARDWARE, /* %SOF_TIMESTAMPING_SYS_HARDWARE */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline void sock_copy_flags(struct sock *nsk, struct sock *osk)
 | 
			
		||||
| 
						 | 
				
			
			@ -1346,13 +1353,44 @@ static __inline__ void
 | 
			
		|||
sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
 | 
			
		||||
{
 | 
			
		||||
	ktime_t kt = skb->tstamp;
 | 
			
		||||
	struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb);
 | 
			
		||||
 | 
			
		||||
	if (sock_flag(sk, SOCK_RCVTSTAMP))
 | 
			
		||||
	/*
 | 
			
		||||
	 * generate control messages if
 | 
			
		||||
	 * - receive time stamping in software requested (SOCK_RCVTSTAMP
 | 
			
		||||
	 *   or SOCK_TIMESTAMPING_RX_SOFTWARE)
 | 
			
		||||
	 * - software time stamp available and wanted
 | 
			
		||||
	 *   (SOCK_TIMESTAMPING_SOFTWARE)
 | 
			
		||||
	 * - hardware time stamps available and wanted
 | 
			
		||||
	 *   (SOCK_TIMESTAMPING_SYS_HARDWARE or
 | 
			
		||||
	 *   SOCK_TIMESTAMPING_RAW_HARDWARE)
 | 
			
		||||
	 */
 | 
			
		||||
	if (sock_flag(sk, SOCK_RCVTSTAMP) ||
 | 
			
		||||
	    sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE) ||
 | 
			
		||||
	    (kt.tv64 && sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE)) ||
 | 
			
		||||
	    (hwtstamps->hwtstamp.tv64 &&
 | 
			
		||||
	     sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE)) ||
 | 
			
		||||
	    (hwtstamps->syststamp.tv64 &&
 | 
			
		||||
	     sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE)))
 | 
			
		||||
		__sock_recv_timestamp(msg, sk, skb);
 | 
			
		||||
	else
 | 
			
		||||
		sk->sk_stamp = kt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * sock_tx_timestamp - checks whether the outgoing packet is to be time stamped
 | 
			
		||||
 * @msg:	outgoing packet
 | 
			
		||||
 * @sk:		socket sending this packet
 | 
			
		||||
 * @shtx:	filled with instructions for time stamping
 | 
			
		||||
 *
 | 
			
		||||
 * Currently only depends on SOCK_TIMESTAMPING* flags. Returns error code if
 | 
			
		||||
 * parameters are invalid.
 | 
			
		||||
 */
 | 
			
		||||
extern int sock_tx_timestamp(struct msghdr *msg,
 | 
			
		||||
			     struct sock *sk,
 | 
			
		||||
			     union skb_shared_tx *shtx);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * sk_eat_skb - Release a skb if it is no longer needed
 | 
			
		||||
 * @sk: socket to eat this skb from
 | 
			
		||||
| 
						 | 
				
			
			@ -1421,7 +1459,7 @@ static inline struct sock *skb_steal_sock(struct sk_buff *skb)
 | 
			
		|||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern void sock_enable_timestamp(struct sock *sk);
 | 
			
		||||
extern void sock_enable_timestamp(struct sock *sk, int flag);
 | 
			
		||||
extern int sock_get_timestamp(struct sock *, struct timeval __user *);
 | 
			
		||||
extern int sock_get_timestampns(struct sock *, struct timespec __user *);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										19
									
								
								net/compat.c
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								net/compat.c
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -216,7 +216,7 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, struct sock *sk,
 | 
			
		|||
int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct compat_timeval ctv;
 | 
			
		||||
	struct compat_timespec cts;
 | 
			
		||||
	struct compat_timespec cts[3];
 | 
			
		||||
	struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control;
 | 
			
		||||
	struct compat_cmsghdr cmhdr;
 | 
			
		||||
	int cmlen;
 | 
			
		||||
| 
						 | 
				
			
			@ -233,12 +233,17 @@ int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *dat
 | 
			
		|||
		data = &ctv;
 | 
			
		||||
		len = sizeof(ctv);
 | 
			
		||||
	}
 | 
			
		||||
	if (level == SOL_SOCKET && type == SCM_TIMESTAMPNS) {
 | 
			
		||||
	if (level == SOL_SOCKET &&
 | 
			
		||||
	    (type == SCM_TIMESTAMPNS || type == SCM_TIMESTAMPING)) {
 | 
			
		||||
		int count = type == SCM_TIMESTAMPNS ? 1 : 3;
 | 
			
		||||
		int i;
 | 
			
		||||
		struct timespec *ts = (struct timespec *)data;
 | 
			
		||||
		cts.tv_sec = ts->tv_sec;
 | 
			
		||||
		cts.tv_nsec = ts->tv_nsec;
 | 
			
		||||
		for (i = 0; i < count; i++) {
 | 
			
		||||
			cts[i].tv_sec = ts[i].tv_sec;
 | 
			
		||||
			cts[i].tv_nsec = ts[i].tv_nsec;
 | 
			
		||||
		}
 | 
			
		||||
		data = &cts;
 | 
			
		||||
		len = sizeof(cts);
 | 
			
		||||
		len = sizeof(cts[0]) * count;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmlen = CMSG_COMPAT_LEN(len);
 | 
			
		||||
| 
						 | 
				
			
			@ -455,7 +460,7 @@ int compat_sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp)
 | 
			
		|||
	struct timeval tv;
 | 
			
		||||
 | 
			
		||||
	if (!sock_flag(sk, SOCK_TIMESTAMP))
 | 
			
		||||
		sock_enable_timestamp(sk);
 | 
			
		||||
		sock_enable_timestamp(sk, SOCK_TIMESTAMP);
 | 
			
		||||
	tv = ktime_to_timeval(sk->sk_stamp);
 | 
			
		||||
	if (tv.tv_sec == -1)
 | 
			
		||||
		return err;
 | 
			
		||||
| 
						 | 
				
			
			@ -479,7 +484,7 @@ int compat_sock_get_timestampns(struct sock *sk, struct timespec __user *usersta
 | 
			
		|||
	struct timespec ts;
 | 
			
		||||
 | 
			
		||||
	if (!sock_flag(sk, SOCK_TIMESTAMP))
 | 
			
		||||
		sock_enable_timestamp(sk);
 | 
			
		||||
		sock_enable_timestamp(sk, SOCK_TIMESTAMP);
 | 
			
		||||
	ts = ktime_to_timespec(sk->sk_stamp);
 | 
			
		||||
	if (ts.tv_sec == -1)
 | 
			
		||||
		return err;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -120,6 +120,7 @@
 | 
			
		|||
#include <net/net_namespace.h>
 | 
			
		||||
#include <net/request_sock.h>
 | 
			
		||||
#include <net/sock.h>
 | 
			
		||||
#include <linux/net_tstamp.h>
 | 
			
		||||
#include <net/xfrm.h>
 | 
			
		||||
#include <linux/ipsec.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -255,13 +256,16 @@ static void sock_warn_obsolete_bsdism(const char *name)
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void sock_disable_timestamp(struct sock *sk)
 | 
			
		||||
static void sock_disable_timestamp(struct sock *sk, int flag)
 | 
			
		||||
{
 | 
			
		||||
	if (sock_flag(sk, SOCK_TIMESTAMP)) {
 | 
			
		||||
		sock_reset_flag(sk, SOCK_TIMESTAMP);
 | 
			
		||||
	if (sock_flag(sk, flag)) {
 | 
			
		||||
		sock_reset_flag(sk, flag);
 | 
			
		||||
		if (!sock_flag(sk, SOCK_TIMESTAMP) &&
 | 
			
		||||
		    !sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE)) {
 | 
			
		||||
			net_disable_timestamp();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 | 
			
		||||
| 
						 | 
				
			
			@ -614,13 +618,38 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
 | 
			
		|||
			else
 | 
			
		||||
				sock_set_flag(sk, SOCK_RCVTSTAMPNS);
 | 
			
		||||
			sock_set_flag(sk, SOCK_RCVTSTAMP);
 | 
			
		||||
			sock_enable_timestamp(sk);
 | 
			
		||||
			sock_enable_timestamp(sk, SOCK_TIMESTAMP);
 | 
			
		||||
		} else {
 | 
			
		||||
			sock_reset_flag(sk, SOCK_RCVTSTAMP);
 | 
			
		||||
			sock_reset_flag(sk, SOCK_RCVTSTAMPNS);
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SO_TIMESTAMPING:
 | 
			
		||||
		if (val & ~SOF_TIMESTAMPING_MASK) {
 | 
			
		||||
			ret = EINVAL;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE,
 | 
			
		||||
				  val & SOF_TIMESTAMPING_TX_HARDWARE);
 | 
			
		||||
		sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE,
 | 
			
		||||
				  val & SOF_TIMESTAMPING_TX_SOFTWARE);
 | 
			
		||||
		sock_valbool_flag(sk, SOCK_TIMESTAMPING_RX_HARDWARE,
 | 
			
		||||
				  val & SOF_TIMESTAMPING_RX_HARDWARE);
 | 
			
		||||
		if (val & SOF_TIMESTAMPING_RX_SOFTWARE)
 | 
			
		||||
			sock_enable_timestamp(sk,
 | 
			
		||||
					      SOCK_TIMESTAMPING_RX_SOFTWARE);
 | 
			
		||||
		else
 | 
			
		||||
			sock_disable_timestamp(sk,
 | 
			
		||||
					       SOCK_TIMESTAMPING_RX_SOFTWARE);
 | 
			
		||||
		sock_valbool_flag(sk, SOCK_TIMESTAMPING_SOFTWARE,
 | 
			
		||||
				  val & SOF_TIMESTAMPING_SOFTWARE);
 | 
			
		||||
		sock_valbool_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE,
 | 
			
		||||
				  val & SOF_TIMESTAMPING_SYS_HARDWARE);
 | 
			
		||||
		sock_valbool_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE,
 | 
			
		||||
				  val & SOF_TIMESTAMPING_RAW_HARDWARE);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SO_RCVLOWAT:
 | 
			
		||||
		if (val < 0)
 | 
			
		||||
			val = INT_MAX;
 | 
			
		||||
| 
						 | 
				
			
			@ -768,6 +797,24 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
 | 
			
		|||
		v.val = sock_flag(sk, SOCK_RCVTSTAMPNS);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SO_TIMESTAMPING:
 | 
			
		||||
		v.val = 0;
 | 
			
		||||
		if (sock_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE))
 | 
			
		||||
			v.val |= SOF_TIMESTAMPING_TX_HARDWARE;
 | 
			
		||||
		if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE))
 | 
			
		||||
			v.val |= SOF_TIMESTAMPING_TX_SOFTWARE;
 | 
			
		||||
		if (sock_flag(sk, SOCK_TIMESTAMPING_RX_HARDWARE))
 | 
			
		||||
			v.val |= SOF_TIMESTAMPING_RX_HARDWARE;
 | 
			
		||||
		if (sock_flag(sk, SOCK_TIMESTAMPING_RX_SOFTWARE))
 | 
			
		||||
			v.val |= SOF_TIMESTAMPING_RX_SOFTWARE;
 | 
			
		||||
		if (sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE))
 | 
			
		||||
			v.val |= SOF_TIMESTAMPING_SOFTWARE;
 | 
			
		||||
		if (sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE))
 | 
			
		||||
			v.val |= SOF_TIMESTAMPING_SYS_HARDWARE;
 | 
			
		||||
		if (sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE))
 | 
			
		||||
			v.val |= SOF_TIMESTAMPING_RAW_HARDWARE;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case SO_RCVTIMEO:
 | 
			
		||||
		lv=sizeof(struct timeval);
 | 
			
		||||
		if (sk->sk_rcvtimeo == MAX_SCHEDULE_TIMEOUT) {
 | 
			
		||||
| 
						 | 
				
			
			@ -969,7 +1016,8 @@ void sk_free(struct sock *sk)
 | 
			
		|||
		rcu_assign_pointer(sk->sk_filter, NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sock_disable_timestamp(sk);
 | 
			
		||||
	sock_disable_timestamp(sk, SOCK_TIMESTAMP);
 | 
			
		||||
	sock_disable_timestamp(sk, SOCK_TIMESTAMPING_RX_SOFTWARE);
 | 
			
		||||
 | 
			
		||||
	if (atomic_read(&sk->sk_omem_alloc))
 | 
			
		||||
		printk(KERN_DEBUG "%s: optmem leakage (%d bytes) detected.\n",
 | 
			
		||||
| 
						 | 
				
			
			@ -1787,7 +1835,7 @@ int sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp)
 | 
			
		|||
{
 | 
			
		||||
	struct timeval tv;
 | 
			
		||||
	if (!sock_flag(sk, SOCK_TIMESTAMP))
 | 
			
		||||
		sock_enable_timestamp(sk);
 | 
			
		||||
		sock_enable_timestamp(sk, SOCK_TIMESTAMP);
 | 
			
		||||
	tv = ktime_to_timeval(sk->sk_stamp);
 | 
			
		||||
	if (tv.tv_sec == -1)
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
| 
						 | 
				
			
			@ -1803,7 +1851,7 @@ int sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp)
 | 
			
		|||
{
 | 
			
		||||
	struct timespec ts;
 | 
			
		||||
	if (!sock_flag(sk, SOCK_TIMESTAMP))
 | 
			
		||||
		sock_enable_timestamp(sk);
 | 
			
		||||
		sock_enable_timestamp(sk, SOCK_TIMESTAMP);
 | 
			
		||||
	ts = ktime_to_timespec(sk->sk_stamp);
 | 
			
		||||
	if (ts.tv_sec == -1)
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
| 
						 | 
				
			
			@ -1815,10 +1863,19 @@ int sock_get_timestampns(struct sock *sk, struct timespec __user *userstamp)
 | 
			
		|||
}
 | 
			
		||||
EXPORT_SYMBOL(sock_get_timestampns);
 | 
			
		||||
 | 
			
		||||
void sock_enable_timestamp(struct sock *sk)
 | 
			
		||||
void sock_enable_timestamp(struct sock *sk, int flag)
 | 
			
		||||
{
 | 
			
		||||
	if (!sock_flag(sk, SOCK_TIMESTAMP)) {
 | 
			
		||||
		sock_set_flag(sk, SOCK_TIMESTAMP);
 | 
			
		||||
	if (!sock_flag(sk, flag)) {
 | 
			
		||||
		sock_set_flag(sk, flag);
 | 
			
		||||
		/*
 | 
			
		||||
		 * we just set one of the two flags which require net
 | 
			
		||||
		 * time stamping, but time stamping might have been on
 | 
			
		||||
		 * already because of the other one
 | 
			
		||||
		 */
 | 
			
		||||
		if (!sock_flag(sk,
 | 
			
		||||
				flag == SOCK_TIMESTAMP ?
 | 
			
		||||
				SOCK_TIMESTAMPING_RX_SOFTWARE :
 | 
			
		||||
				SOCK_TIMESTAMP))
 | 
			
		||||
			net_enable_timestamp();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										74
									
								
								net/socket.c
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								net/socket.c
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -545,6 +545,18 @@ void sock_release(struct socket *sock)
 | 
			
		|||
	sock->file = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int sock_tx_timestamp(struct msghdr *msg, struct sock *sk,
 | 
			
		||||
		      union skb_shared_tx *shtx)
 | 
			
		||||
{
 | 
			
		||||
	shtx->flags = 0;
 | 
			
		||||
	if (sock_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE))
 | 
			
		||||
		shtx->hardware = 1;
 | 
			
		||||
	if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE))
 | 
			
		||||
		shtx->software = 1;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(sock_tx_timestamp);
 | 
			
		||||
 | 
			
		||||
static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock,
 | 
			
		||||
				 struct msghdr *msg, size_t size)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -595,35 +607,67 @@ int kernel_sendmsg(struct socket *sock, struct msghdr *msg,
 | 
			
		|||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ktime2ts(ktime_t kt, struct timespec *ts)
 | 
			
		||||
{
 | 
			
		||||
	if (kt.tv64) {
 | 
			
		||||
		*ts = ktime_to_timespec(kt);
 | 
			
		||||
		return 1;
 | 
			
		||||
	} else {
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP)
 | 
			
		||||
 */
 | 
			
		||||
void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
 | 
			
		||||
	struct sk_buff *skb)
 | 
			
		||||
{
 | 
			
		||||
	ktime_t kt = skb->tstamp;
 | 
			
		||||
	int need_software_tstamp = sock_flag(sk, SOCK_RCVTSTAMP);
 | 
			
		||||
	struct timespec ts[3];
 | 
			
		||||
	int empty = 1;
 | 
			
		||||
	struct skb_shared_hwtstamps *shhwtstamps =
 | 
			
		||||
		skb_hwtstamps(skb);
 | 
			
		||||
 | 
			
		||||
	/* Race occurred between timestamp enabling and packet
 | 
			
		||||
	   receiving.  Fill in the current time for now. */
 | 
			
		||||
	if (need_software_tstamp && skb->tstamp.tv64 == 0)
 | 
			
		||||
		__net_timestamp(skb);
 | 
			
		||||
 | 
			
		||||
	if (need_software_tstamp) {
 | 
			
		||||
		if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
 | 
			
		||||
			struct timeval tv;
 | 
			
		||||
		/* Race occurred between timestamp enabling and packet
 | 
			
		||||
		   receiving.  Fill in the current time for now. */
 | 
			
		||||
		if (kt.tv64 == 0)
 | 
			
		||||
			kt = ktime_get_real();
 | 
			
		||||
		skb->tstamp = kt;
 | 
			
		||||
		tv = ktime_to_timeval(kt);
 | 
			
		||||
		put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP, sizeof(tv), &tv);
 | 
			
		||||
			skb_get_timestamp(skb, &tv);
 | 
			
		||||
			put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP,
 | 
			
		||||
				 sizeof(tv), &tv);
 | 
			
		||||
		} else {
 | 
			
		||||
			struct timespec ts;
 | 
			
		||||
		/* Race occurred between timestamp enabling and packet
 | 
			
		||||
		   receiving.  Fill in the current time for now. */
 | 
			
		||||
		if (kt.tv64 == 0)
 | 
			
		||||
			kt = ktime_get_real();
 | 
			
		||||
		skb->tstamp = kt;
 | 
			
		||||
		ts = ktime_to_timespec(kt);
 | 
			
		||||
		put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS, sizeof(ts), &ts);
 | 
			
		||||
			skb_get_timestampns(skb, &ts);
 | 
			
		||||
			put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS,
 | 
			
		||||
				 sizeof(ts), &ts);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	memset(ts, 0, sizeof(ts));
 | 
			
		||||
	if (skb->tstamp.tv64 &&
 | 
			
		||||
	    sock_flag(sk, SOCK_TIMESTAMPING_SOFTWARE)) {
 | 
			
		||||
		skb_get_timestampns(skb, ts + 0);
 | 
			
		||||
		empty = 0;
 | 
			
		||||
	}
 | 
			
		||||
	if (shhwtstamps) {
 | 
			
		||||
		if (sock_flag(sk, SOCK_TIMESTAMPING_SYS_HARDWARE) &&
 | 
			
		||||
		    ktime2ts(shhwtstamps->syststamp, ts + 1))
 | 
			
		||||
			empty = 0;
 | 
			
		||||
		if (sock_flag(sk, SOCK_TIMESTAMPING_RAW_HARDWARE) &&
 | 
			
		||||
		    ktime2ts(shhwtstamps->hwtstamp, ts + 2))
 | 
			
		||||
			empty = 0;
 | 
			
		||||
	}
 | 
			
		||||
	if (!empty)
 | 
			
		||||
		put_cmsg(msg, SOL_SOCKET,
 | 
			
		||||
			 SCM_TIMESTAMPING, sizeof(ts), &ts);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORT_SYMBOL_GPL(__sock_recv_timestamp);
 | 
			
		||||
 | 
			
		||||
static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue