diff options
author | Eric Dumazet <dada1@cosmosbay.com> | 2007-03-26 01:14:49 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-04-26 01:24:21 -0400 |
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 0e407563ae85..9a0f5f2b90c8 100644 --- a/net/compat.c +++ b/net/compat.c | |||
@@ -215,6 +215,7 @@ Efault: | |||
215 | int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) | 215 | int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) |
216 | { | 216 | { |
217 | struct compat_timeval ctv; | 217 | struct compat_timeval ctv; |
218 | struct compat_timespec cts; | ||
218 | struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; | 219 | struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; |
219 | struct compat_cmsghdr cmhdr; | 220 | struct compat_cmsghdr cmhdr; |
220 | int cmlen; | 221 | int cmlen; |
@@ -229,7 +230,14 @@ int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *dat | |||
229 | ctv.tv_sec = tv->tv_sec; | 230 | ctv.tv_sec = tv->tv_sec; |
230 | ctv.tv_usec = tv->tv_usec; | 231 | ctv.tv_usec = tv->tv_usec; |
231 | data = &ctv; | 232 | data = &ctv; |
232 | len = sizeof(struct compat_timeval); | 233 | len = sizeof(ctv); |
234 | } | ||
235 | if (level == SOL_SOCKET && type == SO_TIMESTAMPNS) { | ||
236 | struct timespec *ts = (struct timespec *)data; | ||
237 | cts.tv_sec = ts->tv_sec; | ||
238 | cts.tv_nsec = ts->tv_nsec; | ||
239 | data = &cts; | ||
240 | len = sizeof(cts); | ||
233 | } | 241 | } |
234 | 242 | ||
235 | cmlen = CMSG_COMPAT_LEN(len); | 243 | cmlen = CMSG_COMPAT_LEN(len); |
diff --git a/net/core/sock.c b/net/core/sock.c index 792ae39804a2..f9e6991d3729 100644 --- a/net/core/sock.c +++ b/net/core/sock.c | |||
@@ -521,11 +521,18 @@ set_rcvbuf: | |||
521 | break; | 521 | break; |
522 | 522 | ||
523 | case SO_TIMESTAMP: | 523 | case SO_TIMESTAMP: |
524 | case SO_TIMESTAMPNS: | ||
524 | if (valbool) { | 525 | if (valbool) { |
526 | if (optname == SO_TIMESTAMP) | ||
527 | sock_reset_flag(sk, SOCK_RCVTSTAMPNS); | ||
528 | else | ||
529 | sock_set_flag(sk, SOCK_RCVTSTAMPNS); | ||
525 | sock_set_flag(sk, SOCK_RCVTSTAMP); | 530 | sock_set_flag(sk, SOCK_RCVTSTAMP); |
526 | sock_enable_timestamp(sk); | 531 | sock_enable_timestamp(sk); |
527 | } else | 532 | } else { |
528 | sock_reset_flag(sk, SOCK_RCVTSTAMP); | 533 | sock_reset_flag(sk, SOCK_RCVTSTAMP); |
534 | sock_reset_flag(sk, SOCK_RCVTSTAMPNS); | ||
535 | } | ||
529 | break; | 536 | break; |
530 | 537 | ||
531 | case SO_RCVLOWAT: | 538 | case SO_RCVLOWAT: |
@@ -715,7 +722,12 @@ int sock_getsockopt(struct socket *sock, int level, int optname, | |||
715 | break; | 722 | break; |
716 | 723 | ||
717 | case SO_TIMESTAMP: | 724 | case SO_TIMESTAMP: |
718 | v.val = sock_flag(sk, SOCK_RCVTSTAMP); | 725 | v.val = sock_flag(sk, SOCK_RCVTSTAMP) && |
726 | !sock_flag(sk, SOCK_RCVTSTAMPNS); | ||
727 | break; | ||
728 | |||
729 | case SO_TIMESTAMPNS: | ||
730 | v.val = sock_flag(sk, SOCK_RCVTSTAMPNS); | ||
719 | break; | 731 | break; |
720 | 732 | ||
721 | case SO_RCVTIMEO: | 733 | case SO_RCVTIMEO: |
diff --git a/net/socket.c b/net/socket.c index cf18c5eb592e..a7bd0df115b2 100644 --- a/net/socket.c +++ b/net/socket.c | |||
@@ -585,6 +585,35 @@ int kernel_sendmsg(struct socket *sock, struct msghdr *msg, | |||
585 | return result; | 585 | return result; |
586 | } | 586 | } |
587 | 587 | ||
588 | /* | ||
589 | * called from sock_recv_timestamp() if sock_flag(sk, SOCK_RCVTSTAMP) | ||
590 | */ | ||
591 | void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, | ||
592 | struct sk_buff *skb) | ||
593 | { | ||
594 | ktime_t kt = skb->tstamp; | ||
595 | |||
596 | if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) { | ||
597 | struct timeval tv; | ||
598 | /* Race occurred between timestamp enabling and packet | ||
599 | receiving. Fill in the current time for now. */ | ||
600 | if (kt.tv64 == 0) | ||
601 | kt = ktime_get_real(); | ||
602 | skb->tstamp = kt; | ||
603 | tv = ktime_to_timeval(kt); | ||
604 | put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP, sizeof(tv), &tv); | ||
605 | } else { | ||
606 | struct timespec ts; | ||
607 | /* Race occurred between timestamp enabling and packet | ||
608 | receiving. Fill in the current time for now. */ | ||
609 | if (kt.tv64 == 0) | ||
610 | kt = ktime_get_real(); | ||
611 | skb->tstamp = kt; | ||
612 | ts = ktime_to_timespec(kt); | ||
613 | put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS, sizeof(ts), &ts); | ||
614 | } | ||
615 | } | ||
616 | |||
588 | static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock, | 617 | static inline int __sock_recvmsg(struct kiocb *iocb, struct socket *sock, |
589 | struct msghdr *msg, size_t size, int flags) | 618 | struct msghdr *msg, size_t size, int flags) |
590 | { | 619 | { |