diff options
| author | Richard Cochran <richardcochran@gmail.com> | 2010-04-07 22:41:28 +0000 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2010-04-13 01:30:48 -0700 | 
| commit | ed85b565b825566da34e55eee9ad150ed93fdda0 (patch) | |
| tree | 77b9bad66d0cd6a1b7c657995100d555b8ecb9eb /net/packet | |
| parent | 7d53b80980a598836c56517290b29aa7b0766ef8 (diff) | |
packet: support for TX time stamps on RAW sockets
Enable the SO_TIMESTAMPING socket infrastructure for raw packet sockets.
We introduce PACKET_TX_TIMESTAMP for the control message cmsg_type.
Similar support for UDP and CAN sockets was added in commit
51f31cabe3ce5345b51e4a4f82138b38c4d5dc91
Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/packet')
| -rw-r--r-- | net/packet/af_packet.c | 61 | 
1 files changed, 60 insertions, 1 deletions
| diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index d7d0310dca9d..f162d59d8161 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -82,6 +82,7 @@  #include <linux/mutex.h>  #include <linux/if_vlan.h>  #include <linux/virtio_net.h> +#include <linux/errqueue.h>  #ifdef CONFIG_INET  #include <net/inet_common.h> @@ -315,6 +316,8 @@ static inline struct packet_sock *pkt_sk(struct sock *sk)  static void packet_sock_destruct(struct sock *sk)  { +	skb_queue_purge(&sk->sk_error_queue); +  	WARN_ON(atomic_read(&sk->sk_rmem_alloc));  	WARN_ON(atomic_read(&sk->sk_wmem_alloc)); @@ -483,6 +486,9 @@ retry:  	skb->dev = dev;  	skb->priority = sk->sk_priority;  	skb->mark = sk->sk_mark; +	err = sock_tx_timestamp(msg, sk, skb_tx(skb)); +	if (err < 0) +		goto out_unlock;  	dev_queue_xmit(skb);  	rcu_read_unlock(); @@ -1188,6 +1194,9 @@ static int packet_snd(struct socket *sock,  	err = skb_copy_datagram_from_iovec(skb, offset, msg->msg_iov, 0, len);  	if (err)  		goto out_free; +	err = sock_tx_timestamp(msg, sk, skb_tx(skb)); +	if (err < 0) +		goto out_free;  	skb->protocol = proto;  	skb->dev = dev; @@ -1487,6 +1496,51 @@ out:  	return err;  } +static int packet_recv_error(struct sock *sk, struct msghdr *msg, int len) +{ +	struct sock_exterr_skb *serr; +	struct sk_buff *skb, *skb2; +	int copied, err; + +	err = -EAGAIN; +	skb = skb_dequeue(&sk->sk_error_queue); +	if (skb == NULL) +		goto out; + +	copied = skb->len; +	if (copied > len) { +		msg->msg_flags |= MSG_TRUNC; +		copied = len; +	} +	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); +	if (err) +		goto out_free_skb; + +	sock_recv_timestamp(msg, sk, skb); + +	serr = SKB_EXT_ERR(skb); +	put_cmsg(msg, SOL_PACKET, PACKET_TX_TIMESTAMP, +		 sizeof(serr->ee), &serr->ee); + +	msg->msg_flags |= MSG_ERRQUEUE; +	err = copied; + +	/* Reset and regenerate socket error */ +	spin_lock_bh(&sk->sk_error_queue.lock); +	sk->sk_err = 0; +	if ((skb2 = skb_peek(&sk->sk_error_queue)) != NULL) { +		sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno; +		spin_unlock_bh(&sk->sk_error_queue.lock); +		sk->sk_error_report(sk); +	} else +		spin_unlock_bh(&sk->sk_error_queue.lock); + +out_free_skb: +	kfree_skb(skb); +out: +	return err; +} +  /*   *	Pull a packet from our receive queue and hand it to the user.   *	If necessary we block. @@ -1502,7 +1556,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,  	int vnet_hdr_len = 0;  	err = -EINVAL; -	if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT)) +	if (flags & ~(MSG_PEEK|MSG_DONTWAIT|MSG_TRUNC|MSG_CMSG_COMPAT|MSG_ERRQUEUE))  		goto out;  #if 0 @@ -1511,6 +1565,11 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,  		return -ENODEV;  #endif +	if (flags & MSG_ERRQUEUE) { +		err = packet_recv_error(sk, msg, len); +		goto out; +	} +  	/*  	 *	Call the generic datagram receiver. This handles all sorts  	 *	of horrible races and re-entrancy so we can forget about it | 
