diff options
author | Eric Dumazet <dada1@cosmosbay.com> | 2007-03-25 22:14:49 -0700 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-04-25 22:24:21 -0700 |
commit | 92f37fd2ee805aa77925c1e64fd56088b46094fc (patch) | |
tree | 8251c38b83ab362116dac89d94412ce229b42831 /net | |
parent | c7a3c5da35055e2fa97ed4f0da3eec4bd0ef4c38 (diff) |
[NET]: Adding SO_TIMESTAMPNS / SCM_TIMESTAMPNS support
Now that network timestamps use ktime_t infrastructure, we can add a new
SOL_SOCKET sockopt SO_TIMESTAMPNS.
This command is similar to SO_TIMESTAMP, but permits transmission of
a 'timespec struct' instead of a 'timeval struct' control message.
(nanosecond resolution instead of microsecond)
Control message is labelled SCM_TIMESTAMPNS instead of SCM_TIMESTAMP
A socket cannot mix SO_TIMESTAMP and SO_TIMESTAMPNS : the two modes are
mutually exclusive.
sock_recv_timestamp() became too big to be fully inlined so I added a
__sock_recv_timestamp() helper function.
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
CC: linux-arch@vger.kernel.org
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/compat.c | 10 | ||||
-rw-r--r-- | net/core/sock.c | 16 | ||||
-rw-r--r-- | net/socket.c | 29 |
3 files changed, 52 insertions, 3 deletions
diff --git a/net/compat.c b/net/compat.c index 0e407563ae8..9a0f5f2b90c 100644 --- a/net/compat.c +++ b/net/compat.c @@ -215,6 +215,7 @@ Efault: 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_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; struct compat_cmsghdr cmhdr; int cmlen; @@ -229,7 +230,14 @@ int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *dat ctv.tv_sec = tv->tv_sec; ctv.tv_usec = tv->tv_usec; data = &ctv; - len = sizeof(struct compat_timeval); + len = sizeof(ctv); + } + if (level == SOL_SOCKET && type == SO_TIMESTAMPNS) { + struct timespec *ts = (struct timespec *)data; + cts.tv_sec = ts->tv_sec; + cts.tv_nsec = ts->tv_nsec; + data = &cts; + len = sizeof(cts); } cmlen = CMSG_COMPAT_LEN(len); diff --git a/net/core/sock.c b/net/core/sock.c index 792ae39804a..f9e6991d372 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -521,11 +521,18 @@ set_rcvbuf: break; case SO_TIMESTAMP: + case SO_TIMESTAMPNS: if (valbool) { + if (optname == SO_TIMESTAMP) + sock_reset_flag(sk, SOCK_RCVTSTAMPNS); + else + sock_set_flag(sk, SOCK_RCVTSTAMPNS); sock_set_flag(sk, SOCK_RCVTSTAMP); sock_enable_timestamp(sk); - } else + } else { sock_reset_flag(sk, SOCK_RCVTSTAMP); + sock_reset_flag(sk, SOCK_RCVTSTAMPNS); + } break; case SO_RCVLOWAT: @@ -715,7 +722,12 @@ int sock_getsockopt(struct socket *sock, int level, int optname, break; case SO_TIMESTAMP: - v.val = sock_flag(sk, SOCK_RCVTSTAMP); + v.val = sock_flag(sk, SOCK_RCVTSTAMP) && + !sock_flag(sk, SOCK_RCVTSTAMPNS); + break; + + case SO_TIMESTAMPNS: + v.val = sock_flag(sk, SOCK_RCVTSTAMPNS); break; case SO_RCVTIMEO: diff --git a/net/socket.c b/net/socket.c index cf18c5eb592..a7bd0df115b 100644 --- a/net/socket.c +++ b/net/socket.c @@ -585,6 +585,35 @@ int kernel_sendmsg(struct socket *sock, struct msghdr *msg, return result; } +/* + * 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; + + 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); + } 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); + } +} + static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags) { |